remove internal prompt files (#306009)

This commit is contained in:
Martin Aeschlimann
2026-03-28 23:18:26 +01:00
committed by GitHub
parent c38a4a4a4b
commit f4ca6f1926
10 changed files with 26 additions and 333 deletions

View File

@@ -532,13 +532,6 @@ export class PromptFilePickers {
result.push(...sortByLabel(await Promise.all(plugins.map(p => this._createPromptPickItem(p, pluginButtons, getVisibility(p), token)))));
}
// Internal built-in files are read-only
const internals = await this._promptsService.listPromptFilesForStorage(options.type, PromptsStorage.internal, token);
if (internals.length) {
const internalButtons: IQuickInputButton[] = [];
result.push({ type: 'separator', label: localize('separator.builtin', "Built-in") });
result.push(...sortByLabel(await Promise.all(internals.map(p => this._createPromptPickItem(p, internalButtons, getVisibility(p), token)))));
}
return result;
}
@@ -587,9 +580,6 @@ export class PromptFilePickers {
case PromptsStorage.plugin:
tooltip = promptFile.name;
break;
case PromptsStorage.internal:
tooltip = undefined;
break;
default:
assertNever(promptFile);
}

View File

@@ -14,7 +14,6 @@ import { IConfigurationService } from '../../../../../platform/configuration/com
import { IHoverService } from '../../../../../platform/hover/browser/hover.js';
import { IOpenerService } from '../../../../../platform/opener/common/opener.js';
import product from '../../../../../platform/product/common/product.js';
import { CHAT_INTERNAL_SCHEME } from '../../common/promptSyntax/internalCustomizations/internalPromptFileSystem.js';
const _remoteImageDisallowed = () => false;
@@ -81,7 +80,7 @@ export class ChatContentMarkdownRenderer implements IMarkdownRenderer {
override: allowedChatMarkdownHtmlTags,
},
...options?.sanitizerConfig,
allowedLinkSchemes: { augment: [product.urlProtocol, CHAT_INTERNAL_SCHEME, 'copilot-skill'] },
allowedLinkSchemes: { augment: [product.urlProtocol, 'copilot-skill'] },
remoteImageIsAllowed: _remoteImageDisallowed,
}
};

View File

@@ -452,7 +452,7 @@ export class CustomChatMode implements IChatMode {
type IChatModeSourceData =
| { readonly storage: PromptsStorage.extension; readonly extensionId: string; type?: ExtensionAgentSourceType }
| { readonly storage: PromptsStorage.local | PromptsStorage.user | PromptsStorage.internal }
| { readonly storage: PromptsStorage.local | PromptsStorage.user }
| { readonly storage: PromptsStorage.plugin; readonly pluginUri: URI };
function isChatModeSourceData(value: unknown): value is IChatModeSourceData {
@@ -466,7 +466,7 @@ function isChatModeSourceData(value: unknown): value is IChatModeSourceData {
if (data.storage === PromptsStorage.plugin) {
return isUriComponents(data.pluginUri);
}
return data.storage === PromptsStorage.local || data.storage === PromptsStorage.user || data.storage === PromptsStorage.internal;
return data.storage === PromptsStorage.local || data.storage === PromptsStorage.user;
}
function serializeChatModeSource(source: IAgentSource | undefined): IChatModeSourceData | undefined {
@@ -492,9 +492,6 @@ function reviveChatModeSource(data: IChatModeSourceData | undefined): IAgentSour
if (data.storage === PromptsStorage.plugin) {
return { storage: PromptsStorage.plugin, pluginUri: URI.revive(data.pluginUri) };
}
if (data.storage === PromptsStorage.internal) {
return { storage: PromptsStorage.internal };
}
return { storage: data.storage };
}

View File

@@ -125,7 +125,6 @@ export enum PromptFileSource {
ExtensionContribution = 'extension-contribution',
ExtensionAPI = 'extension-api',
Plugin = 'plugin',
Internal = 'internal',
}
/**

View File

@@ -1,75 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from '../../../../../../base/common/lifecycle.js';
import { URI } from '../../../../../../base/common/uri.js';
import { IFileService } from '../../../../../../platform/files/common/files.js';
import { IAgentSkill, IInternalPromptPath, PromptsStorage } from '../service/promptsService.js';
import { PromptsType } from '../promptTypes.js';
import { registerChatInternalFileSystem } from './internalPromptFileSystem.js';
import { InternalSkill } from './internalSkill.js';
export { InternalSkill } from './internalSkill.js';
/**
* Manages built-in internal customizations (skills, instructions, agents, etc.)
* backed by a readonly virtual filesystem.
*
* To add a new internal skill, create an {@link InternalSkill} instance and
* register it in the constructor.
*/
export class ChatInternalCustomizations extends Disposable {
private readonly skills: readonly InternalSkill[];
private readonly skillsByUri = new Map<string, InternalSkill>();
constructor(fileService: IFileService) {
super();
const { provider, disposable: fsDisposable } = registerChatInternalFileSystem(fileService);
this._register(fsDisposable);
// --- Built-in skills (add new entries here) ---
this.skills = [];
for (const skill of this.skills) {
provider.registerFile(skill.uri, skill.content);
this.skillsByUri.set(skill.uri.toString(), skill);
this._register(skill);
}
}
/**
* Returns the {@link IAgentSkill} metadata for all internal skills,
* for injection into the skills list.
*/
getSkills(): readonly IAgentSkill[] {
return this.skills.map(s => s.skill);
}
/**
* Looks up the {@link InternalSkill} instance for a given URI,
* e.g. to check its {@link InternalSkill.when} clause.
*/
getInternalSkillByUri(uri: URI): InternalSkill | undefined {
return this.skillsByUri.get(uri.toString());
}
/**
* Returns internal prompt file paths for a given customization type.
*/
getPromptPaths(type: PromptsType): readonly IInternalPromptPath[] {
if (type === PromptsType.skill) {
return this.skills.map(s => ({
uri: s.uri,
storage: PromptsStorage.internal as const,
type,
name: s.name,
description: s.description,
}));
}
return [];
}
}

View File

@@ -1,129 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { VSBuffer } from '../../../../../../base/common/buffer.js';
import { Event } from '../../../../../../base/common/event.js';
import { Disposable, IDisposable } from '../../../../../../base/common/lifecycle.js';
import { URI } from '../../../../../../base/common/uri.js';
import { createFileSystemProviderError, FileSystemProviderCapabilities, FileSystemProviderErrorCode, FileType, IFileDeleteOptions, IFileOverwriteOptions, IFileService, IFileSystemProviderWithFileReadWriteCapability, IFileWriteOptions, IStat } from '../../../../../../platform/files/common/files.js';
/**
* URI scheme for internal chat prompt files (skills, instructions, agents, etc.)
* backed by a readonly virtual filesystem.
*/
export const CHAT_INTERNAL_SCHEME = 'vscode-chat-internal';
/**
* A readonly virtual filesystem provider for internal chat prompt files.
*
* Files are registered at startup via {@link registerFile} and cannot be
* modified or deleted afterwards.
*/
export class ChatInternalFileSystemProvider extends Disposable implements IFileSystemProviderWithFileReadWriteCapability {
private readonly files = new Map<string, Uint8Array>();
readonly onDidChangeCapabilities = Event.None;
readonly onDidChangeFile = Event.None;
readonly capabilities = FileSystemProviderCapabilities.FileReadWrite
| FileSystemProviderCapabilities.Readonly
| FileSystemProviderCapabilities.PathCaseSensitive;
/**
* Register a file with static content. Must be called before the
* file can be read. Typically called once at startup.
*/
registerFile(uri: URI, content: string): void {
this.files.set(uri.toString(), VSBuffer.fromString(content).buffer);
}
// --- IFileSystemProvider ---
watch(): IDisposable {
return Disposable.None;
}
async stat(resource: URI): Promise<IStat> {
const data = this.files.get(resource.toString());
if (data) {
return {
type: FileType.File,
ctime: 0,
mtime: 0,
size: data.byteLength,
};
}
// Check if this is a directory (any registered file has this as prefix)
const prefix = resource.toString() + '/';
for (const key of this.files.keys()) {
if (key.startsWith(prefix)) {
return { type: FileType.Directory, ctime: 0, mtime: 0, size: 0 };
}
}
throw createFileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
}
async mkdir(): Promise<void> {
throw createFileSystemProviderError('readonly filesystem', FileSystemProviderErrorCode.NoPermissions);
}
async readdir(resource: URI): Promise<[string, FileType][]> {
const prefix = resource.toString() + '/';
const entries = new Map<string, FileType>();
for (const key of this.files.keys()) {
if (key.startsWith(prefix)) {
const rest = key.substring(prefix.length);
const slash = rest.indexOf('/');
if (slash === -1) {
entries.set(rest, FileType.File);
} else {
entries.set(rest.substring(0, slash), FileType.Directory);
}
}
}
return [...entries.entries()];
}
async delete(_resource: URI, _opts: IFileDeleteOptions): Promise<void> {
throw createFileSystemProviderError('readonly filesystem', FileSystemProviderErrorCode.NoPermissions);
}
async rename(_from: URI, _to: URI, _opts: IFileOverwriteOptions): Promise<void> {
throw createFileSystemProviderError('readonly filesystem', FileSystemProviderErrorCode.NoPermissions);
}
async readFile(resource: URI): Promise<Uint8Array> {
const data = this.files.get(resource.toString());
if (data) {
return data;
}
throw createFileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound);
}
async writeFile(_resource: URI, _content: Uint8Array, _opts: IFileWriteOptions): Promise<void> {
throw createFileSystemProviderError('readonly filesystem', FileSystemProviderErrorCode.NoPermissions);
}
}
/**
* Registers the internal chat filesystem provider with the file service,
* populates it with built-in files, and returns both the provider (for
* event subscription) and a disposable for cleanup.
*/
export function registerChatInternalFileSystem(fileService: IFileService): { provider: ChatInternalFileSystemProvider; disposable: IDisposable } {
const provider = new ChatInternalFileSystemProvider();
const registration = fileService.registerProvider(CHAT_INTERNAL_SCHEME, provider);
return {
provider,
disposable: {
dispose() {
registration.dispose();
provider.dispose();
}
}
};
}

View File

@@ -1,58 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from '../../../../../../base/common/lifecycle.js';
import { URI } from '../../../../../../base/common/uri.js';
import { ContextKeyExpression } from '../../../../../../platform/contextkey/common/contextkey.js';
import { IAgentSkill, PromptsStorage } from '../service/promptsService.js';
import { CHAT_INTERNAL_SCHEME } from './internalPromptFileSystem.js';
/**
* A built-in skill backed by the internal readonly virtual filesystem.
*
* To add a new internal skill, create an instance documenting the skill
* name, description and SKILL.md content, then add it to the
* {@link internalSkills} array in {@link internalCustomizations.ts}.
*/
export class InternalSkill extends Disposable {
/** Virtual filesystem URI for the SKILL.md file. */
readonly uri: URI;
/** The skill metadata exposed to the skills list and system prompt. */
readonly skill: IAgentSkill;
/**
* Optional context key expression. When set, the skill is only included
* in the system prompt when this expression evaluates to true.
*/
readonly when: ContextKeyExpression | undefined;
constructor(
readonly name: string,
readonly description: string,
readonly content: string,
options?: {
disableModelInvocation?: boolean;
userInvocable?: boolean;
when?: ContextKeyExpression;
},
) {
super();
this.uri = URI.from({
scheme: CHAT_INTERNAL_SCHEME,
path: `/skills/${name}/SKILL.md`,
});
this.skill = {
uri: this.uri,
storage: PromptsStorage.internal,
name,
description,
disableModelInvocation: options?.disableModelInvocation ?? false,
userInvocable: options?.userInvocable ?? true,
};
this.when = options?.when;
}
}

View File

@@ -75,7 +75,6 @@ export enum PromptsStorage {
user = 'user',
extension = 'extension',
plugin = 'plugin',
internal = 'internal',
}
/**
@@ -90,7 +89,7 @@ export enum ExtensionAgentSourceType {
* Represents a prompt path with its type.
* This is used for both prompt files and prompt source folders.
*/
export type IPromptPath = IExtensionPromptPath | ILocalPromptPath | IUserPromptPath | IPluginPromptPath | IInternalPromptPath;
export type IPromptPath = IExtensionPromptPath | ILocalPromptPath | IUserPromptPath | IPluginPromptPath;
export interface IPromptPathBase {
@@ -149,10 +148,6 @@ export interface IPluginPromptPath extends IPromptPathBase {
readonly pluginUri: URI;
}
export interface IInternalPromptPath extends IPromptPathBase {
readonly storage: PromptsStorage.internal;
}
export type IAgentSource = {
readonly storage: PromptsStorage.extension;
readonly extensionId: ExtensionIdentifier;
@@ -162,8 +157,6 @@ export type IAgentSource = {
} | {
readonly storage: PromptsStorage.plugin;
readonly pluginUri: URI;
} | {
readonly storage: PromptsStorage.internal;
};
/**

View File

@@ -51,7 +51,6 @@ import { ContextKeyExpr, IContextKeyService } from '../../../../../../platform/c
import { getCanonicalPluginCommandId, IAgentPlugin, IAgentPluginService } from '../../plugins/agentPluginService.js';
import { isContributionEnabled } from '../../enablement.js';
import { assertNever } from '../../../../../../base/common/assert.js';
import { ChatInternalCustomizations } from '../internalCustomizations/internalCustomizations.js';
/**
* Error thrown when a skill file is missing the required name attribute.
@@ -95,11 +94,6 @@ export class PromptsService extends Disposable implements IPromptsService {
*/
private readonly fileLocator: PromptFilesLocator;
/**
* Built-in internal customizations (skills, instructions, etc.).
*/
private readonly internalCustomizations: ChatInternalCustomizations;
/**
* Cached custom agents.
*/
@@ -195,9 +189,6 @@ export class PromptsService extends Disposable implements IPromptsService {
this.fileLocator = this.createPromptFilesLocator();
// Register the internal readonly customizations
this.internalCustomizations = this._register(new ChatInternalCustomizations(this.fileService));
this._register(this.modelService.onModelRemoved((model) => {
this.cachedParsedPromptFromModels.delete(model.uri);
}));
@@ -380,7 +371,7 @@ export class PromptsService extends Disposable implements IPromptsService {
this._pluginPromptFilesByType.get(type) ?? [],
]);
return [...prompts.flat(), ...this.internalCustomizations.getPromptPaths(type)];
return prompts.flat();
}
/**
@@ -518,8 +509,6 @@ export class PromptsService extends Disposable implements IPromptsService {
return this.fileLocator.listFiles(type, PromptsStorage.user, token).then(uris => uris.map(uri => ({ uri, storage: PromptsStorage.user, type } satisfies IUserPromptPath)));
case PromptsStorage.plugin:
return this._pluginPromptFilesByType.get(type) ?? [];
case PromptsStorage.internal:
return this.internalCustomizations.getPromptPaths(type);
default:
throw new Error(`[listPromptFilesForStorage] Unsupported prompt storage type: ${storage}`);
}
@@ -673,14 +662,13 @@ export class PromptsService extends Disposable implements IPromptsService {
private asChatPromptSlashCommand(parsedPromptFile: ParsedPromptFile, promptPath: IPromptPath): IChatPromptSlashCommand {
let name = parsedPromptFile?.header?.name ?? promptPath.name ?? getCleanPromptName(promptPath.uri);
name = name.replace(/[^\p{L}\d_\-\.:]+/gu, '-'); // replace spaces with dashes
const internalSkill = this.internalCustomizations.getInternalSkillByUri(promptPath.uri);
return {
name: name,
description: parsedPromptFile?.header?.description ?? promptPath.description,
argumentHint: parsedPromptFile?.header?.argumentHint,
parsedPromptFile,
promptPath,
when: internalSkill?.when,
when: undefined,
};
}
@@ -917,7 +905,6 @@ export class PromptsService extends Disposable implements IPromptsService {
return localize('extension.with.id', 'Extension: {0}', promptPath.extension.displayName ?? promptPath.extension.id);
}
case PromptsStorage.plugin: return localize('plugin.capitalized', 'Plugin');
case PromptsStorage.internal: return localize('internal.builtIn', 'Built-in');
default: assertNever(promptPath, 'Unknown prompt storage type');
}
}
@@ -1146,7 +1133,6 @@ export class PromptsService extends Disposable implements IPromptsService {
for (const file of files) {
if (file.status === 'loaded' && file.name) {
const sanitizedDescription = this.truncateAgentSkillDescription(file.description, file.uri);
const internalSkill = this.internalCustomizations.getInternalSkillByUri(file.uri);
result.push({
uri: file.uri,
storage: file.storage,
@@ -1154,7 +1140,7 @@ export class PromptsService extends Disposable implements IPromptsService {
description: sanitizedDescription,
disableModelInvocation: file.disableModelInvocation ?? false,
userInvocable: file.userInvocable ?? true,
when: internalSkill?.when,
when: undefined,
pluginUri: file.pluginUri,
extension: file.extension,
});
@@ -1446,12 +1432,7 @@ export class PromptsService extends Disposable implements IPromptsService {
storage: p.storage,
source: PromptFileSource.Plugin,
pluginUri: p.pluginUri,
})), ...this.internalCustomizations.getSkills()
.map(s => ({
fileUri: s.uri,
storage: s.storage,
source: PromptFileSource.Internal,
})));
})));
const getPriority = (skill: IResolvedPromptFile | IExtensionPromptPath): number => {
if (skill.storage === PromptsStorage.local) {
@@ -1833,10 +1814,6 @@ namespace IAgentSource {
storage: PromptsStorage.plugin,
pluginUri: promptPath.pluginUri
};
} else if (promptPath.storage === PromptsStorage.internal) {
return {
storage: PromptsStorage.internal
};
} else {
return {
storage: promptPath.storage

View File

@@ -1665,7 +1665,7 @@ suite('PromptsService', () => {
},
]);
const result = (await service.listPromptFiles(PromptsType.skill, CancellationToken.None)).filter(s => s.storage !== PromptsStorage.internal);
const result = await service.listPromptFiles(PromptsType.skill, CancellationToken.None);
assert.strictEqual(result.length, 2, 'Should find 2 skills');
@@ -1756,7 +1756,7 @@ suite('PromptsService', () => {
},
]);
const result = (await service.listPromptFiles(PromptsType.skill, CancellationToken.None)).filter(s => s.storage !== PromptsStorage.internal);
const result = await service.listPromptFiles(PromptsType.skill, CancellationToken.None);
assert.strictEqual(result.length, 0, 'Should not find any skills in non-skill locations');
});
@@ -1842,7 +1842,7 @@ suite('PromptsService', () => {
},
]);
const result = (await service.listPromptFiles(PromptsType.skill, CancellationToken.None)).filter(s => s.storage !== PromptsStorage.internal);
const result = await service.listPromptFiles(PromptsType.skill, CancellationToken.None);
assert.strictEqual(result.length, 1, 'Should find only 1 skill (from enabled folder)');
assert.ok(result[0].uri.path.includes('.claude/skills'), 'Should only find skill from .claude/skills');
@@ -1878,7 +1878,7 @@ suite('PromptsService', () => {
},
]);
const result = (await service.listPromptFiles(PromptsType.skill, CancellationToken.None)).filter(s => s.storage !== PromptsStorage.internal);
const result = await service.listPromptFiles(PromptsType.skill, CancellationToken.None);
assert.strictEqual(result.length, 1, 'Should find 1 skill from tilde-expanded path');
assert.ok(result[0].uri.path.includes('/home/user/my-custom-skills'), 'Path should be expanded from tilde');
@@ -2431,7 +2431,7 @@ suite('PromptsService', () => {
const allResult = await service.findAgentSkills(CancellationToken.None);
assert.ok(allResult, 'Should return results when agent skills are enabled');
const result = allResult.filter(s => s.storage !== PromptsStorage.internal);
const result = allResult;
assert.strictEqual(result.length, 5, 'Should find 5 skills total');
// Check project skills (both from .github/skills and .claude/skills)
@@ -2507,7 +2507,7 @@ suite('PromptsService', () => {
// Should return both skills - the malformed one uses folder name as fallback
assert.ok(allResult, 'Should return results even with parsing errors');
const result = allResult.filter(s => s.storage !== PromptsStorage.internal);
const result = allResult;
assert.strictEqual(result.length, 2, 'Should find 2 skills');
const validSkill = result.find(s => s.name === 'Valid Skill');
@@ -2534,7 +2534,7 @@ suite('PromptsService', () => {
const allResult = await service.findAgentSkills(CancellationToken.None);
assert.ok(allResult, 'Should return results array');
const result = allResult.filter(s => s.storage !== PromptsStorage.internal);
const result = allResult;
assert.strictEqual(result.length, 0, 'Should find no skills');
});
@@ -2569,7 +2569,7 @@ suite('PromptsService', () => {
const allResult = await service.findAgentSkills(CancellationToken.None);
assert.ok(allResult, 'Should return results');
const result = allResult.filter(s => s.storage !== PromptsStorage.internal);
const result = allResult;
assert.strictEqual(result.length, 1, 'Should find 1 skill');
assert.strictEqual(result[0].name.length, 64, 'Name should be truncated to 64 characters');
assert.strictEqual(result[0].description?.length, 1024, 'Description should be truncated to 1024 characters');
@@ -2602,7 +2602,7 @@ suite('PromptsService', () => {
const allResult = await service.findAgentSkills(CancellationToken.None);
assert.ok(allResult, 'Should return results');
const result = allResult.filter(s => s.storage !== PromptsStorage.internal);
const result = allResult;
assert.strictEqual(result.length, 1, 'Should find 1 skill');
assert.strictEqual(result[0].name, 'Skill with XML tags', 'XML tags should be removed from name');
assert.strictEqual(result[0].description, 'Description with HTML and other tags', 'XML tags should be removed from description');
@@ -2639,7 +2639,7 @@ suite('PromptsService', () => {
const allResult = await service.findAgentSkills(CancellationToken.None);
assert.ok(allResult, 'Should return results');
const result = allResult.filter(s => s.storage !== PromptsStorage.internal);
const result = allResult;
assert.strictEqual(result.length, 1, 'Should find 1 skill');
// XML tags are removed first, then truncation happens
assert.ok(!result[0].name.includes('<'), 'Name should not contain XML tags');
@@ -2698,7 +2698,7 @@ suite('PromptsService', () => {
const allResult = await service.findAgentSkills(CancellationToken.None);
assert.ok(allResult, 'Should return results');
const result = allResult.filter(s => s.storage !== PromptsStorage.internal);
const result = allResult;
assert.strictEqual(result.length, 2, 'Should find 2 skills (duplicate skipped)');
const duplicateSkill = result.find(s => s.name === 'Duplicate Skill');
@@ -2747,7 +2747,7 @@ suite('PromptsService', () => {
const allResult = await service.findAgentSkills(CancellationToken.None);
assert.ok(allResult, 'Should return results');
const result = allResult.filter(s => s.storage !== PromptsStorage.internal);
const result = allResult;
assert.strictEqual(result.length, 1, 'Should find 1 skill (duplicates resolved by priority)');
assert.strictEqual(result[0].description, 'Workspace version - highest priority', 'Workspace should win over user');
assert.strictEqual(result[0].storage, PromptsStorage.local);
@@ -2791,7 +2791,7 @@ suite('PromptsService', () => {
const allResult = await service.findAgentSkills(CancellationToken.None);
assert.ok(allResult, 'Should return results');
const result = allResult.filter(s => s.storage !== PromptsStorage.internal);
const result = allResult;
assert.strictEqual(result.length, 2, 'Should find both skills');
const mismatchedSkill = result.find(s => s.name === 'wrong-folder-name');
@@ -2837,7 +2837,7 @@ suite('PromptsService', () => {
const allResult = await service.findAgentSkills(CancellationToken.None);
assert.ok(allResult, 'Should return results');
const result = allResult.filter(s => s.storage !== PromptsStorage.internal);
const result = allResult;
assert.strictEqual(result.length, 2, 'Should find both skills');
const noNameSkill = result.find(s => s.name === 'no-name-skill');
@@ -2899,7 +2899,7 @@ suite('PromptsService', () => {
const allResult = await service.findAgentSkills(CancellationToken.None);
assert.ok(allResult, 'Should return results');
const result = allResult.filter(s => s.storage !== PromptsStorage.internal);
const result = allResult;
assert.strictEqual(result.length, 2, 'Should find 2 skills (workspace + extension)');
const workspaceSkill = result.find(s => s.name === 'Workspace Skill');
@@ -2962,7 +2962,7 @@ suite('PromptsService', () => {
const allResult = await service.findAgentSkills(CancellationToken.None);
assert.ok(allResult, 'Should return results');
const result = allResult.filter(s => s.storage !== PromptsStorage.internal);
const result = allResult;
assert.strictEqual(result.length, 2, 'Should find 2 skills (local + contributed)');
const localSkill = result.find(s => s.name === 'Local Skill');
@@ -2976,7 +2976,7 @@ suite('PromptsService', () => {
registered.dispose();
// After disposal, only local skill should remain
const resultAfterDispose = (await service.findAgentSkills(CancellationToken.None))?.filter(s => s.storage !== PromptsStorage.internal);
const resultAfterDispose = await service.findAgentSkills(CancellationToken.None);
assert.strictEqual(resultAfterDispose?.length, 1, 'Should find 1 skill after disposal');
assert.strictEqual(resultAfterDispose?.[0].name, 'Local Skill');
});
@@ -3650,7 +3650,7 @@ suite('PromptsService', () => {
},
]);
const slashCommands = (await service.getPromptSlashCommands(CancellationToken.None)).filter(c => c.promptPath.storage !== PromptsStorage.internal);
const slashCommands = await service.getPromptSlashCommands(CancellationToken.None);
// All commands should be present in the raw list
assert.strictEqual(slashCommands.length, 4, 'Should find all 4 commands');