customizationHarnessService.ts ×16

Frontier kind: Code frontier

unlabeled · c_91d6a6e03e34

108 tests · 37837 LOC · 182 files · introduces 0 tests · 598 LOC · 2 files

Introduces — evidence that enters the hierarchy at this concept

Code
17 ranges598 lines · 2 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
3257 ranges37837 lines · 182 files · Browse complete extent
All tests (intent)
108 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

2 files ranked by introduced lines: 598 introduced LOC across 17 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/contrib/chat/common/customizationHarnessService.ts 452 introduced LOC · 16 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- customizationHarnessService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { Codicon } from '../../../../base/common/codicons.js';
7 > import { derived, IObservable, ISettableObservable, observableValue } from '../../../../base/common/observable.js';
8 > import { IDisposable } from '../../../../base/common/lifecycle.js';
9 > import { Emitter, Event } from '../../../../base/common/event.js';
10 > import { ThemeIcon } from '../../../../base/common/themables.js';
11 > import { URI } from '../../../../base/common/uri.js';
12 > import { localize } from '../../../../nls.js';
13 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
14 > import { AICustomizationManagementSection, AICustomizationSource, BUILTIN_STORAGE } from './aiCustomizationWorkspaceService.js';
15 > import { PromptsType } from './promptSyntax/promptTypes.js';
16 > import { AGENT_MD_FILENAME } from './promptSyntax/config/promptFileLocations.js';
17 > import { IAgentSource, IChatPromptSlashCommand, ICustomAgent, IPromptsService, IResolvedChatPromptSlashCommand, matchesSessionType, PromptsStorage } from './promptSyntax/service/promptsService.js';
18 > import { CancellationToken } from '../../../../base/common/cancellation.js';
19 > import { SessionType } from './chatSessionsService.js';
20 > import { CustomAgent } from './promptSyntax/service/promptsServiceImpl.js';
21 > import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js';
22 > import { getCanonicalPluginCommandId } from './plugins/agentPluginService.js';
23 > import { getChatSessionType, LocalChatSessionUri } from './model/chatUri.js';
24 >
25 >
26 > export const ICustomizationHarnessService = createDecorator<ICustomizationHarnessService>('customizationHarnessService');
27 >
28 > /**
29 > * Override for a management section's create-button behavior.
30 > */
31 > export interface ISectionOverride {
32 > /**
33 > * Label for the primary button. Required when `commandId` or `rootFile`
34 > * is set. Ignored otherwise (the widget uses its default label).
35 > */
36 > readonly label?: string;
37 > /** When set, the primary button invokes this command (e.g. hooks quick pick). */
38 > readonly commandId?: string;
39 > /** When set, the primary button creates this file at the workspace root. */
40 > readonly rootFile?: string;
41 > /**
42 > * Custom type label for the dropdown workspace/user create actions
43 > * (e.g. "Rule" instead of "Instruction"). When undefined, the
44 > * section's default type label is used.
45 > */
46 > readonly typeLabel?: string;
47 > /**
48 > * Root-level file shortcuts added to the dropdown (e.g. `['AGENTS.md']`).
49 > * Each entry creates a "New {filename}" action that creates the file at
50 > * the workspace root. Harnesses that don't support a file simply omit it.
51 > */
52 > readonly rootFileShortcuts?: readonly string[];
53 > /**
54 > * File extension override for new files created under this section.
55 > * When set, files are created with this extension (e.g. `.md` for
56 > * Claude rules) instead of the default for the prompt type
57 > * (e.g. `.instructions.md`).
58 > */
59 > readonly fileExtension?: string;
60 > }
61 >
62 > export interface ICustomizationItemAction {
63 > readonly id: string;
64 > readonly label: string;
65 > readonly tooltip?: string;
66 > readonly icon?: ThemeIcon;
67 > readonly enabled?: boolean;
68 > run(): void | Promise<void>;
69 > }
70 >
71 > /**
72 > * Describes a single harness option for the UI toggle.
73 > */
74 > export interface IHarnessDescriptor {
75 > /**
76 > * The harness/session-type identifier.
77 > */
78 > readonly id: string;
79 > readonly label: string;
80 > readonly icon: ThemeIcon;
81 > /**
82 > * Management sections that should be hidden when this harness is active.
83 > * For example, Claude does not support prompt files so the Prompts
84 > * section is hidden.
85 > */
86 > readonly hiddenSections?: readonly string[];
87 > /**
88 > * When `true`, the "Generate with AI" sparkle button is hidden and replaced
89 > * with a plain "New X" manual-creation button (like sessions).
90 > */
91 > readonly hideGenerateButton?: boolean;
92 > /**
93 > * Per-section overrides for the create button behavior.
94 > *
95 > * A `commandId` entry replaces the button entirely with a command
96 > * invocation (e.g. Claude hooks → `copilot.claude.hooks`).
97 > *
98 > * A `rootFile` entry makes the primary button create a specific file
99 > * at the workspace root (e.g. Claude instructions → `CLAUDE.md`).
100 > * When combined with `typeLabel`, the dropdown create actions use
101 > * that label instead of the section's default (e.g. "Rule" instead
102 > * of "Instruction").
103 > */
104 > readonly sectionOverrides?: ReadonlyMap<string, ISectionOverride>;
105 > /**
106 > * The chat agent ID that must be registered for this harness to appear.
107 > * When `undefined`, the harness is always available (e.g. Local).
108 > */
109 > readonly requiredAgentId?: string;
110 > /**
111 > * When set, this harness is backed by an extension-contributed provider
112 > * that can supply customization items directly (bypassing promptsService
113 > * discovery and filtering).
114 > */
115 > readonly itemProvider?: ICustomizationItemProvider;
116 > /**
117 > * When `true`, the "Troubleshoot" action is available in item context
118 > * menus. This opens chat with the `/troubleshoot` command pre-filled
119 > * for the selected customization.
120 > */
121 > readonly supportsTroubleshoot?: boolean;
122 > /**
123 > * When set, this harness uses an opt-out sync model where all eligible
124 > * local customizations are synced by default. The UI shows disable
125 > * affordances when this harness is active.
126 > */
127 > readonly syncProvider?: ICustomizationSyncProvider;
128 > /**
129 > * Optional plugin-management actions shown in the Plugins section add menu.
130 > * Harnesses can use these to add environment-specific commands alongside
131 > * the default install-from-source action (for example, configuring plugins on
132 > * a remote agent host). The create action remains a separate toolbar button.
133 > */
134 > readonly pluginActions?: readonly ICustomizationItemAction[];
135 > }
136 >
137 > /**
138 > * Represents a customization item provided by any source.
139 > */
140 > export interface ICustomizationItem {
141 > /** Optional stable identity used by list widgets when URI alone is not unique. */
142 > readonly itemKey?: string;
143 > readonly uri: URI;
144 > readonly type: string;
145 > readonly name: string;
146 > readonly description?: string;
147 > /** Customization source (local, user, extension, plugin, builtin). Set by providers that know the source. */
148 > readonly source: AICustomizationSource;
149 > /** The extension identifier that contributed this customization, if any. */
150 > readonly extensionId: string | undefined;
151 > /** The URI of the plugin that contributed this customization, if any. */
152 > readonly pluginUri: URI | undefined;
153 > /** Human-readable name of the plugin that contributed this customization, if any. */
154 > readonly pluginLabel?: string;
155 > /** Server-reported loading status for this customization. */
156 > readonly status?: 'loading' | 'loaded' | 'degraded' | 'error';
157 > /** Human-readable status detail (e.g. error message or warning). */
158 > readonly statusMessage?: string;
159 > /** Whether this customization is currently enabled. */
160 > readonly enabled?: boolean;
161 > /** When set, items with the same groupKey are displayed under a shared collapsible header. */
162 > readonly groupKey?: string;
163 > /** When set, shows a small inline badge next to the item name (e.g. an applyTo glob pattern). */
164 > readonly badge?: string;
165 > /** Tooltip shown when hovering the badge. */
166 > readonly badgeTooltip?: string;
167 > /**
168 > * Whether this customization item can be invoked by the user.
169 > * Relevant for prompt / skill and custom agents
170 > */
171 > readonly userInvocable?: boolean;
172 > /** Optional inline/context-menu actions specific to this item. */
173 > readonly actions?: readonly ICustomizationItemAction[];
174 > }
175 >
176 > export interface ICustomizationAgentRef {
177 > readonly id: string;
178 >
179 > readonly uri: URI;
180 > /** Agent name (from frontmatter `name`, or file-derived) */
181 > readonly name: string;
182 > /** Optional short description for UI preview (from frontmatter `description`) */
183 > readonly description?: string;
184 > }
185 >
186 > export function isPluginCustomizationItem(item: { readonly type: string }): boolean {
187 return item.type === 'plugin' || item.type === AICustomizationManagementSection.Plugins;
188 }
190 > /**
191 > * Provider interface for extension-contributed harnesses that supply
192 > * customization items directly from their SDK.
193 > */
194 > export interface ICustomizationItemProvider {
195 > /**
196 > * Event that fires when the provider's customizations change.
197 > */
198 > readonly onDidChange: Event<void>;
199 > /**
200 > * Provide the customization items this harness supports.
201 > *
202 > * @param sessionResource URI of the chat session whose
203 > * customizations should be included. Providers that surface
204 > * session-scoped state (e.g. an agent host) should read from
205 > * this session.
206 > */
207 > provideChatSessionCustomizations(sessionResource: URI, token: CancellationToken): Promise<ICustomizationItem[] | undefined>;
208 >
209 > /**
210 > * Provide the custom agents this harness supports.
211 > *
212 > * @param sessionResource URI of the chat session whose
213 > * customizations should be included. Providers that surface
214 > * session-scoped state (e.g. an agent host) should read from
215 > * this session.
216 > */
217 > provideCustomAgents?(sessionResource: URI, token: CancellationToken): Promise<readonly ICustomAgent[]>;
218 >
219 > /**
220 > * Provide the directories where new customization files of the given
221 > * type can be created for this session. The result includes both
222 > * workspace-scoped and user-scoped folders; the caller is responsible
223 > * for partitioning them by storage target.
224 > *
225 > * @param sessionResource URI of the chat session whose
226 > * creation locations should be returned.
227 > */
228 > provideSourceFolders?(sessionResource: URI, type: PromptsType, token: CancellationToken): Promise<readonly ICustomizationSourceFolder[] | undefined>;
229 > }
230 >
231 > /**
232 > * A directory where new customization files of a given type can be created.
233 > */
234 > export interface ICustomizationSourceFolder {
235 > readonly uri: URI;
236 > /** Display label for the picker when multiple folders are offered. */
237 > readonly label: string;
238 > /** Customization source for this folder (typically 'local' or 'user' for writable creation locations). */
239 > readonly source: AICustomizationSource;
240 > }
241 >
242 > /**
243 > * Provider interface for harnesses that use an opt-out sync model.
244 > *
245 > * Every eligible local customization is synced by default; the user
246 > * can disable individual items. The persisted set captures only the
247 > * user's opt-outs.
248 > */
249 > export interface ICustomizationSyncProvider {
250 > readonly onDidChange: Event<void>;
251 > isDisabled(uri: URI): boolean;
252 > setDisabled(uri: URI, disabled: boolean): void;
253 > }
254 >
255 > /**
256 > * Service that manages the active customization harness and provides
257 > * per-type storage source filters based on the selected harness.
258 > *
259 > * The default (core) registration exposes a single "VS Code" harness
260 > * that shows all storage sources. The sessions window overrides this
261 > * to provide CLI-scoped harnesses.
262 > */
263 > export interface ICustomizationHarnessService {
264 > readonly _serviceBrand: undefined;
265 >
266 > /**
267 > * The currently active chat session resource.
268 > */
269 > readonly activeSessionResource: IObservable<URI>;
270 >
271 > /**
272 > * The currently active harness.
273 > */
274 > readonly activeHarness: IObservable<string>;
275 >
276 > /**
277 > * All harnesses available in this window.
278 > * When only one harness is available the UI should hide the toggle.
279 > */
280 > readonly availableHarnesses: IObservable<readonly IHarnessDescriptor[]>;
281 >
282 > /**
283 > * Finds the descriptor of the harness with the given id, or `undefined` if no such harness exists.
284 > * @param sessionType The harness id (sessionType)
285 > */
286 > findHarnessById(sessionType: string): IHarnessDescriptor | undefined;
287 >
288 > /**
289 > * Changes the active session. The new session's type must be present in
290 > * `availableHarnesses`.
291 > */
292 > setActiveSession(sessionResource: URI): void;
293 >
294 > /**
295 > * Returns the descriptor of the currently active harness.
296 > */
297 > getActiveDescriptor(): IHarnessDescriptor;
298 >
299 > /**
300 > * Registers an external harness contributed by an extension.
301 > * The harness appears in the UI toggle alongside static harnesses.
302 > * Returns a disposable that removes the harness when disposed.
303 > */
304 > registerExternalHarness(descriptor: IHarnessDescriptor): IDisposable;
305 >
306 >
307 > /**
308 > * Fires when one of the provided slash commands changes.
309 > */
310 > readonly onDidChangeSlashCommands: Event<{ readonly sessionType: string }>;
311 >
312 > /**
313 > * Fires when one of the provided custom agents changes.
314 > */
315 > readonly onDidChangeCustomAgents: Event<{ readonly sessionType: string }>;
316 >
317 > /**
318 > * Returns the prompt and skill slash commands for the given session type.
319 > * Provider-backed harnesses contribute their own items directly; the default
320 > * VS Code harness falls back to the core prompts service.
321 > *
322 > * @param sessionResource URI of the chat session whose customizations
323 > * should be considered. Forwarded to the underlying
324 > * {@link ICustomizationItemProvider.provideChatSessionCustomizations}.
325 > */
326 > getSlashCommands(sessionResource: URI, token: CancellationToken): Promise<readonly IChatPromptSlashCommand[]>;
327 >
328 > /**
329 > * Returns the custom agents for the given session type.
330 > * Provider-backed harnesses select items via their own provider and resolve
331 > * details via the core prompts service.
332 > *
333 > * @param sessionResource URI of the chat session whose customizations
334 > * should be considered. Forwarded to the underlying
335 > * {@link ICustomizationItemProvider.provideChatSessionCustomizations}.
336 > */
337 > getCustomAgents(sessionResource: URI, token: CancellationToken): Promise<readonly ICustomAgent[]>;
338 >
339 > /**
340 > * Resolves a slash command to its full metadata, including the parsed prompt file for prompt commands.
341 > * Provider-backed harnesses resolve their own items directly; the default VS Code harness falls back to the core prompts service.
342 > *
343 > * @param sessionResource URI of the chat session whose customizations
344 > * should be considered when looking up the slash command.
345 > */
346 > resolvePromptSlashCommand(name: string, sessionResource: URI, token: CancellationToken): Promise<IResolvedChatPromptSlashCommand | undefined>;
347 >
348 > /**
349 > * Returns the best session resource to use for a harness lookup.
350 > * Implementations should prefer the most recently used session for the
351 > * given session type and fall back to an untitled session resource.
352 > */
353 > getSessionResourceForHarness(sessionType: string): URI;
354 > }
355 >
356 > /**
357 > * Minimal slash-command metadata resolved from the active harness.
358 > */
359 > export interface ICustomizationSlashCommand {
360 > readonly uri: URI;
361 > readonly type: PromptsType.prompt | PromptsType.skill;
362 > readonly name: string;
363 > readonly description?: string;
364 > readonly userInvocable: boolean;
365 > readonly sessionTypes?: readonly string[];
366 > }
367 >
368 > // #region Shared descriptor constants
369 >
370 > /**
371 > * Empty descriptor returned when no harness is registered yet.
372 > */
373 > const EMPTY_DESCRIPTOR: IHarnessDescriptor = {
374 > id: '',
375 > label: '',
376 > icon: Codicon.sparkle,
377 > };
378 >
379 >
380 > // #endregion
381 >
382 > // #region Harness descriptor factories
383 >
384 > /**
385 > * Builds the full source list from the base set (local, user, plugin)
386 > * plus any additional sources specific to the window type.
387 > *
388 > * Core passes `[PromptsStorage.extension]`; sessions passes its
389 > * BUILTIN_STORAGE constant.
390 > */
391 >
392 > /**
393 > * Creates a "VS Code" harness descriptor that shows all storage sources
394 > * with no user-root restrictions.
395 > */
396 > export function createVSCodeHarnessDescriptor(): IHarnessDescriptor {
397 return {
398 id: SessionType.Local,
408 };
409 }
411 > // #endregion
412 >
413 > // #region Base implementation
414 >
415 > /**
416 > * Reusable base implementation of {@link ICustomizationHarnessService}.
417 > * Concrete registrations only need to supply the list of harness
418 > * descriptors and a default harness id.
419 > */
420 > export class CustomizationHarnessServiceBase implements ICustomizationHarnessService {
421 > declare readonly _serviceBrand: undefined;
422 > private readonly _onDidChangeSlashCommands = new Emitter<{ readonly sessionType: string }>();
423 > readonly onDidChangeSlashCommands = this._onDidChangeSlashCommands.event;
424 > private readonly _onDidChangeCustomAgents = new Emitter<{ readonly sessionType: string }>();
425 > readonly onDidChangeCustomAgents = this._onDidChangeCustomAgents.event;
426 > private readonly _providerListeners: IDisposable[] = [];
427 > private _isDisposed = false;
428 >
429 > private readonly _activeSessionResource: ISettableObservable<URI>;
430 > readonly activeSessionResource: IObservable<URI>;
431 >
432 > private readonly _activeHarness: IObservable<string>;
433 > readonly activeHarness: IObservable<string>;
434 >
435 > private readonly _staticHarnesses: readonly IHarnessDescriptor[];
436 > private readonly _externalHarnesses: IHarnessDescriptor[] = [];
437 > private readonly _availableHarnesses: ISettableObservable<readonly IHarnessDescriptor[]>;
438 > readonly availableHarnesses: IObservable<readonly IHarnessDescriptor[]>;
439 >
440 > constructor(
441 staticHarnesses: readonly IHarnessDescriptor[],
442 defaultHarness: string,
453 this._rebindProviderListeners();
454 }
456 > private _getAllHarnesses(): readonly IHarnessDescriptor[] {
457 // External harnesses shadow static ones with the same id so that
458 // extension-contributed harnesses can upgrade a built-in entry.
471 this._rebindProviderListeners();
472 }
474 > private _rebindProviderListeners(): void {
475 for (const listener of this._providerListeners) {
476 listener.dispose();
488 }
489 }
491 > dispose(): void {
492 this._isDisposed = true;
493 for (const listener of this._providerListeners) {
498 this._onDidChangeCustomAgents.dispose();
499 }
501 > registerExternalHarness(descriptor: IHarnessDescriptor): IDisposable {
502 this._externalHarnesses.push(descriptor);
503 this._refreshAvailableHarnesses();
515 };
516 }
518 > findHarnessById(id: string): IHarnessDescriptor | undefined {
519 return this._getAllHarnesses().find(h => h.id === id);
520 }
522 > setActiveSession(sessionResource: URI): void {
523 this._activeSessionResource.set(sessionResource, undefined);
524 }
526 > getActiveDescriptor(): IHarnessDescriptor {
527 const activeId = this._activeHarness.get();
528 const descriptor = this.findHarnessById(activeId);
529 return descriptor ?? EMPTY_DESCRIPTOR;
530 }
532 > async getSlashCommands(sessionResource: URI, token: CancellationToken): Promise<readonly IChatPromptSlashCommand[]> {
533 const sessionType = getChatSessionType(sessionResource);
534 const harness = this.findHarnessById(sessionType);
565 return result;
566 }
568 > async getCustomAgents(sessionResource: URI, token: CancellationToken): Promise<readonly ICustomAgent[]> {
569 const sessionType = getChatSessionType(sessionResource);
570 const harness = this.findHarnessById(sessionType);
613 return result;
614 }
616 > public async resolvePromptSlashCommand(name: string, sessionResource: URI, token: CancellationToken): Promise<IResolvedChatPromptSlashCommand | undefined> {
617 const commands = await this.getSlashCommands(sessionResource, token);
618 const command = commands.find(cmd => cmd.name === name);
627 return undefined;
628 }
630 > getSessionResourceForHarness(sessionType: string): URI {
631 if (sessionType === SessionType.Local) {
632 return LocalChatSessionUri.getNewSessionUri();
src/vs/workbench/contrib/chat/common/aiCustomizationWorkspaceService.ts 146 introduced LOC · 1 range

Open complete file

1 > /*--------------------------------------------------------------------------------------------- aiCustomizationWorkspaceService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { CancellationToken } from '../../../../base/common/cancellation.js';
7 > import { IObservable } from '../../../../base/common/observable.js';
8 > import { URI } from '../../../../base/common/uri.js';
9 > import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js';
10 > import { PromptsType } from './promptSyntax/promptTypes.js';
11 > import { IChatPromptSlashCommand } from './promptSyntax/service/promptsService.js';
12 >
13 > export const IAICustomizationWorkspaceService = createDecorator<IAICustomizationWorkspaceService>('aiCustomizationWorkspaceService');
14 >
15 > /**
16 > * Extended storage type for AI Customization that includes built-in prompts
17 > * shipped with the application, alongside the core `PromptsStorage` values.
18 > */
19 > export type AICustomizationSource = 'local' | 'user' | 'extension' | 'plugin' | 'builtin';
20 >
21 > export namespace AICustomizationSources {
22 > export const local: AICustomizationSource = 'local';
23 > export const user: AICustomizationSource = 'user';
24 > export const extension: AICustomizationSource = 'extension';
25 > export const plugin: AICustomizationSource = 'plugin';
26 > export const builtin: AICustomizationSource = 'builtin';
27 > export const all: AICustomizationSource[] = [local, user, extension, plugin, builtin];
28 > }
29 >
30 > /**
31 > * Storage type discriminator for built-in customizations shipped with the application.
32 > */
33 > export const BUILTIN_STORAGE = AICustomizationSources.builtin;
34 >
35 > /**
36 > * Possible section IDs for the AI Customization Management Editor sidebar.
37 > */
38 > export const AICustomizationManagementSection = {
39 > Agents: 'agents',
40 > Skills: 'skills',
41 > Instructions: 'instructions',
42 > Prompts: 'prompts',
43 > Hooks: 'hooks',
44 > Automations: 'automations',
45 > McpServers: 'mcpServers',
46 > Plugins: 'plugins',
47 > Models: 'models',
48 > Tools: 'tools',
49 > HarnessSettings: 'harnessSettings',
50 > } as const;
51 >
52 > export type AICustomizationManagementSection = typeof AICustomizationManagementSection[keyof typeof AICustomizationManagementSection];
53 >
54 > /**
55 > * Per-type filter policy controlling which storage sources are visible
56 > * for a given customization type.
57 > */
58 > export interface IWelcomePageFeatures {
59 > /** Show the "Configure Your AI" getting-started banner. */
60 > readonly showGettingStartedBanner: boolean;
61 > }
62 >
63 > /**
64 > * Provides workspace context for AI Customization views.
65 > */
66 > export interface IAICustomizationWorkspaceService {
67 > readonly _serviceBrand: undefined;
68 >
69 > /**
70 > * Observable that fires when the active project root changes.
71 > */
72 > readonly activeProjectRoot: IObservable<URI | undefined>;
73 >
74 > /**
75 > * Returns the current active project root, if any.
76 > */
77 > getActiveProjectRoot(): URI | undefined;
78 >
79 > /**
80 > * The sections to show in the AI Customization Management Editor sidebar.
81 > */
82 > readonly managementSections: readonly AICustomizationManagementSection[];
83 >
84 > /**
85 > * Whether this is a sessions window (vs core VS Code).
86 > */
87 > readonly isSessionsWindow: boolean;
88 >
89 > /**
90 > * Controls which features are displayed on the welcome page.
91 > */
92 > readonly welcomePageFeatures: IWelcomePageFeatures;
93 >
94 > /**
95 > * Commits files in the active project.
96 > */
97 > commitFiles(projectRoot: URI, fileUris: URI[]): Promise<void>;
98 >
99 > /**
100 > * Commits the deletion of resources that have already been removed from disk.
101 > * The URIs may point to individual files or to directories (for example, when
102 > * deleting a skill, the entire customization folder is removed). Implementations
103 > * should ensure that directory deletions are handled recursively as needed.
104 > * In sessions this stages and commits the removal in the relevant repositories.
105 > */
106 > deleteFiles(projectRoot: URI, fileUris: URI[]): Promise<void>;
107 >
108 > /**
109 > * Launches the AI-guided creation flow for the given customization type.
110 > */
111 > generateCustomization(type: PromptsType): Promise<void>;
112 >
113 > /**
114 > * Whether a transient project root override is currently active.
115 > */
116 > readonly hasOverrideProjectRoot: IObservable<boolean>;
117 >
118 > /**
119 > * Sets a transient override for the active project root.
120 > * While set, `activeProjectRoot` returns this value instead of the
121 > * session- or workspace-derived root. Call `clearOverrideProjectRoot()` to revert.
122 > */
123 > setOverrideProjectRoot(root: URI): void;
124 >
125 > /**
126 > * Clears the transient project root override, reverting to the
127 > * session-derived (or workspace-derived) root.
128 > */
129 > clearOverrideProjectRoot(): void;
130 >
131 > /**
132 > * Returns prompt/skill slash commands filtered through the workspace
133 > * service's storage source policy, ensuring the results match the
134 > * customizations visible in the AI Customization views.
135 > */
136 > getFilteredPromptSlashCommands(token: CancellationToken): Promise<readonly IChatPromptSlashCommand[]>;
137 >
138 > /**
139 > * Returns a map of built-in skill names that have direct UI integrations
140 > * (toolbar buttons, menu items, etc.) to a tooltip describing the
141 > * integration. Used to display a 'UI Integration' badge in the
142 > * customizations editor, especially important when users override a
143 > * built-in skill that drives a UI surface.
144 > */
145 > getSkillUIIntegrations(): ReadonlyMap<string, string>;
146 > }