mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-17 15:24:40 +01:00
remove internal prompt files (#306009)
This commit is contained in:
committed by
GitHub
parent
c38a4a4a4b
commit
f4ca6f1926
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,6 @@ export enum PromptFileSource {
|
||||
ExtensionContribution = 'extension-contribution',
|
||||
ExtensionAPI = 'extension-api',
|
||||
Plugin = 'plugin',
|
||||
Internal = 'internal',
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 [];
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
Reference in New Issue
Block a user