promptsServiceImpl.ts ×15

Frontier kind: Code frontier

unlabeled · c_9e0c9f1f2641

138 tests · 67368 LOC · 326 files · introduces 0 tests · 213 LOC · 5 files

Introduces — evidence that enters the hierarchy at this concept

Code
27 ranges213 lines · 5 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
6411 ranges67368 lines · 326 files · Browse complete extent
All tests (intent)
138 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.

5 files ranked by introduced lines: 213 introduced LOC across 27 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsServiceImpl.ts 153 introduced LOC · 15 ranges

Open complete file

136
137 constructor(
138 > @ILogService public readonly logger: ILogService, promptsServiceImpl.ts
139 > @ILabelService private readonly labelService: ILabelService,
140 > @IModelService private readonly modelService: IModelService,
141 > @IInstantiationService protected readonly instantiationService: IInstantiationService,
142 > @IUserDataProfileService private readonly userDataService: IUserDataProfileService,
143 > @IConfigurationService private readonly configurationService: IConfigurationService,
144 > @IFileService protected readonly fileService: IFileService,
145 > @IStorageService private readonly storageService: IStorageService,
146 > @ITelemetryService private readonly telemetryService: ITelemetryService,
147 > @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService,
148 > @IPathService protected readonly pathService: IPathService,
149 > @IAgentPluginService private readonly agentPluginService: IAgentPluginService,
150 > @IWorkspaceTrustManagementService private readonly workspaceTrustService: IWorkspaceTrustManagementService,
151 > ) {
152 > super();
153 >
154 > this.fileLocator = this.createPromptFilesLocator();
155 >
156 > this._register(this.modelService.onModelRemoved((model) => {
157 this.cachedParsedPromptFromModels.delete(model.uri);
159 >
160 > this.extensionPromptFiles = this._register(this.instantiationService.createInstance(ExtensionPromptFileService));
161 > const onDidChangeExtensionPromptFiles = this.extensionPromptFiles.onDidChange;
162 >
163 > // Invalidate the cached file location list whenever an extension contribution
164 > // or provider for the same type changes (or its `when` re-evaluates).
165 > this._register(onDidChangeExtensionPromptFiles(e => {
166 this.cachedFileLocations[e.type] = undefined;
168 >
169 > const modelChangeEvent = this._register(new ModelChangeTracker(this.modelService)).onDidPromptChange;
170 > this.cachedCustomAgents = this._register(new CachedPromise(
171 > (token) => this.computeAgentDiscoveryInfo(token),
172 > () => Event.any(
173 > this.getFileLocatorEvent(PromptsType.agent),
174 > Event.filter(modelChangeEvent, e => e.promptType === PromptsType.agent),
175 > Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(PromptsConfig.USE_CHAT_HOOKS)),
176 > Event.filter(onDidChangeExtensionPromptFiles, e => e.type === PromptsType.agent),
177 > Event.filter(this._onDidPluginPromptFilesChange.event, t => t === PromptsType.agent),
178 > this.workspaceTrustService.onDidChangeTrust,
179 > )
180 > ));
181 >
182 > this.cachedSlashCommands = this._register(new CachedPromise(
183 > (token) => this.computeSlashCommandDiscoveryInfo(token),
184 > () => Event.any(
185 > this.getFileLocatorEvent(PromptsType.prompt),
186 > this.getFileLocatorEvent(PromptsType.skill),
187 > Event.filter(modelChangeEvent, e => e.promptType === PromptsType.prompt),
188 > Event.filter(modelChangeEvent, e => e.promptType === PromptsType.skill),
189 > Event.filter(onDidChangeExtensionPromptFiles, e => e.type === PromptsType.prompt || e.type === PromptsType.skill),
190 > Event.filter(this._onDidPluginPromptFilesChange.event, t => t === PromptsType.prompt || t === PromptsType.skill)),
191 > ));
192 >
193 > this.cachedSkills = this._register(new CachedPromise(
194 > (token) => this.computeSkillDiscovery(token),
195 > () => Event.any(
196 > this.getFileLocatorEvent(PromptsType.skill),
197 > Event.filter(modelChangeEvent, e => e.promptType === PromptsType.skill),
198 > Event.filter(onDidChangeExtensionPromptFiles, e => e.type === PromptsType.skill),
199 > Event.filter(this._onDidPluginPromptFilesChange.event, t => t === PromptsType.skill))
200 > ));
201 >
202 > this.cachedHooks = this._register(new CachedPromise(
203 > (token) => this.computeHooks(token),
204 > () => Event.any(
205 > this.getFileLocatorEvent(PromptsType.hook),
206 > Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(PromptsConfig.USE_CHAT_HOOKS) || e.affectsConfiguration(PromptsConfig.USE_CLAUDE_HOOKS)),
207 > this._onDidPluginHooksChange.event,
208 > this.workspaceTrustService.onDidChangeTrust,
209 > )
210 > ));
211 >
212 > this.cachedInstructions = this._register(new CachedPromise(
213 > (token) => this.computeInstructionFiles(token),
214 > () => Event.any(
215 > this.getFileLocatorEvent(PromptsType.instructions),
216 > Event.filter(onDidChangeExtensionPromptFiles, e => e.type === PromptsType.instructions),
217 > Event.filter(this._onDidPluginPromptFilesChange.event, t => t === PromptsType.instructions),
218 > )
219 > ));
220 >
221 > this._register(this.watchPluginPromptFilesForType(
222 > PromptsType.prompt,
223 > (plugin, reader) => plugin.commands.read(reader),
224 > ));
225 > this._register(this.watchPluginPromptFilesForType(
226 > PromptsType.skill,
227 > (plugin, reader) => plugin.skills.read(reader),
228 > ));
229 > this._register(this.watchPluginPromptFilesForType(
230 > PromptsType.agent,
231 > (plugin, reader) => plugin.agents.read(reader),
232 > ));
233 > this._register(this.watchPluginPromptFilesForType(
234 > PromptsType.instructions,
235 > (plugin, reader) => plugin.instructions.read(reader),
236 > ));
237 >
238 > this._register(autorun(reader => {
239 > const plugins = this.agentPluginService.plugins.read(reader);
240 > const hookFiles: IPluginPromptPath[] = [];
241 > for (const plugin of plugins) {
242 if (isContributionEnabled(plugin.enablement.read(reader))) {
243 for (const hook of plugin.hooks.read(reader)) {
254 }
255 }
257 > this._pluginPromptFilesByType.set(PromptsType.hook, hookFiles);
258 > this.cachedFileLocations[PromptsType.hook] = undefined;
259 > this._onDidPluginHooksChange.fire();
260 > }));
261 > }
262
263 private watchPluginPromptFilesForType(
264 > type: PromptsType, promptsServiceImpl.ts
265 > getItems: (plugin: IAgentPlugin, reader: IReader) => readonly { uri: URI; name: string }[],
266 > ) {
267 > return autorun(reader => {
268 > const plugins = this.agentPluginService.plugins.read(reader);
269 > const nextFiles: IPluginPromptPath[] = [];
270 > for (const plugin of plugins) {
271 if (!isContributionEnabled(plugin.enablement.read(reader))) {
272 continue;
284 }
285 }
287 > nextFiles.sort((a, b) => `${a.name ?? ''}|${a.uri.toString()}`.localeCompare(`${b.name ?? ''}|${b.uri.toString()}`));
288 > this._pluginPromptFilesByType.set(type, nextFiles);
289 > this.cachedFileLocations[type] = undefined;
290 > this._onDidPluginPromptFilesChange.fire(type);
291 > });
292 > }
293
294 protected createPromptFilesLocator(): PromptFilesLocator {
295 > return this.instantiationService.createInstance(PromptFilesLocator); promptsServiceImpl.ts
296 > }
297
298 private getFileLocatorEvent(type: PromptsType): Event<void> {
299 > let event = this.fileLocatorEvents[type]; promptsServiceImpl.ts
300 > if (!event) {
301 > event = this.fileLocatorEvents[type] = this._register(this.fileLocator.createFilesUpdatedEvent(type)).event;
302 > this._register(event(() => {
303 this.cachedFileLocations[type] = undefined;
305 > }
306 > return event;
307 > }
308
309 public getParsedPromptFile(textModel: ITextModel): ParsedPromptFile {
1439
1440 constructor(private readonly computeFn: (token: CancellationToken) => Promise<T>, private readonly getEvent: () => Event<void>, private readonly delay: number = 0) {
1441 > super(); promptsServiceImpl.ts
1442 > this.onDidUpdatePromiseEmitter = this._register(new Emitter<void>());
1443 > const delayer = this._register(new Delayer<void>(this.delay));
1444 > this._register(this.getEvent()(() => {
1445 > this.cachedPromise = undefined;
1446 > delayer.trigger(() => this.onDidUpdatePromiseEmitter.fire());
1447 > }));
1448 > }
1449
1450 public get onDidChangePromise(): Event<void> {
1508
1509 constructor(modelService: IModelService) {
1510 > super(); promptsServiceImpl.ts
1511 > this.onDidPromptModelChange = this._register(new Emitter<ModelChangeEvent>());
1512 > const onAdd = (model: ITextModel) => {
1513 const promptType = getPromptsTypeForLanguageId(model.getLanguageId());
1514 if (promptType !== undefined) {
1517 return promptType;
1518 };
1519 > const onRemove = (languageId: string, uri: URI) => { promptsServiceImpl.ts
1520 const promptType = getPromptsTypeForLanguageId(languageId);
1521 if (promptType !== undefined) {
1525 return promptType;
1526 };
1527 > this._register(modelService.onModelAdded(model => onAdd(model))); promptsServiceImpl.ts
1528 > this._register(modelService.onModelLanguageChanged(e => {
1529 const removedPromptType = onRemove(e.oldLanguageId, e.model.uri);
1530 const addedPromptType = onAdd(e.model);
1537 }
1538 }
1539 > })); promptsServiceImpl.ts
1540 > this._register(modelService.onModelRemoved(model => onRemove(model.getLanguageId(), model.uri)));
1541 > }
1542
1543 public override dispose(): void {
1544 > super.dispose(); promptsServiceImpl.ts
1545 > this.listeners.forEach(listener => listener.dispose());
1546 > this.listeners.clear();
1547 > }
1548 }
1549
src/vs/workbench/contrib/chat/common/promptSyntax/utils/promptFilesLocator.ts 46 introduced LOC · 8 ranges

Open complete file

85
86 protected onDidChangeWorkspaceFolders(): Event<void> {
87 > return Event.map(this.workspaceService.onDidChangeWorkspaceFolders, () => undefined); promptFilesLocator.ts
88 > }
89
90 /**
201
202 public createFilesUpdatedEvent(type: PromptsType): { readonly event: Event<void>; dispose: () => void } {
203 > const disposables = new DisposableStore(); promptFilesLocator.ts
204 > const eventEmitter = disposables.add(new Emitter<void>());
205 > const token = disposables.add(new CancellationTokenSource()).token; // track the disposal of the event listeners so we can cancel any in-flight async operations when the event is disposed
206 >
207 > const externalFolderWatchers = disposables.add(new DisposableStore());
208 > const key = getPromptFileLocationsConfigKey(type);
209 > const userDataFolder = this.userDataService.currentProfile.promptsHome;
210 >
211 > let parentFolders: readonly IResolvedPromptSourceFolder[] = [];
212 >
213 > const updateExternalFolderWatchers = () => {
214 > externalFolderWatchers.clear();
215 > for (const folder of parentFolders) {
216 > if (!this.getWorkspaceFolder(folder.searchRoot)) {
217 > // if the folder is not part of the workspace, we need to watch it
218 > const recursive = folder.filePattern !== undefined || type === PromptsType.instructions; // instructions can be in subfolders, so watch recursively
219 > externalFolderWatchers.add(this.fileService.watch(folder.searchRoot, { recursive, excludes: [] }));
220 > }
221 > }
222 > };
223 >
224 > const update = async () => {
225 > try {
226 > const configuredLocations = this.getPromptSourceFolders(type);
227 > parentFolders = await this.toAbsoluteLocations(type, configuredLocations, undefined);
228 >
229 > if (token.isCancellationRequested) {
230 return;
231 }
232 > updateExternalFolderWatchers(); promptFilesLocator.ts
233 > } catch (err) {
234 this.logService.error(`Error updating prompt file watchers after config change:`, err);
235 }
237 > disposables.add(this.configService.onDidChangeConfiguration(e => {
238 if (e.affectsConfiguration(key) || e.affectsConfiguration(PromptsConfig.USE_CUSTOMIZATIONS_IN_PARENT_REPOS)) {
239 void update();
240 eventEmitter.fire();
241 }
243 > disposables.add(this.onDidChangeWorkspaceFolders()(() => {
244 void update();
245 eventEmitter.fire();
247 > disposables.add(this.workspaceTrustManagementService.onDidChangeTrustedFolders(() => {
248 void update();
249 eventEmitter.fire();
251 > disposables.add(this.fileService.onDidFilesChange(e => {
252 if (e.affects(userDataFolder)) {
253 eventEmitter.fire();
258 return;
259 }
261 > disposables.add(this.fileService.watch(userDataFolder));
262 >
263 > void update();
264 >
265 > return { event: eventEmitter.event, dispose: () => disposables.dispose() };
266 > }
267
268 public createAgentInstructionsUpdatedEvent(): { readonly event: Event<void>; dispose: () => void } {
src/vs/workbench/contrib/chat/common/promptSyntax/service/extensionPromptFileService.ts 12 introduced LOC · 2 ranges

Open complete file

102
103 constructor(
104 > @ILogService private readonly logger: ILogService, extensionPromptFileService.ts
105 > @IFileService private readonly fileService: IFileService,
106 > @IModelService private readonly modelService: IModelService,
107 > @IExtensionService private readonly extensionService: IExtensionService,
108 > @IFilesConfigurationService private readonly filesConfigService: IFilesConfigurationService,
109 > @IContextKeyService private readonly contextKeyService: IContextKeyService,
110 > ) {
111 > super();
112 >
113 > this._register(this.contextKeyService.onDidChangeContext(e => {
114 if (e.affectsSome(this._contributedWhenKeys)) {
115 // A tracked context key changed; the visibility of any
src/vs/workbench/contrib/chat/common/promptSyntax/config/config.ts 1 introduced LOC · 1 range

Open complete file

268 return PromptsConfig.PROMPT_LOCATIONS_KEY;
269 case PromptsType.agent:
270 > return PromptsConfig.AGENTS_LOCATION_KEY; config.ts
271 case PromptsType.skill:
272 return PromptsConfig.SKILLS_LOCATION_KEY;
src/vs/workbench/contrib/chat/common/promptSyntax/config/promptFileLocations.ts 1 introduced LOC · 1 range

Open complete file

320 return DEFAULT_PROMPT_SOURCE_FOLDERS;
321 case PromptsType.agent:
322 > return DEFAULT_AGENT_SOURCE_FOLDERS; promptFileLocations.ts
323 case PromptsType.skill:
324 return DEFAULT_SKILL_SOURCE_FOLDERS;