Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/chatSessions/claude/node/claudeSkills.ts
13405 views
1
/*---------------------------------------------------------------------------------------------
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 type { Uri } from 'vscode';
7
import { IConfigurationService } from '../../../../platform/configuration/common/configurationService';
8
import { INativeEnvService } from '../../../../platform/env/common/envService';
9
import { IPromptsService } from '../../../../platform/promptFiles/common/promptsService';
10
import { IWorkspaceService } from '../../../../platform/workspace/common/workspaceService';
11
import { createServiceIdentifier } from '../../../../util/common/services';
12
import { CancellationToken } from '../../../../util/vs/base/common/cancellation';
13
import { Disposable } from '../../../../util/vs/base/common/lifecycle';
14
import { ResourceSet } from '../../../../util/vs/base/common/map';
15
import { Schemas } from '../../../../util/vs/base/common/network';
16
import { dirname } from '../../../../util/vs/base/common/resources';
17
import { URI } from '../../../../util/vs/base/common/uri';
18
import { resolveSkillConfigLocations } from '../../common/skillConfigLocations';
19
20
/** The Claude SDK loads `.claude` directories automatically — skip them to avoid duplicates. */
21
function isClaudeDirectory(uri: URI): boolean {
22
return uri.path.split('/').includes('.claude');
23
}
24
25
export interface IClaudePluginService {
26
readonly _serviceBrand: undefined;
27
/**
28
* Returns plugin root directories suitable for the Claude SDK's `plugins` option.
29
*
30
* Combines two sources:
31
* 1. **Skills** — discovered as directories containing `SKILL.md` files, but the Claude SDK
32
* plugin loader expects the *parent* of the `skills/` directory (the plugin root),
33
* so we walk one level up from each skill location.
34
* 2. **Plugins** — returned directly by the prompts service as actual plugin root directories.
35
*/
36
getPluginLocations(token: CancellationToken): Promise<Uri[]>;
37
}
38
39
export const IClaudePluginService = createServiceIdentifier<IClaudePluginService>('IClaudePluginService');
40
41
export class ClaudePluginService extends Disposable implements IClaudePluginService {
42
declare _serviceBrand: undefined;
43
44
constructor(
45
@IConfigurationService private readonly configurationService: IConfigurationService,
46
@INativeEnvService private readonly envService: INativeEnvService,
47
@IWorkspaceService private readonly workspaceService: IWorkspaceService,
48
@IPromptsService private readonly promptsService: IPromptsService,
49
) {
50
super();
51
}
52
53
async getPluginLocations(token: CancellationToken): Promise<Uri[]> {
54
const pluginRoots = new ResourceSet();
55
56
// #region Skills as plugin roots
57
// Skill locations point to directories containing skill subdirectories (e.g. .../skills/).
58
// The Claude SDK plugin loader expects the parent of the skills/ directory, so we
59
// walk one level up from each location.
60
for (const uri of resolveSkillConfigLocations(this.configurationService, this.envService, this.workspaceService)) {
61
pluginRoots.add(dirname(uri));
62
}
63
64
(await this.promptsService.getSkills(token))
65
.filter(s => s.uri.scheme === Schemas.file)
66
.map(s => s.uri)
67
.map(uri => dirname(dirname(dirname(uri))))
68
.filter(uri => !isClaudeDirectory(uri))
69
.forEach(uri => pluginRoots.add(uri));
70
// #endregion
71
72
// #region Plugin roots from prompts service
73
(await this.promptsService.getPlugins(token))
74
.filter(p => p.uri.scheme === Schemas.file)
75
.filter(p => !isClaudeDirectory(p.uri))
76
.map(p => p.uri)
77
.forEach(uri => pluginRoots.add(uri));
78
// #endregion
79
80
return Array.from(pluginRoots);
81
}
82
}
83
84