Skip to content

Commit 0e4c71b

Browse files
committed
Add support for skills
1 parent 1e71015 commit 0e4c71b

File tree

9 files changed

+1089
-1
lines changed

9 files changed

+1089
-1
lines changed

src/core/prompts/sections/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ export { getToolUseGuidelinesSection } from "./tool-use-guidelines"
88
export { getCapabilitiesSection } from "./capabilities"
99
export { getModesSection } from "./modes"
1010
export { markdownFormattingSection } from "./markdown-formatting"
11+
export { getSkillsSection } from "./skills"
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { SkillsManager, SkillMetadata } from "../../../services/skills/SkillsManager"
2+
3+
/**
4+
* Generate the skills section for the system prompt.
5+
* Only includes skills relevant to the current mode.
6+
* Format matches the modes section style.
7+
*
8+
* @param skillsManager - The SkillsManager instance
9+
* @param currentMode - The current mode slug (e.g., 'code', 'architect')
10+
*/
11+
export async function getSkillsSection(
12+
skillsManager: SkillsManager | undefined,
13+
currentMode: string | undefined,
14+
): Promise<string> {
15+
if (!skillsManager || !currentMode) return ""
16+
17+
// Get skills filtered by current mode (with override resolution)
18+
const skills = skillsManager.getSkillsForMode(currentMode)
19+
if (skills.length === 0) return ""
20+
21+
// Separate generic and mode-specific skills for display
22+
const genericSkills = skills.filter((s) => !s.mode)
23+
const modeSpecificSkills = skills.filter((s) => s.mode === currentMode)
24+
25+
let skillsList = ""
26+
27+
if (modeSpecificSkills.length > 0) {
28+
skillsList += modeSpecificSkills
29+
.map((skill) => ` * "${skill.name}" skill (${currentMode} mode) - ${skill.description} [${skill.path}]`)
30+
.join("\n")
31+
}
32+
33+
if (genericSkills.length > 0) {
34+
if (skillsList) skillsList += "\n"
35+
skillsList += genericSkills
36+
.map((skill) => ` * "${skill.name}" skill - ${skill.description} [${skill.path}]`)
37+
.join("\n")
38+
}
39+
40+
return `====
41+
42+
AVAILABLE SKILLS
43+
44+
Skills are pre-packaged instructions for specific tasks. When a user request matches a skill description, read the full SKILL.md file to get detailed instructions.
45+
46+
- These are the currently available skills for "${currentMode}" mode:
47+
${skillsList}
48+
49+
To use a skill:
50+
1. Identify which skill matches the user's request based on the description
51+
2. Use read_file to load the full SKILL.md file from the path shown in brackets
52+
3. Follow the instructions in the skill file
53+
4. Access any bundled files (scripts, references, assets) as needed
54+
`
55+
}

src/core/prompts/system.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { isEmpty } from "../../utils/object"
1818

1919
import { McpHub } from "../../services/mcp/McpHub"
2020
import { CodeIndexManager } from "../../services/code-index/manager"
21+
import { SkillsManager } from "../../services/skills/SkillsManager"
2122

2223
import { PromptVariables, loadSystemPromptFile } from "./sections/custom-system-prompt"
2324

@@ -34,6 +35,7 @@ import {
3435
getModesSection,
3536
addCustomInstructions,
3637
markdownFormattingSection,
38+
getSkillsSection,
3739
} from "./sections"
3840

3941
// Helper function to get prompt component, filtering out empty objects
@@ -69,6 +71,7 @@ async function generatePrompt(
6971
settings?: SystemPromptSettings,
7072
todoList?: TodoItem[],
7173
modelId?: string,
74+
skillsManager?: SkillsManager,
7275
): Promise<string> {
7376
if (!context) {
7477
throw new Error("Extension context is required for generating system prompt")
@@ -91,7 +94,7 @@ async function generatePrompt(
9194
// Determine the effective protocol (defaults to 'xml')
9295
const effectiveProtocol = getEffectiveProtocol(settings?.toolProtocol)
9396

94-
const [modesSection, mcpServersSection] = await Promise.all([
97+
const [modesSection, mcpServersSection, skillsSection] = await Promise.all([
9598
getModesSection(context),
9699
shouldIncludeMcp
97100
? getMcpServersSection(
@@ -101,6 +104,7 @@ async function generatePrompt(
101104
!isNativeProtocol(effectiveProtocol),
102105
)
103106
: Promise.resolve(""),
107+
getSkillsSection(skillsManager, mode as string),
104108
])
105109

106110
// Build tools catalog section only for XML protocol
@@ -148,6 +152,8 @@ ${getCapabilitiesSection(cwd, shouldIncludeMcp ? mcpHub : undefined)}
148152
149153
${modesSection}
150154
155+
${skillsSection}
156+
151157
${getRulesSection(cwd, settings)}
152158
153159
${getSystemInfoSection(cwd)}
@@ -183,6 +189,7 @@ export const SYSTEM_PROMPT = async (
183189
settings?: SystemPromptSettings,
184190
todoList?: TodoItem[],
185191
modelId?: string,
192+
skillsManager?: SkillsManager,
186193
): Promise<string> => {
187194
if (!context) {
188195
throw new Error("Extension context is required for generating system prompt")
@@ -255,5 +262,6 @@ ${customInstructions}`
255262
settings,
256263
todoList,
257264
modelId,
265+
skillsManager,
258266
)
259267
}

src/core/task/Task.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3538,6 +3538,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
35383538
},
35393539
undefined, // todoList
35403540
this.api.getModel().id,
3541+
provider.getSkillsManager(),
35413542
)
35423543
})()
35433544
}

src/core/webview/ClineProvider.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import { ShadowCheckpointService } from "../../services/checkpoints/ShadowCheckp
7171
import { CodeIndexManager } from "../../services/code-index/manager"
7272
import type { IndexProgressUpdate } from "../../services/code-index/interfaces/manager"
7373
import { MdmService } from "../../services/mdm/MdmService"
74+
import { SkillsManager } from "../../services/skills/SkillsManager"
7475

7576
import { fileExistsAtPath } from "../../utils/fs"
7677
import { setTtsEnabled, setTtsSpeed } from "../../utils/tts"
@@ -137,6 +138,7 @@ export class ClineProvider
137138
private codeIndexManager?: CodeIndexManager
138139
private _workspaceTracker?: WorkspaceTracker // workSpaceTracker read-only for access outside this class
139140
protected mcpHub?: McpHub // Change from private to protected
141+
protected skillsManager?: SkillsManager
140142
private marketplaceManager: MarketplaceManager
141143
private mdmService?: MdmService
142144
private taskCreationCallback: (task: Task) => void
@@ -197,6 +199,12 @@ export class ClineProvider
197199
this.log(`Failed to initialize MCP Hub: ${error}`)
198200
})
199201

202+
// Initialize Skills Manager for skill discovery
203+
this.skillsManager = new SkillsManager(this)
204+
this.skillsManager.initialize().catch((error) => {
205+
this.log(`Failed to initialize Skills Manager: ${error}`)
206+
})
207+
200208
this.marketplaceManager = new MarketplaceManager(this.context, this.customModesManager)
201209

202210
// Forward <most> task events to the provider.
@@ -603,6 +611,8 @@ export class ClineProvider
603611
this._workspaceTracker = undefined
604612
await this.mcpHub?.unregisterClient()
605613
this.mcpHub = undefined
614+
await this.skillsManager?.dispose()
615+
this.skillsManager = undefined
606616
this.marketplaceManager?.cleanup()
607617
this.customModesManager?.dispose()
608618
this.log("Disposed all disposables")
@@ -2443,6 +2453,10 @@ export class ClineProvider
24432453
return this.mcpHub
24442454
}
24452455

2456+
public getSkillsManager(): SkillsManager | undefined {
2457+
return this.skillsManager
2458+
}
2459+
24462460
/**
24472461
* Check if the current state is compliant with MDM policy
24482462
* @returns true if compliant or no MDM policy exists, false if MDM policy exists and user is non-compliant

src/core/webview/generateSystemPrompt.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ export const generateSystemPrompt = async (provider: ClineProvider, message: Web
9999
toolProtocol,
100100
isStealthModel: modelInfo?.isStealthModel,
101101
},
102+
undefined, // todoList
103+
undefined, // modelId
104+
provider.getSkillsManager(),
102105
)
103106

104107
return systemPrompt

0 commit comments

Comments
 (0)