chat: route AI customization list through item sources#309666
chat: route AI customization list through item sources#309666joshspicer wants to merge 2 commits intomainfrom
Conversation
Addresses code quality issues from council review: - Extract shared isChatExtensionItem() to aiCustomizationItemSourceUtils - Move storageToIcon() to aiCustomizationIcons (pure function, no class dep) - Extract shared expandHookFileItems() utility, deduplicating hook file parsing from PromptsServiceCustomizationItemProvider and ProviderCustomizationItemSource - Replace fragile backward-index splice loops in applyLocalFilters with idiomatic .filter() chains - Cache ProviderCustomizationItemSource per active harness descriptor to avoid redundant event composition on every call Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Screenshot ChangesBase: Changed (5) |
There was a problem hiding this comment.
Pull request overview
Refactors the AI Customization management list to fetch items through a unified, browser-internal “item source” abstraction, converging both promptsService-backed discovery and extension-contributed providers onto the same normalized list-item model.
Changes:
- Introduces
IAICustomizationItemSource+ supporting utilities/normalizer to unify discovery and normalization intoIAICustomizationListItem. - Adds a promptsService adapter (
PromptsServiceCustomizationItemProvider) so local/static harnesses keep the rich promptsService pipeline while still producing provider-shaped items. - Updates the list widget to select/cache an item source per active harness and remove direct branching between “core” vs “provider” paths.
Show a summary per file
| File | Description |
|---|---|
| src/vs/workbench/contrib/chat/browser/aiCustomization/providerCustomizationItemSource.ts | New unified item source that merges provider items + optional local sync overlays and emits a composed onDidChange. |
| src/vs/workbench/contrib/chat/browser/aiCustomization/promptsServiceCustomizationItemProvider.ts | Adapts IPromptsService to IExternalCustomizationItemProvider, including hook expansion and harness filtering. |
| src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationListWidget.ts | Switches the widget to consume an IAICustomizationItemSource and cache it per harness descriptor. |
| src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationListItem.ts | Extracts the widget list item model + item source contract into a shared browser file. |
| src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationItemSourceUtils.ts | Shared utilities for friendly naming, hook expansion, and built-in chat extension detection. |
| src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationItemNormalizer.ts | Normalizes provider-shaped items into rich UI list items, inferring source/grouping details. |
| src/vs/workbench/contrib/chat/browser/aiCustomization/aiCustomizationIcons.ts | Moves storageToIcon into the icons module. |
| src/vs/sessions/AI_CUSTOMIZATIONS.md | Updates sessions documentation to describe the new management editor item pipeline. |
Copilot's findings
- Files reviewed: 8/8 changed files
- Comments generated: 2
| switch (item.groupKey) { | ||
| case PromptsStorage.local: | ||
| case PromptsStorage.user: | ||
| case PromptsStorage.extension: | ||
| case PromptsStorage.plugin: | ||
| return { ...inferred, storage: item.groupKey }; | ||
| case BUILTIN_STORAGE: | ||
| return { ...inferred, storage: PromptsStorage.extension, groupKey: BUILTIN_STORAGE, isBuiltin: true }; | ||
| default: | ||
| return { ...inferred, groupKey: item.groupKey }; | ||
| } |
There was a problem hiding this comment.
In resolveSource, when a provider supplies groupKey equal to a PromptsStorage value (e.g. 'user'), the code returns { ...inferred, storage: item.groupKey }. If the URI is non-file: (so inferred.groupKey is BUILTIN_STORAGE and isBuiltin is true), the inferred built-in grouping leaks through and the item will still be grouped/shown as Built-in despite the provider explicitly setting the group. Consider treating PromptsStorage.* groupKeys as an explicit group override: clear/override groupKey and isBuiltin from the inferred value (and/or set groupKey to the provided storage) so the provider’s grouping is respected for non-file URIs too.
| case 'local': return workspaceIcon; | ||
| case 'user': return userIcon; | ||
| case 'extension': return extensionIcon; | ||
| case 'plugin': return pluginIcon; |
There was a problem hiding this comment.
storageToIcon switches on PromptsStorage but uses raw string literals ('local', 'user', …). Since PromptsStorage is an enum, using PromptsStorage.local/user/... here avoids accidental typos and stays resilient if the enum members ever change.
| case 'local': return workspaceIcon; | |
| case 'user': return userIcon; | |
| case 'extension': return extensionIcon; | |
| case 'plugin': return pluginIcon; | |
| case PromptsStorage.local: return workspaceIcon; | |
| case PromptsStorage.user: return userIcon; | |
| case PromptsStorage.extension: return extensionIcon; | |
| case PromptsStorage.plugin: return pluginIcon; |
Summary
Route the AI Customizations list widget through a small browser-internal item-source abstraction instead of branching directly between the promptsService path and extension-provider path.
This keeps Local/static harnesses on the existing rich promptsService loading pipeline (storage filtering, plugin metadata, hook parsing, built-in grouping, etc.) while adapting extension-contributed providers into the same list-item source shape.
Resolves #309627
Notes
Changes
Commit 1: Extract AI customization item sources
Introduces a pipeline architecture where both data sources converge on
IExternalCustomizationItemearly:New files:
aiCustomizationListItem.ts,aiCustomizationItemSourceUtils.ts,aiCustomizationItemNormalizer.ts,promptsServiceCustomizationItemProvider.ts,providerCustomizationItemSource.tsCommit 2: Clean up extraction
Council-review-driven cleanup:
isChatExtensionItem()and hook expansion logic into shared utilsstorageToIcon()to icons file (pure function, retainsPromptsStoragetype safety).filter()chainsProviderCustomizationItemSourceper active harness descriptor.find()Validation