-
Notifications
You must be signed in to change notification settings - Fork 52
Expand file tree
/
Copy pathskill-discover.ts
More file actions
115 lines (102 loc) · 4.3 KB
/
skill-discover.ts
File metadata and controls
115 lines (102 loc) · 4.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// altimate_change start — skill discovery tool for on-demand external skill scanning
import z from "zod"
import path from "path"
import { Tool } from "../../tool/tool"
import { discoverExternalSkills } from "../../skill/discover-external"
import { Instance } from "../../project/instance"
import { Skill } from "../../skill/skill"
export const SkillDiscoverTool = Tool.define("skill_discover", {
description:
"Discover skills and commands from external AI tool configs (Claude Code, Codex CLI, Gemini CLI). Lists what's available and which are already loaded.",
parameters: z.object({
action: z
.enum(["list", "add"])
.describe('"list" to show discovered skills, "add" to load them into the current session'),
skills: z
.array(z.string())
.optional()
.describe('When action is "add", which skills to load. Omit to add all discovered skills.'),
}),
async execute(args) {
const { skills: discovered, sources } = await discoverExternalSkills(Instance.worktree)
if (discovered.length === 0) {
return {
title: "Skill Discovery",
metadata: { action: args.action, found: 0 },
output:
"No external skills or commands found.\n\n" +
"Searched for:\n" +
"- .claude/commands/**/*.md (Claude Code commands)\n" +
"- .codex/skills/**/SKILL.md (Codex CLI skills)\n" +
"- .gemini/skills/**/SKILL.md (Gemini CLI skills)\n" +
"- .gemini/commands/**/*.toml (Gemini CLI commands)\n\n" +
"These are searched in both the project directory and home directory.",
}
}
// Get currently loaded skills to show which are new
const existing = await Skill.all()
const existingNames = new Set(existing.map((s) => s.name))
const newSkills = discovered.filter((s) => !existingNames.has(s.name))
const alreadyLoaded = discovered.filter((s) => existingNames.has(s.name))
if (args.action === "list") {
const lines: string[] = [
`Found ${discovered.length} external skill(s) from ${sources.join(", ")}:`,
"",
]
if (newSkills.length > 0) {
lines.push(`**New** (${newSkills.length}):`)
for (const s of newSkills) {
lines.push(`- \`${s.name}\` — ${s.description || "(no description)"} (${s.location})`)
}
lines.push("")
}
if (alreadyLoaded.length > 0) {
lines.push(`**Already loaded** (${alreadyLoaded.length}):`)
for (const s of alreadyLoaded) {
lines.push(`- \`${s.name}\``)
}
lines.push("")
}
if (newSkills.length > 0) {
lines.push(
'To add these skills to the current session, call this tool again with `action: "add"`.',
)
}
return {
title: `Skill Discovery: ${discovered.length} found (${newSkills.length} new)`,
metadata: { action: "list", found: discovered.length, new: newSkills.length },
output: lines.join("\n"),
}
}
// action === "add"
const toAdd = args.skills
? discovered.filter((s) => args.skills!.includes(s.name) && !existingNames.has(s.name))
: newSkills
if (toAdd.length === 0) {
return {
title: "Skill Discovery: nothing to add",
metadata: { action: "add", added: 0 },
output: "All discovered skills are already loaded in the current session.",
}
}
// Directly register skills into the runtime state — works regardless of
// auto_skill_discovery config setting. This is the on-demand path.
const currentState = await Skill.state()
for (const skill of toAdd) {
if (!currentState.skills[skill.name]) {
currentState.skills[skill.name] = skill
currentState.dirs.push(path.dirname(skill.location))
}
}
return {
title: `Skill Discovery: ${toAdd.length} skill(s) added`,
metadata: { action: "add", added: toAdd.length, names: toAdd.map((s) => s.name) },
output:
`Added ${toAdd.length} skill(s) to the current session:\n` +
toAdd.map((s) => `- \`/${s.name}\` — ${s.description || "(no description)"}`).join("\n") +
"\n\nTo enable auto-discovery at startup, set `experimental.auto_skill_discovery: true` in your config." +
"\n\nYou can now use these skills by name (e.g., `/" + toAdd[0].name + "`).",
}
},
})
// altimate_change end