@downcity/plugins 1.0.52 → 1.0.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/BuiltinPlugins.d.ts +2 -3
- package/bin/BuiltinPlugins.d.ts.map +1 -1
- package/bin/BuiltinPlugins.js +2 -2
- package/bin/BuiltinPlugins.js.map +1 -1
- package/bin/asr/Plugin.d.ts +1 -2
- package/bin/asr/Plugin.d.ts.map +1 -1
- package/bin/asr/Plugin.js +2 -2
- package/bin/asr/Plugin.js.map +1 -1
- package/bin/auth/Plugin.d.ts +1 -2
- package/bin/auth/Plugin.d.ts.map +1 -1
- package/bin/auth/Plugin.js +2 -2
- package/bin/auth/Plugin.js.map +1 -1
- package/bin/chat/ChatPlugin.d.ts +1 -2
- package/bin/chat/ChatPlugin.d.ts.map +1 -1
- package/bin/chat/ChatPlugin.js +3 -12
- package/bin/chat/ChatPlugin.js.map +1 -1
- package/bin/chat/PROMPT.direct.d.ts +1 -1
- package/bin/chat/PROMPT.direct.d.ts.map +1 -1
- package/bin/chat/PROMPT.direct.js +1 -1
- package/bin/chat/PROMPT.direct.js.map +1 -1
- package/bin/chat/channels/qq/PROMPT.direct.d.ts +1 -1
- package/bin/chat/channels/qq/PROMPT.direct.d.ts.map +1 -1
- package/bin/chat/channels/qq/PROMPT.direct.js +1 -1
- package/bin/chat/channels/qq/PROMPT.direct.js.map +1 -1
- package/bin/chat/channels/telegram/Bot.d.ts +6 -0
- package/bin/chat/channels/telegram/Bot.d.ts.map +1 -1
- package/bin/chat/channels/telegram/Bot.js +24 -0
- package/bin/chat/channels/telegram/Bot.js.map +1 -1
- package/bin/chat/channels/telegram/TelegramPlatformClient.d.ts +14 -0
- package/bin/chat/channels/telegram/TelegramPlatformClient.d.ts.map +1 -1
- package/bin/chat/channels/telegram/TelegramPlatformClient.js +39 -5
- package/bin/chat/channels/telegram/TelegramPlatformClient.js.map +1 -1
- package/bin/contact/ContactPlugin.d.ts +1 -2
- package/bin/contact/ContactPlugin.d.ts.map +1 -1
- package/bin/contact/ContactPlugin.js +2 -2
- package/bin/contact/ContactPlugin.js.map +1 -1
- package/bin/contact/PROMPT.d.ts +1 -1
- package/bin/contact/PROMPT.d.ts.map +1 -1
- package/bin/contact/PROMPT.js +1 -1
- package/bin/contact/PROMPT.js.map +1 -1
- package/bin/image/ImagePlugin.d.ts +61 -0
- package/bin/image/ImagePlugin.d.ts.map +1 -0
- package/bin/image/ImagePlugin.js +189 -0
- package/bin/image/ImagePlugin.js.map +1 -0
- package/bin/image/types/ImagePlugin.d.ts +136 -0
- package/bin/image/types/ImagePlugin.d.ts.map +1 -0
- package/bin/image/types/ImagePlugin.js +10 -0
- package/bin/image/types/ImagePlugin.js.map +1 -0
- package/bin/index.d.ts +2 -0
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js +1 -0
- package/bin/index.js.map +1 -1
- package/bin/shell/ShellPlugin.d.ts +1 -2
- package/bin/shell/ShellPlugin.d.ts.map +1 -1
- package/bin/shell/ShellPlugin.js +2 -2
- package/bin/shell/ShellPlugin.js.map +1 -1
- package/bin/skill/Action.d.ts +8 -3
- package/bin/skill/Action.d.ts.map +1 -1
- package/bin/skill/Action.js +9 -11
- package/bin/skill/Action.js.map +1 -1
- package/bin/skill/Command.d.ts +1 -1
- package/bin/skill/Command.d.ts.map +1 -1
- package/bin/skill/Command.js +3 -10
- package/bin/skill/Command.js.map +1 -1
- package/bin/skill/Config.d.ts +9 -9
- package/bin/skill/Config.d.ts.map +1 -1
- package/bin/skill/Config.js +36 -24
- package/bin/skill/Config.js.map +1 -1
- package/bin/skill/PROMPT.d.ts +1 -1
- package/bin/skill/PROMPT.d.ts.map +1 -1
- package/bin/skill/PROMPT.js +1 -1
- package/bin/skill/PROMPT.js.map +1 -1
- package/bin/skill/Plugin.d.ts +2 -2
- package/bin/skill/Plugin.d.ts.map +1 -1
- package/bin/skill/Plugin.js +20 -37
- package/bin/skill/Plugin.js.map +1 -1
- package/bin/skill/runtime/Discovery.d.ts +3 -3
- package/bin/skill/runtime/Discovery.d.ts.map +1 -1
- package/bin/skill/runtime/Discovery.js +37 -14
- package/bin/skill/runtime/Discovery.js.map +1 -1
- package/bin/skill/runtime/Paths.d.ts +5 -5
- package/bin/skill/runtime/Paths.d.ts.map +1 -1
- package/bin/skill/runtime/Paths.js +23 -33
- package/bin/skill/runtime/Paths.js.map +1 -1
- package/bin/skill/runtime/Prompt.d.ts +2 -2
- package/bin/skill/runtime/Prompt.d.ts.map +1 -1
- package/bin/skill/runtime/Prompt.js +3 -8
- package/bin/skill/runtime/Prompt.js.map +1 -1
- package/bin/skill/runtime/SystemProvider.d.ts +2 -2
- package/bin/skill/runtime/SystemProvider.d.ts.map +1 -1
- package/bin/skill/runtime/SystemProvider.js +2 -2
- package/bin/skill/runtime/SystemProvider.js.map +1 -1
- package/bin/skill/types/SkillPlugin.d.ts +34 -12
- package/bin/skill/types/SkillPlugin.d.ts.map +1 -1
- package/bin/skill/types/SkillPlugin.js +2 -1
- package/bin/skill/types/SkillPlugin.js.map +1 -1
- package/bin/skill/types/SkillRoot.d.ts +1 -1
- package/bin/task/Action.js +2 -2
- package/bin/task/Action.js.map +1 -1
- package/bin/task/PROMPT.d.ts +1 -1
- package/bin/task/PROMPT.d.ts.map +1 -1
- package/bin/task/PROMPT.js +1 -1
- package/bin/task/PROMPT.js.map +1 -1
- package/bin/task/TaskPlugin.d.ts +1 -2
- package/bin/task/TaskPlugin.d.ts.map +1 -1
- package/bin/task/TaskPlugin.js +2 -2
- package/bin/task/TaskPlugin.js.map +1 -1
- package/bin/tts/Plugin.d.ts +1 -2
- package/bin/tts/Plugin.d.ts.map +1 -1
- package/bin/tts/Plugin.js +6 -6
- package/bin/tts/Plugin.js.map +1 -1
- package/bin/web/PROMPT.agent-browser.d.ts +1 -1
- package/bin/web/PROMPT.agent-browser.d.ts.map +1 -1
- package/bin/web/PROMPT.agent-browser.js +1 -1
- package/bin/web/PROMPT.agent-browser.js.map +1 -1
- package/bin/web/Plugin.d.ts +1 -2
- package/bin/web/Plugin.d.ts.map +1 -1
- package/bin/web/Plugin.js +10 -3
- package/bin/web/Plugin.js.map +1 -1
- package/bin/workboard/Plugin.d.ts +1 -2
- package/bin/workboard/Plugin.d.ts.map +1 -1
- package/bin/workboard/Plugin.js +2 -2
- package/bin/workboard/Plugin.js.map +1 -1
- package/package.json +2 -2
- package/src/BuiltinPlugins.ts +3 -6
- package/src/asr/Plugin.ts +2 -3
- package/src/auth/Plugin.ts +2 -3
- package/src/chat/ChatPlugin.ts +3 -14
- package/src/chat/PROMPT.direct.ts +1 -1
- package/src/chat/PROMPT.direct.ts.txt +5 -7
- package/src/chat/channels/qq/PROMPT.direct.ts +1 -1
- package/src/chat/channels/qq/PROMPT.direct.ts.txt +1 -1
- package/src/chat/channels/telegram/Bot.ts +22 -0
- package/src/chat/channels/telegram/TelegramPlatformClient.ts +42 -6
- package/src/contact/ContactPlugin.ts +2 -3
- package/src/contact/PROMPT.ts +1 -1
- package/src/contact/PROMPT.ts.txt +12 -12
- package/src/image/ImagePlugin.ts +250 -0
- package/src/image/types/ImagePlugin.ts +157 -0
- package/src/index.ts +13 -0
- package/src/shell/ShellPlugin.ts +2 -3
- package/src/skill/Action.ts +20 -9
- package/src/skill/Command.ts +3 -13
- package/src/skill/Config.ts +41 -34
- package/src/skill/PROMPT.ts +1 -1
- package/src/skill/PROMPT.ts.txt +14 -2
- package/src/skill/Plugin.ts +205 -214
- package/src/skill/runtime/Discovery.ts +43 -17
- package/src/skill/runtime/Paths.ts +24 -35
- package/src/skill/runtime/Prompt.ts +4 -10
- package/src/skill/runtime/SystemProvider.ts +4 -4
- package/src/skill/types/SkillPlugin.ts +39 -12
- package/src/skill/types/SkillRoot.ts +1 -1
- package/src/task/Action.ts +2 -2
- package/src/task/PROMPT.ts +1 -1
- package/src/task/PROMPT.ts.txt +10 -10
- package/src/task/TaskPlugin.ts +2 -3
- package/src/tts/Plugin.ts +6 -7
- package/src/web/PROMPT.agent-browser.ts +1 -1
- package/src/web/PROMPT.agent-browser.ts.txt +1 -1
- package/src/web/Plugin.ts +3 -4
- package/src/workboard/Plugin.ts +2 -3
package/src/skill/PROMPT.ts.txt
CHANGED
|
@@ -5,6 +5,18 @@ skill 是你拥有的技能。用户的提出的需求和任务,你都需要
|
|
|
5
5
|
|
|
6
6
|
## 使用
|
|
7
7
|
|
|
8
|
-
使用技能时需要首先通过 `
|
|
8
|
+
使用技能时需要首先通过 `skill` plugin 的 `lookup` action 载入对应的技能内容。
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
如果当前工具集中存在 `plugin_call`,使用:
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
plugin_call({
|
|
14
|
+
plugin: "skill",
|
|
15
|
+
action: "lookup",
|
|
16
|
+
payload: {
|
|
17
|
+
name: "<skill-name>",
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
可用 action 包括 `list`、`find`、`install`、`lookup`。
|
package/src/skill/Plugin.ts
CHANGED
|
@@ -7,21 +7,17 @@
|
|
|
7
7
|
* - skills overview 文本通过 `plugin.system` 注入,不再依赖 plugin.system。
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type { AgentRuntime } from "@downcity/agent/internal/types/runtime/agent/AgentRuntime.js";
|
|
11
10
|
import { BasePlugin } from "@downcity/agent/internal/plugin/core/BasePlugin.js";
|
|
12
11
|
import type { Plugin } from "@downcity/agent/internal/plugin/types/Plugin.js";
|
|
13
12
|
import type { JsonObject, JsonValue } from "@downcity/agent/internal/types/common/Json.js";
|
|
14
|
-
import { isPluginEnabled } from "@downcity/agent/internal/plugin/core/Activation.js";
|
|
15
13
|
import type {
|
|
16
14
|
SkillPluginFindPayload,
|
|
17
15
|
SkillPluginInstallPayload,
|
|
18
16
|
SkillPluginLookupPayload,
|
|
17
|
+
SkillPluginOptions,
|
|
19
18
|
} from "@/skill/types/SkillPlugin.js";
|
|
20
19
|
import { SKILL_PLUGIN_ACTIONS } from "@/skill/types/SkillPlugin.js";
|
|
21
|
-
import {
|
|
22
|
-
DEFAULT_SKILL_PLUGIN_CONFIG,
|
|
23
|
-
readSkillPluginConfig,
|
|
24
|
-
} from "@/skill/Config.js";
|
|
20
|
+
import { resolveSkillPluginOptions } from "@/skill/Config.js";
|
|
25
21
|
import { skillFindCommand, skillInstallCommand } from "@/skill/Command.js";
|
|
26
22
|
import {
|
|
27
23
|
findLearnedSkillExact,
|
|
@@ -92,245 +88,239 @@ function inferSkillQueryFromSpec(spec: string): string {
|
|
|
92
88
|
return "";
|
|
93
89
|
}
|
|
94
90
|
|
|
95
|
-
function createSkillPluginDefinition(
|
|
91
|
+
function createSkillPluginDefinition(options: SkillPluginOptions): Plugin {
|
|
96
92
|
return {
|
|
97
93
|
name: "skill",
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
94
|
+
title: "Skill Catalog And Loader",
|
|
95
|
+
description:
|
|
96
|
+
"Finds, installs, lists, and reads local skills, and injects the current skill overview into system prompts so the agent knows what capabilities are available.",
|
|
97
|
+
async system(context) {
|
|
98
|
+
const dynamicText = String(
|
|
99
|
+
await buildSkillsSystemText({
|
|
100
|
+
rootPath: context.rootPath,
|
|
101
|
+
options,
|
|
102
|
+
}),
|
|
103
|
+
).trim();
|
|
104
|
+
return [SKILL_PLUGIN_PROMPT, dynamicText].filter(Boolean).join("\n\n");
|
|
106
105
|
},
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
reasons: [],
|
|
120
|
-
};
|
|
121
|
-
},
|
|
122
|
-
async system(context) {
|
|
123
|
-
const dynamicText = String(await buildSkillsSystemText(context)).trim();
|
|
124
|
-
return [SKILL_PLUGIN_PROMPT, dynamicText].filter(Boolean).join("\n\n");
|
|
125
|
-
},
|
|
126
|
-
actions: {
|
|
127
|
-
[SKILL_PLUGIN_ACTIONS.find]: {
|
|
128
|
-
command: {
|
|
129
|
-
description: "查找 `list` 中不存在的未学会 skills(缺失时再 install)",
|
|
130
|
-
configure(command) {
|
|
131
|
-
command.argument("<query>");
|
|
132
|
-
},
|
|
133
|
-
mapInput({ args }): SkillPluginFindPayload {
|
|
134
|
-
const query = String(args[0] || "").trim();
|
|
135
|
-
if (!query) throw new Error("Missing query");
|
|
136
|
-
return { query };
|
|
106
|
+
actions: {
|
|
107
|
+
[SKILL_PLUGIN_ACTIONS.find]: {
|
|
108
|
+
command: {
|
|
109
|
+
description: "查找 `list` 中不存在的未学会 skills(缺失时再 install)",
|
|
110
|
+
configure(command) {
|
|
111
|
+
command.argument("<query>");
|
|
112
|
+
},
|
|
113
|
+
mapInput({ args }): SkillPluginFindPayload {
|
|
114
|
+
const query = String(args[0] || "").trim();
|
|
115
|
+
if (!query) throw new Error("Missing query");
|
|
116
|
+
return { query };
|
|
117
|
+
},
|
|
137
118
|
},
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
119
|
+
async execute(params) {
|
|
120
|
+
const payload = params.payload as SkillPluginFindPayload;
|
|
121
|
+
const rootPath = params.context.rootPath;
|
|
122
|
+
const exactLearned = findLearnedSkillExact(
|
|
123
|
+
rootPath,
|
|
124
|
+
payload.query,
|
|
125
|
+
options,
|
|
126
|
+
);
|
|
127
|
+
if (exactLearned) {
|
|
128
|
+
return {
|
|
129
|
+
success: true,
|
|
130
|
+
data: {
|
|
131
|
+
query: payload.query,
|
|
132
|
+
message: "该技能已在 list 中,无需 install。使用前请先执行 lookup。",
|
|
133
|
+
workflow: ["list", "lookup"],
|
|
134
|
+
nextAction: "lookup",
|
|
135
|
+
learnedSkill: exactLearned,
|
|
136
|
+
learnedHints: [],
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const learnedHints = searchLearnedSkills(
|
|
142
|
+
rootPath,
|
|
143
|
+
payload.query,
|
|
144
|
+
5,
|
|
145
|
+
options,
|
|
146
|
+
);
|
|
147
|
+
await skillFindCommand(payload.query);
|
|
144
148
|
return {
|
|
145
149
|
success: true,
|
|
146
150
|
data: {
|
|
147
151
|
query: payload.query,
|
|
148
|
-
message: "
|
|
149
|
-
workflow: ["
|
|
150
|
-
nextAction: "
|
|
151
|
-
learnedSkill:
|
|
152
|
-
learnedHints
|
|
152
|
+
message: "已执行缺失技能检索;若目标不在 list 中,可 install 后再 lookup。",
|
|
153
|
+
workflow: ["find", "install", "lookup"],
|
|
154
|
+
nextAction: "install",
|
|
155
|
+
learnedSkill: null,
|
|
156
|
+
learnedHints,
|
|
153
157
|
},
|
|
154
158
|
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const learnedHints = searchLearnedSkills(rootPath, payload.query, 5);
|
|
158
|
-
await skillFindCommand(payload.query);
|
|
159
|
-
return {
|
|
160
|
-
success: true,
|
|
161
|
-
data: {
|
|
162
|
-
query: payload.query,
|
|
163
|
-
message: "已执行缺失技能检索;若目标不在 list 中,可 install 后再 lookup。",
|
|
164
|
-
workflow: ["find", "install", "lookup"],
|
|
165
|
-
nextAction: "install",
|
|
166
|
-
learnedSkill: null,
|
|
167
|
-
learnedHints,
|
|
168
|
-
},
|
|
169
|
-
};
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
[SKILL_PLUGIN_ACTIONS.install]: {
|
|
173
|
-
command: {
|
|
174
|
-
description: "安装 `list` 中不存在的 skill(完成后请先 lookup)",
|
|
175
|
-
configure(command) {
|
|
176
|
-
command
|
|
177
|
-
.argument("<spec>")
|
|
178
|
-
.option("-g, --global", "全局安装(默认 true)", true)
|
|
179
|
-
.option("-y, --yes", "跳过确认(默认 true)", true)
|
|
180
|
-
.option("--agent <agent>", "指定 agent", "claude-code");
|
|
181
|
-
},
|
|
182
|
-
mapInput({ args, opts }): SkillPluginInstallPayload {
|
|
183
|
-
const spec = String(args[0] || "").trim();
|
|
184
|
-
if (!spec) throw new Error("Missing spec");
|
|
185
|
-
return {
|
|
186
|
-
spec,
|
|
187
|
-
global: getBooleanOpt(opts, "global"),
|
|
188
|
-
yes: getBooleanOpt(opts, "yes"),
|
|
189
|
-
agent: getStringOpt(opts, "agent"),
|
|
190
|
-
};
|
|
191
159
|
},
|
|
192
160
|
},
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
161
|
+
[SKILL_PLUGIN_ACTIONS.install]: {
|
|
162
|
+
command: {
|
|
163
|
+
description: "安装 `list` 中不存在的 skill(完成后请先 lookup)",
|
|
164
|
+
configure(command) {
|
|
165
|
+
command
|
|
166
|
+
.argument("<spec>")
|
|
167
|
+
.option("-g, --global", "全局安装(默认 true)", true)
|
|
168
|
+
.option("-y, --yes", "跳过确认(默认 true)", true)
|
|
169
|
+
.option("--agent <agent>", "指定 agent", "claude-code");
|
|
170
|
+
},
|
|
171
|
+
mapInput({ args, opts }): SkillPluginInstallPayload {
|
|
172
|
+
const spec = String(args[0] || "").trim();
|
|
173
|
+
if (!spec) throw new Error("Missing spec");
|
|
174
|
+
return {
|
|
175
|
+
spec,
|
|
176
|
+
global: getBooleanOpt(opts, "global"),
|
|
177
|
+
yes: getBooleanOpt(opts, "yes"),
|
|
178
|
+
agent: getStringOpt(opts, "agent"),
|
|
179
|
+
};
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
async execute(params) {
|
|
183
|
+
const payload = params.payload as SkillPluginInstallPayload;
|
|
184
|
+
const rootPath = params.context.rootPath;
|
|
185
|
+
const queryFromSpec = inferSkillQueryFromSpec(payload.spec);
|
|
186
|
+
const beforeList = listSkills(rootPath, options).skills;
|
|
187
|
+
const beforeIds = new Set(beforeList.map((item) => item.id));
|
|
188
|
+
|
|
189
|
+
const learnedBefore =
|
|
190
|
+
findLearnedSkillExact(rootPath, queryFromSpec, options) ||
|
|
191
|
+
findLearnedSkillExact(rootPath, payload.spec, options);
|
|
192
|
+
if (learnedBefore) {
|
|
193
|
+
return {
|
|
194
|
+
success: true,
|
|
195
|
+
data: {
|
|
196
|
+
spec: payload.spec,
|
|
197
|
+
skipped: true,
|
|
198
|
+
message: "技能已在 list 中,无需 install。使用前请先执行 lookup。",
|
|
199
|
+
workflow: ["list", "lookup"],
|
|
200
|
+
nextAction: "lookup",
|
|
201
|
+
queryFromSpec,
|
|
202
|
+
addedSkills: [],
|
|
203
|
+
learnedSkill: learnedBefore,
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
await skillInstallCommand(payload.spec, {
|
|
209
|
+
global: payload.global,
|
|
210
|
+
yes: payload.yes,
|
|
211
|
+
agent: payload.agent,
|
|
212
|
+
});
|
|
213
|
+
const afterList = listSkills(rootPath, options).skills;
|
|
214
|
+
const addedSkills = afterList.filter((item) => !beforeIds.has(item.id));
|
|
215
|
+
const learnedAfter =
|
|
216
|
+
findLearnedSkillExact(rootPath, queryFromSpec, options) ||
|
|
217
|
+
findLearnedSkillExact(rootPath, payload.spec, options) ||
|
|
218
|
+
(addedSkills.length === 1 ? addedSkills[0] : undefined);
|
|
199
219
|
|
|
200
|
-
const learnedBefore =
|
|
201
|
-
findLearnedSkillExact(rootPath, queryFromSpec) ||
|
|
202
|
-
findLearnedSkillExact(rootPath, payload.spec);
|
|
203
|
-
if (learnedBefore) {
|
|
204
220
|
return {
|
|
205
221
|
success: true,
|
|
206
222
|
data: {
|
|
207
223
|
spec: payload.spec,
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
workflow: ["list", "lookup"],
|
|
224
|
+
message: "技能学习完成。使用技能前请先执行 lookup 读取该技能的 SKILL.md 内容。",
|
|
225
|
+
workflow: ["find", "install", "lookup"],
|
|
211
226
|
nextAction: "lookup",
|
|
227
|
+
skipped: false,
|
|
212
228
|
queryFromSpec,
|
|
213
|
-
addedSkills
|
|
214
|
-
learnedSkill:
|
|
229
|
+
addedSkills,
|
|
230
|
+
learnedSkill: learnedAfter || null,
|
|
215
231
|
},
|
|
216
232
|
};
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
await skillInstallCommand(payload.spec, {
|
|
220
|
-
global: payload.global,
|
|
221
|
-
yes: payload.yes,
|
|
222
|
-
agent: payload.agent,
|
|
223
|
-
});
|
|
224
|
-
const afterList = listSkills(rootPath).skills;
|
|
225
|
-
const addedSkills = afterList.filter((item) => !beforeIds.has(item.id));
|
|
226
|
-
const learnedAfter =
|
|
227
|
-
findLearnedSkillExact(rootPath, queryFromSpec) ||
|
|
228
|
-
findLearnedSkillExact(rootPath, payload.spec) ||
|
|
229
|
-
(addedSkills.length === 1 ? addedSkills[0] : undefined);
|
|
230
|
-
|
|
231
|
-
return {
|
|
232
|
-
success: true,
|
|
233
|
-
data: {
|
|
234
|
-
spec: payload.spec,
|
|
235
|
-
message: "技能学习完成。使用技能前请先执行 lookup 读取该技能的 SKILL.md 内容。",
|
|
236
|
-
workflow: ["find", "install", "lookup"],
|
|
237
|
-
nextAction: "lookup",
|
|
238
|
-
skipped: false,
|
|
239
|
-
queryFromSpec,
|
|
240
|
-
addedSkills,
|
|
241
|
-
learnedSkill: learnedAfter || null,
|
|
242
|
-
},
|
|
243
|
-
};
|
|
244
|
-
},
|
|
245
|
-
},
|
|
246
|
-
[SKILL_PLUGIN_ACTIONS.list]: {
|
|
247
|
-
command: {
|
|
248
|
-
description: "列出当前已学会(本地可发现)的 skills",
|
|
249
|
-
mapInput() {
|
|
250
|
-
return {};
|
|
251
233
|
},
|
|
252
234
|
},
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
data: listSkills(params.context.rootPath),
|
|
260
|
-
};
|
|
261
|
-
},
|
|
262
|
-
},
|
|
263
|
-
[SKILL_PLUGIN_ACTIONS.lookup]: {
|
|
264
|
-
command: {
|
|
265
|
-
description: "读取已学会 skill 内容(SKILL.md)",
|
|
266
|
-
configure(command) {
|
|
267
|
-
command.argument("<name>");
|
|
235
|
+
[SKILL_PLUGIN_ACTIONS.list]: {
|
|
236
|
+
command: {
|
|
237
|
+
description: "列出当前已学会(本地可发现)的 skills",
|
|
238
|
+
mapInput() {
|
|
239
|
+
return {};
|
|
240
|
+
},
|
|
268
241
|
},
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (!name) throw new Error("Missing name");
|
|
272
|
-
return { name };
|
|
242
|
+
api: {
|
|
243
|
+
method: "GET",
|
|
273
244
|
},
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
const name = String(body.name || "").trim();
|
|
280
|
-
if (!name) throw new Error("Missing name");
|
|
281
|
-
return { name };
|
|
245
|
+
execute(params) {
|
|
246
|
+
return {
|
|
247
|
+
success: true,
|
|
248
|
+
data: listSkills(params.context.rootPath, options),
|
|
249
|
+
};
|
|
282
250
|
},
|
|
283
251
|
},
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
name: payload.name,
|
|
252
|
+
[SKILL_PLUGIN_ACTIONS.lookup]: {
|
|
253
|
+
command: {
|
|
254
|
+
description: "读取已学会 skill 内容(SKILL.md)",
|
|
255
|
+
configure(command) {
|
|
256
|
+
command.argument("<name>");
|
|
290
257
|
},
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
258
|
+
mapInput({ args }): SkillPluginLookupPayload {
|
|
259
|
+
const name = String(args[0] || "").trim();
|
|
260
|
+
if (!name) throw new Error("Missing name");
|
|
261
|
+
return { name };
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
api: {
|
|
265
|
+
method: "POST",
|
|
266
|
+
async mapInput(c): Promise<SkillPluginLookupPayload> {
|
|
267
|
+
const body = readJsonObject(await c.req.json());
|
|
268
|
+
const name = String(body.name || "").trim();
|
|
269
|
+
if (!name) throw new Error("Missing name");
|
|
270
|
+
return { name };
|
|
271
|
+
},
|
|
272
|
+
},
|
|
273
|
+
async execute(params) {
|
|
274
|
+
const payload = params.payload as SkillPluginLookupPayload;
|
|
275
|
+
const result = await lookupSkill({
|
|
276
|
+
projectRoot: params.context.rootPath,
|
|
277
|
+
request: {
|
|
278
|
+
name: payload.name,
|
|
279
|
+
},
|
|
280
|
+
options,
|
|
281
|
+
});
|
|
282
|
+
if (!result.success) {
|
|
283
|
+
return {
|
|
284
|
+
success: false,
|
|
285
|
+
error: result.error || "skill lookup failed",
|
|
286
|
+
};
|
|
287
|
+
}
|
|
298
288
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
289
|
+
const skillName = String(result.skill?.name || result.skill?.id || "").trim();
|
|
290
|
+
const openingTag = skillName
|
|
291
|
+
? `<skill name="${sanitizeXmlAttr(skillName)}">`
|
|
292
|
+
: "<skill>";
|
|
293
|
+
const skillUserMessage = [
|
|
294
|
+
openingTag,
|
|
295
|
+
String(result.content || "").trim(),
|
|
296
|
+
"</skill>",
|
|
297
|
+
]
|
|
298
|
+
.filter(Boolean)
|
|
299
|
+
.join("\n")
|
|
300
|
+
.trim();
|
|
311
301
|
|
|
312
|
-
|
|
313
|
-
success: true,
|
|
314
|
-
data: {
|
|
302
|
+
return {
|
|
315
303
|
success: true,
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
304
|
+
data: {
|
|
305
|
+
success: true,
|
|
306
|
+
...(result.skill ? { skill: result.skill } : {}),
|
|
307
|
+
message: "技能内容已准备,下一步将以 `<skill>...</skill>` user message 注入。",
|
|
308
|
+
__ship: {
|
|
309
|
+
injectUserMessages: [
|
|
310
|
+
{
|
|
311
|
+
text: skillUserMessage,
|
|
312
|
+
note: "skill_lookup",
|
|
313
|
+
},
|
|
314
|
+
],
|
|
315
|
+
suppressToolOutput: true,
|
|
316
|
+
toolOutputMessage:
|
|
317
|
+
"skill lookup success; content injected as <skill> user message.",
|
|
318
|
+
},
|
|
328
319
|
},
|
|
329
|
-
}
|
|
330
|
-
}
|
|
320
|
+
};
|
|
321
|
+
},
|
|
331
322
|
},
|
|
332
323
|
},
|
|
333
|
-
},
|
|
334
324
|
};
|
|
335
325
|
}
|
|
336
326
|
|
|
@@ -340,8 +330,9 @@ function createSkillPluginDefinition(plugin: Plugin): Plugin {
|
|
|
340
330
|
export class SkillPlugin extends BasePlugin {
|
|
341
331
|
readonly name = "skill";
|
|
342
332
|
|
|
343
|
-
constructor(
|
|
344
|
-
super(
|
|
345
|
-
|
|
333
|
+
constructor(options: SkillPluginOptions = {}) {
|
|
334
|
+
super();
|
|
335
|
+
const resolvedOptions = resolveSkillPluginOptions(options);
|
|
336
|
+
Object.assign(this, createSkillPluginDefinition(resolvedOptions));
|
|
346
337
|
}
|
|
347
338
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Skills discovery:扫描可用 skills 并生成索引。
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
|
-
* - 扫描范围由 `paths.ts`
|
|
5
|
+
* - 扫描范围由 `paths.ts` 决定(项目、用户目录、构造参数路径)。
|
|
6
6
|
* - 同名 skill 按根目录优先级“先到先得”。
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -10,14 +10,46 @@ import fs from "fs-extra";
|
|
|
10
10
|
import yaml from "js-yaml";
|
|
11
11
|
import path from "path";
|
|
12
12
|
import type { Dirent, Stats } from "node:fs";
|
|
13
|
-
import
|
|
14
|
-
import { readSkillPluginConfig } from "../Config.js";
|
|
13
|
+
import { resolveSkillPluginOptions } from "../Config.js";
|
|
15
14
|
import { parseFrontMatter } from "./Frontmatter.js";
|
|
16
15
|
import { getClaudeSkillSearchRoots } from "./Paths.js";
|
|
17
|
-
import { isSubpath } from "./Utils.js";
|
|
18
16
|
import type { ClaudeSkill } from "@/skill/types/ClaudeSkill.js";
|
|
17
|
+
import type {
|
|
18
|
+
SkillPluginIgnoreRule,
|
|
19
|
+
SkillPluginOptions,
|
|
20
|
+
} from "@/skill/types/SkillPlugin.js";
|
|
19
21
|
import type { JsonObject, JsonValue } from "@downcity/agent/internal/types/common/Json.js";
|
|
20
22
|
|
|
23
|
+
function matchesIgnoreRule(skill: ClaudeSkill, rule: SkillPluginIgnoreRule): boolean {
|
|
24
|
+
if (typeof rule === "string") {
|
|
25
|
+
const value = rule.trim().toLowerCase();
|
|
26
|
+
if (!value) return false;
|
|
27
|
+
return skill.id.toLowerCase() === value || skill.name.toLowerCase() === value;
|
|
28
|
+
}
|
|
29
|
+
if (rule instanceof RegExp) {
|
|
30
|
+
rule.lastIndex = 0;
|
|
31
|
+
const matchesId = rule.test(skill.id);
|
|
32
|
+
rule.lastIndex = 0;
|
|
33
|
+
const matchesName = rule.test(skill.name);
|
|
34
|
+
rule.lastIndex = 0;
|
|
35
|
+
return matchesId || matchesName;
|
|
36
|
+
}
|
|
37
|
+
if (typeof rule === "function") {
|
|
38
|
+
return rule(skill);
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function shouldIgnoreSkill(
|
|
44
|
+
skill: ClaudeSkill,
|
|
45
|
+
rules: SkillPluginIgnoreRule[],
|
|
46
|
+
): boolean {
|
|
47
|
+
for (const rule of rules) {
|
|
48
|
+
if (matchesIgnoreRule(skill, rule)) return true;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
|
|
21
53
|
/**
|
|
22
54
|
* 扫描并发现 Claude Code-compatible skills。
|
|
23
55
|
*
|
|
@@ -34,26 +66,18 @@ import type { JsonObject, JsonValue } from "@downcity/agent/internal/types/commo
|
|
|
34
66
|
*/
|
|
35
67
|
export function discoverClaudeSkillsSync(
|
|
36
68
|
projectRoot: string,
|
|
37
|
-
|
|
69
|
+
options?: SkillPluginOptions | null,
|
|
38
70
|
): ClaudeSkill[] {
|
|
39
71
|
const root = String(projectRoot || "").trim();
|
|
40
72
|
if (!root) return [];
|
|
41
|
-
const
|
|
42
|
-
const roots = getClaudeSkillSearchRoots(root,
|
|
73
|
+
const resolvedOptions = resolveSkillPluginOptions(options);
|
|
74
|
+
const roots = getClaudeSkillSearchRoots(root, resolvedOptions);
|
|
43
75
|
|
|
44
76
|
const outById = new Map<string, ClaudeSkill>();
|
|
45
77
|
|
|
46
78
|
for (const r of roots) {
|
|
47
79
|
const sourceRoot = r.resolved;
|
|
48
80
|
|
|
49
|
-
// allowExternalPaths 只影响 config 外部路径;home 默认可扫描
|
|
50
|
-
if (
|
|
51
|
-
r.source === "config" &&
|
|
52
|
-
!allowExternal &&
|
|
53
|
-
!isSubpath(root, sourceRoot)
|
|
54
|
-
) {
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
81
|
if (!fs.existsSync(sourceRoot)) continue;
|
|
58
82
|
let stat: Stats;
|
|
59
83
|
try {
|
|
@@ -122,7 +146,7 @@ export function discoverClaudeSkillsSync(
|
|
|
122
146
|
const allowedTools =
|
|
123
147
|
meta?.["allowed-tools"] ?? meta?.allowedTools ?? meta?.allowed_tools;
|
|
124
148
|
|
|
125
|
-
|
|
149
|
+
const skill: ClaudeSkill = {
|
|
126
150
|
id,
|
|
127
151
|
name,
|
|
128
152
|
description,
|
|
@@ -131,7 +155,9 @@ export function discoverClaudeSkillsSync(
|
|
|
131
155
|
directoryPath,
|
|
132
156
|
skillMdPath,
|
|
133
157
|
allowedTools,
|
|
134
|
-
}
|
|
158
|
+
};
|
|
159
|
+
if (shouldIgnoreSkill(skill, resolvedOptions.ignore)) continue;
|
|
160
|
+
outById.set(id, skill);
|
|
135
161
|
}
|
|
136
162
|
}
|
|
137
163
|
|