mit einem Klick
chat-customizations-editor
// Use when working on the Chat Customizations editor — the management UI for agents, skills, instructions, hooks, prompts, MCP servers, and plugins.
// Use when working on the Chat Customizations editor — the management UI for agents, skills, instructions, hooks, prompts, MCP servers, and plugins.
[HINT] Laden Sie das komplette Skill-Verzeichnis einschließlich SKILL.md und aller zugehörigen Dateien herunter
| name | chat-customizations-editor |
| description | Use when working on the Chat Customizations editor — the management UI for agents, skills, instructions, hooks, prompts, MCP servers, and plugins. |
Split-view management pane for AI customization items across workspace, user, extension, and plugin storage. Supports harness-based filtering (Local, Copilot CLI, Claude).
src/vs/sessions/AI_CUSTOMIZATIONS.md — always read before making changes, always update after.
| Folder | What |
|---|---|
src/vs/workbench/contrib/chat/common/ | ICustomizationHarnessService, ISectionOverride, IStorageSourceFilter — shared interfaces and filter helpers |
src/vs/workbench/contrib/chat/browser/aiCustomization/ | Management editor, list widgets (prompts, MCP, plugins), harness service registration |
src/vs/sessions/contrib/chat/browser/ | Sessions-window overrides (harness service, workspace service) |
src/vs/sessions/contrib/sessions/browser/ | Sessions tree view counts and toolbar |
When changing harness descriptor interfaces or factory functions, verify both core and sessions registrations compile.
IHarnessDescriptor — drives all UI behavior declaratively (hidden sections, button overrides, file filters, agent gating). See spec for full field reference.ISectionOverride — per-section button customization (command invocation, root file creation, type labels, file extensions).IStorageSourceFilter — controls which storage sources and user roots are visible per harness/type.IExternalCustomizationItemProvider / IExternalCustomizationItem — internal interfaces (in customizationHarnessService.ts) for extension-contributed providers that supply items directly. These mirror the proposed extension API types.Principle: the UI widgets read everything from the descriptor — no harness-specific conditionals in widget code.
chatSessionCustomizationProvider)The proposed API in src/vscode-dts/vscode.proposed.chatSessionCustomizationProvider.d.ts lets extensions register customization providers. Changes to IExternalCustomizationItem or IExternalCustomizationItemProvider must be kept in sync across the full chain:
| Layer | File | Type |
|---|---|---|
| Extension API | vscode.proposed.chatSessionCustomizationProvider.d.ts | ChatSessionCustomizationItem |
| IPC DTO | extHost.protocol.ts | IChatSessionCustomizationItemDto |
| ExtHost mapping | extHostChatAgents2.ts | $provideChatSessionCustomizations() |
| MainThread mapping | mainThreadChatAgents2.ts | provideChatSessionCustomizations callback |
| Internal interface | customizationHarnessService.ts | IExternalCustomizationItem |
When adding fields to IExternalCustomizationItem, update all five layers. The proposed API .d.ts is additive-only (new optional fields are backward-compatible and do not require a version bump).
Component explorer fixtures (see component-fixtures skill): aiCustomizationListWidget.fixture.ts, aiCustomizationManagementEditor.fixture.ts under src/vs/workbench/test/browser/componentFixtures/.
The management editor fixture supports a selectedSection option to render any tab. Each tab has Dark/Light variants auto-generated by defineThemedFixtureGroup.
Available fixture IDs (use with mcp_component-exp_screenshot):
| Fixture ID pattern | Tab shown |
|---|---|
chat/aiCustomizations/aiCustomizationManagementEditor/AgentsTab/{Dark,Light} | Agents |
chat/aiCustomizations/aiCustomizationManagementEditor/SkillsTab/{Dark,Light} | Skills |
chat/aiCustomizations/aiCustomizationManagementEditor/InstructionsTab/{Dark,Light} | Instructions |
chat/aiCustomizations/aiCustomizationManagementEditor/HooksTab/{Dark,Light} | Hooks |
chat/aiCustomizations/aiCustomizationManagementEditor/PromptsTab/{Dark,Light} | Prompts |
chat/aiCustomizations/aiCustomizationManagementEditor/McpServersTab/{Dark,Light} | MCP Servers |
chat/aiCustomizations/aiCustomizationManagementEditor/PluginsTab/{Dark,Light} | Plugins |
chat/aiCustomizations/aiCustomizationManagementEditor/LocalHarness/{Dark,Light} | Default (Agents, Local harness) |
chat/aiCustomizations/aiCustomizationManagementEditor/CliHarness/{Dark,Light} | Default (Agents, CLI harness) |
chat/aiCustomizations/aiCustomizationManagementEditor/ClaudeHarness/{Dark,Light} | Default (Agents, Claude harness) |
chat/aiCustomizations/aiCustomizationManagementEditor/Sessions/{Dark,Light} | Sessions window variant |
Adding a new tab fixture: Add a variant to the defineThemedFixtureGroup in aiCustomizationManagementEditor.fixture.ts:
MyNewTab: defineComponentFixture({
labels: { kind: 'screenshot' },
render: ctx => renderEditor(ctx, {
harness: CustomizationHarness.VSCode,
selectedSection: AICustomizationManagementSection.MySection,
}),
}),
The selectedSection calls editor.selectSectionById() after setInput, which navigates to the specified tab and re-layouts.
Each customization type requires its own mock path in createMockPromptsService:
getCustomAgents() returns agent objectsfindAgentSkills() returns IAgentSkill[]getPromptSlashCommands() returns IChatPromptSlashCommand[]listPromptFiles() filtered by PromptsTypemcpWorkspaceServers/mcpUserServers arrays passed to IMcpWorkbenchService mockIPluginMarketplaceService.installedPlugins and IAgentPluginService.plugins observablesAll test data lives in allFiles (prompt-based items) and the mcpWorkspace/UserServers arrays. Add enough items per category (8+) to invoke scrolling.
The list widget regroups items from the default chat extension under a "Built-in" header. Three things must be in place for fixtures to exercise this:
BUILTIN_STORAGE in the harness descriptor's visible sourcesIProductService.defaultChatAgent.chatExtensionId (e.g., 'GitHub.copilot-chat')extensionId / extensionDisplayName matching that IDWithout all three, built-in regrouping silently doesn't run and the fixture only shows flat lists.
The management editor embeds a CodeEditorWidget. Electron-side editor contributions (e.g., AgentFeedbackEditorWidgetContribution) are instantiated automatically and crash if their injected services aren't registered. The fixture must mock at minimum:
IAgentFeedbackService — needs onDidChangeFeedback, onDidChangeNavigation as Event.NoneICodeReviewService — needs getReviewState() / getPRReviewState() returning idle observablesIChatEditingService — needs editingSessionsObs as empty observableIAgentSessionsService — needs model.sessions as empty arrayThese are cross-layer imports from vs/sessions/ — use // eslint-disable-next-line local/code-import-patterns on the import lines.
Key fixtures have blocksCi: true in their labels. The screenshot-test.yml GitHub Action captures screenshots on every PR to main and fails the CI status check if any blocks-ci-labeled fixture's screenshot changes. This catches layout regressions automatically.
Currently gated fixtures: LocalHarness, McpServersTab, McpServersTabNarrow, AgentsTabNarrow. When adding a new section or layout-critical fixture, add blocksCi: true:
MyFixture: defineComponentFixture({
labels: { kind: 'screenshot', blocksCi: true },
render: ctx => renderEditor(ctx, { ... }),
}),
Don't add blocksCi to every fixture — only ones that cover critical layout paths (default view, section with list + footer, narrow viewport). Too many gated fixtures creates noisy CI.
Scrollbar fade transitions cause screenshot instability — the scrollbar shifts from visible to invisible fade class ~2 seconds after a programmatic scroll. After calling revealLastItem() or any scroll action, wait for the transition to complete before the fixture's render promise resolves:
await new Promise(resolve => setTimeout(resolve, 2400));
// Then optionally poll until .scrollbar.vertical loses the 'visible' class
./scripts/test.sh --grep "applyStorageSourceFilter|customizationCounts"
npm run compile-check-ts-native && npm run valid-layers-check
See the sessions skill for sessions-window specific guidance.
Component fixtures use mock data and a fixed container size. Layout bugs caused by reflow timing, real data shapes, or narrow window sizes often don't reproduce in fixtures. When a user reports a broken layout, debug in the live Code OSS product.
For launching Code OSS with CDP and connecting agent-browser, see the launch skill. Use --user-data-dir /tmp/code-oss-debug to avoid colliding with an already-running instance from another worktree.
After connecting, use snapshot -i to find the "Open Customizations" button (in the Chat panel header), then click it. To switch sections, use eval with a DOM click since sidebar items aren't interactive refs:
npx agent-browser eval "const items = [...document.querySelectorAll('.section-list-item')]; \
items.find(el => el.textContent?.includes('MCP'))?.click();"
agent-browser eval doesn't always print return values. Use document.title as a return channel:
npx agent-browser eval "const w = document.querySelector('.mcp-list-widget'); \
const lc = w?.querySelector('.mcp-list-container'); \
const rows = lc?.querySelectorAll('.monaco-list-row'); \
document.title = 'DBG:rows=' + (rows?.length ?? -1) \
+ ',listH=' + (lc?.offsetHeight ?? -1) \
+ ',seStH=' + (lc?.querySelector('.monaco-scrollable-element')?.style?.height ?? '') \
+ ',wH=' + (w?.offsetHeight ?? -1);"
npx agent-browser eval "document.title" 2>&1
Key diagnostics:
rows — fewer than expected means list.layout() never received the correct viewport height.seStH — empty means the list was never properly laid out.listH vs wH — list container height should be widget height minus search bar minus footer.| Symptom | Root cause | Fix pattern |
|---|---|---|
| List shows 0-1 rows in a tall container | layout() bailed out because offsetHeight returned 0 during display:none → visible transition | Defer layout via DOM.getWindow(this.element).requestAnimationFrame(...) |
| Badge or row content clips at right edge | Widget container missing overflow: hidden | Add overflow: hidden to the widget's CSS class |
| Items visible in fixture but not in product | Fixture uses many mock items; real product has few | Add fixture variants with fewer items or narrower dimensions (width/height options) |
Fixtures render at a fixed size (default 900×600) with many mock items. They won't catch:
display:none → visible transition may not have reflowed before layout() fireswidth: 550, height: 400)