foliko 2.0.5 → 2.0.6
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/.claude/settings.local.json +4 -1
- package/.cli_default_systemPrompt.md +291 -0
- package/.editorconfig +56 -56
- package/.lintstagedrc +7 -7
- package/.prettierignore +29 -29
- package/.prettierrc +11 -11
- package/CLAUDE.md +3 -0
- package/Dockerfile +63 -63
- package/README.md +20 -3
- package/docs/architecture.md +34 -2
- package/docs/extensions.md +199 -0
- package/docs/migration.md +100 -0
- package/docs/public-api.md +280 -24
- package/docs/usage.md +122 -30
- package/install.ps1 +129 -129
- package/install.sh +121 -121
- package/package.json +1 -1
- package/plugins/core/audit/index.js +1 -1
- package/plugins/core/default/bootstrap.js +44 -19
- package/plugins/core/python-loader/index.js +43 -25
- package/plugins/core/skill-manager/PROMPT.md +6 -0
- package/plugins/core/skill-manager/index.js +402 -115
- package/plugins/core/sub-agent/PROMPT.md +10 -0
- package/plugins/core/sub-agent/index.js +36 -3
- package/plugins/core/think/index.js +1 -0
- package/plugins/core/workflow/index.js +82 -22
- package/plugins/executors/data-splitter/PROMPT.md +13 -0
- package/plugins/executors/data-splitter/index.js +5 -4
- package/plugins/executors/extension/extension-registry.js +145 -0
- package/plugins/executors/extension/index.js +405 -437
- package/plugins/executors/extension/prompt-builder.js +359 -0
- package/plugins/executors/extension/skill-helper.js +143 -0
- package/plugins/messaging/feishu/index.js +5 -3
- package/plugins/messaging/qq/index.js +6 -4
- package/plugins/messaging/telegram/index.js +6 -3
- package/plugins/messaging/weixin/index.js +5 -3
- package/plugins/tools/PROMPT.md +26 -0
- package/plugins/tools/index.js +6 -5
- package/skills/find-skills/SKILL.md +133 -133
- package/skills/foliko/AGENTS.md +196 -43
- package/skills/foliko/SKILL.md +157 -28
- package/skills/mcp/SKILL.md +77 -118
- package/skills/plugins/SKILL.md +89 -3
- package/skills/python/SKILL.md +57 -39
- package/skills/skill-guide/SKILL.md +42 -34
- package/skills/workflows/SKILL.md +224 -9
- package/skills/workflows/workflow-troubleshooting/SKILL.md +221 -281
- package/src/agent/chat.js +48 -27
- package/src/agent/main.js +34 -13
- package/src/agent/prompt-registry.js +56 -16
- package/src/agent/prompts/PROMPT.md +3 -0
- package/src/agent/sub.js +1 -1
- package/src/cli/ui/chat-ui-old.js +5 -2
- package/src/cli/ui/chat-ui.js +5 -2
- package/src/common/constants.js +12 -0
- package/src/common/error-capture.js +91 -0
- package/src/common/logger.js +2 -2
- package/src/context/compressor.js +6 -2
- package/src/executors/mcp-executor.js +105 -125
- package/src/framework/framework.js +23 -11
- package/src/index.js +4 -0
- package/src/plugin/base.js +908 -5
- package/src/plugin/manager.js +29 -8
- package/src/tool/schema.js +32 -9
- package/website/index.html +821 -0
package/src/plugin/base.js
CHANGED
|
@@ -21,6 +21,12 @@ class Plugin {
|
|
|
21
21
|
_framework = null;
|
|
22
22
|
_subAgents = [];
|
|
23
23
|
tools = {};
|
|
24
|
+
_registeredTools = []; // 记录通过 tool.register 注册的工具 { name, def }
|
|
25
|
+
_registeredExtensions = []; // 记录通过 extension.register 注册的扩展工具 { extName, toolName, toolDef }
|
|
26
|
+
_registeredWorkflows = []; // 记录通过 workflow.register 注册的工作流 { name, def }
|
|
27
|
+
_registeredAgents = []; // 记录通过 agent.register 注册的子代理 { name, agent }
|
|
28
|
+
_registeredPrompts = []; // 记录通过 prompt.register 注册的 prompt parts { name, provider, options }
|
|
29
|
+
_registeredSkills = []; // 记录通过 skill.register 注册的技能 { name, content, metadata, options }
|
|
24
30
|
|
|
25
31
|
/**
|
|
26
32
|
* 声明式 prompt parts(推荐使用,替代 start() 中的 registerPromptPart 调用)
|
|
@@ -74,16 +80,730 @@ class Plugin {
|
|
|
74
80
|
}
|
|
75
81
|
}
|
|
76
82
|
|
|
83
|
+
/**
|
|
84
|
+
* 注册工具到 ToolRegistry(AI SDK 直接调用方式)
|
|
85
|
+
* 会在 uninstall/reload 时自动移除
|
|
86
|
+
*
|
|
87
|
+
* 用法:
|
|
88
|
+
* this.tool.register({
|
|
89
|
+
* name: 'my-tool',
|
|
90
|
+
* description: '描述',
|
|
91
|
+
* inputSchema: z.object({ ... }),
|
|
92
|
+
* execute: async (args) => { ... }
|
|
93
|
+
* });
|
|
94
|
+
*/
|
|
95
|
+
get tool() {
|
|
96
|
+
if (!this._toolAccessor) {
|
|
97
|
+
this._toolAccessor = {
|
|
98
|
+
register: (toolDef) => {
|
|
99
|
+
if (!this._framework) {
|
|
100
|
+
log.warn(`[${this.name}] tool.register() called before plugin installed`);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const registry = this._framework.toolRegistry;
|
|
104
|
+
if (!registry) {
|
|
105
|
+
log.warn(`[${this.name}] toolRegistry not found`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (!toolDef || !toolDef.name) {
|
|
109
|
+
log.warn(`[${this.name}] tool.register() missing tool name`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// 记录,用于卸载时清理
|
|
113
|
+
this._registeredTools.push({ name: toolDef.name, def: toolDef });
|
|
114
|
+
// 阻止重复注册同名工具
|
|
115
|
+
if (registry.has(toolDef.name)) {
|
|
116
|
+
log.debug(`[${this.name}] Tool '${toolDef.name}' already registered, skipping`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
registry.register(toolDef);
|
|
120
|
+
log.debug(`[${this.name}] Registered tool: ${toolDef.name}`);
|
|
121
|
+
},
|
|
122
|
+
remove: (toolName) => {
|
|
123
|
+
if (!this._framework) {
|
|
124
|
+
log.warn(`[${this.name}] tool.remove() called before plugin installed`);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const registry = this._framework.toolRegistry;
|
|
128
|
+
if (!registry) return;
|
|
129
|
+
registry.unregister(toolName);
|
|
130
|
+
this._registeredTools = this._registeredTools.filter(t => t.name !== toolName);
|
|
131
|
+
log.debug(`[${this.name}] Removed tool: ${toolName}`);
|
|
132
|
+
},
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* 执行已注册的工具
|
|
136
|
+
* @param {string} toolName 工具名称
|
|
137
|
+
* @param {object} args 工具参数
|
|
138
|
+
* @param {object} context 执行上下文(可选)
|
|
139
|
+
*/
|
|
140
|
+
execute: async (toolName, args, context = {}) => {
|
|
141
|
+
if (!this._framework) {
|
|
142
|
+
throw new Error(`[${this.name}] tool.execute() called before plugin installed`);
|
|
143
|
+
}
|
|
144
|
+
const registry = this._framework.toolRegistry;
|
|
145
|
+
if (!registry) {
|
|
146
|
+
throw new Error(`[${this.name}] toolRegistry not found`);
|
|
147
|
+
}
|
|
148
|
+
if (!registry.has(toolName)) {
|
|
149
|
+
throw new Error(`[${this.name}] Tool '${toolName}' not found`);
|
|
150
|
+
}
|
|
151
|
+
return await registry.execute(toolName, args, context);
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return this._toolAccessor;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 注册扩展工具到 ExtensionExecutor(通过 ext_call 间接调用)
|
|
160
|
+
* 会在 uninstall/reload 时自动移除
|
|
161
|
+
* 扩展名自动使用 this.name
|
|
162
|
+
*
|
|
163
|
+
* 用法:
|
|
164
|
+
* this.extension.register({
|
|
165
|
+
* name: 'do-something',
|
|
166
|
+
* description: '描述',
|
|
167
|
+
* inputSchema: z.object({ ... }),
|
|
168
|
+
* execute: async (args) => { ... }
|
|
169
|
+
* });
|
|
170
|
+
*/
|
|
171
|
+
get extension() {
|
|
172
|
+
if (!this._extensionAccessor) {
|
|
173
|
+
this._extensionAccessor = {
|
|
174
|
+
register: (toolDef) => {
|
|
175
|
+
if (!this._framework) {
|
|
176
|
+
log.warn(`[${this.name}] extension.register() called before plugin installed`);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
if (!toolDef || !toolDef.name) {
|
|
180
|
+
log.warn(`[${this.name}] extension.register() missing tool name`);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// 记录,用于卸载时清理(扩展名自动用 this.name)
|
|
184
|
+
// 即使 extExec 还未加载,也先记录
|
|
185
|
+
this._registeredExtensions.push({ extName: this.name, toolName: toolDef.name, toolDef });
|
|
186
|
+
|
|
187
|
+
const extExec = this._framework.pluginManager.get('extension-executor');
|
|
188
|
+
if (!extExec) {
|
|
189
|
+
log.debug(`[${this.name}] extension-executor not found yet, deferring tool registration: ${toolDef.name}`);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
// extExec 可用,立即注册
|
|
193
|
+
extExec.registerTool(
|
|
194
|
+
this.name,
|
|
195
|
+
{
|
|
196
|
+
name: this.name,
|
|
197
|
+
description: this.description,
|
|
198
|
+
version: this.version,
|
|
199
|
+
},
|
|
200
|
+
toolDef
|
|
201
|
+
);
|
|
202
|
+
// 强制失效缓存,确保 prompt 能更新
|
|
203
|
+
extExec._invalidatePromptCaches(this._framework);
|
|
204
|
+
log.debug(`[${this.name}] Registered extension tool: ${this.name}/${toolDef.name}`);
|
|
205
|
+
},
|
|
206
|
+
remove: (toolName) => {
|
|
207
|
+
if (!this._framework) {
|
|
208
|
+
log.warn(`[${this.name}] extension.remove() called before plugin installed`);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const extExec = this._framework.pluginManager.get('extension-executor');
|
|
212
|
+
if (extExec) {
|
|
213
|
+
extExec._unregisterTool(this.name, toolName);
|
|
214
|
+
}
|
|
215
|
+
this._registeredExtensions = this._registeredExtensions.filter(
|
|
216
|
+
e => !(e.extName === this.name && e.toolName === toolName)
|
|
217
|
+
);
|
|
218
|
+
log.debug(`[${this.name}] Removed extension tool: ${this.name}/${toolName}`);
|
|
219
|
+
},
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* 执行扩展工具(通过 ext_call 调用)
|
|
223
|
+
* 支持两种调用方式:
|
|
224
|
+
* this.extension.execute('tool-name', args) // 执行当前插件的
|
|
225
|
+
* this.extension.execute('plugin-name', 'tool-name', args) // 执行指定插件的
|
|
226
|
+
*/
|
|
227
|
+
execute: async (pluginNameOrToolName, toolNameOrArgs, args = {}) => {
|
|
228
|
+
if (!this._framework) {
|
|
229
|
+
throw new Error(`[${this.name}] extension.execute() called before plugin installed`);
|
|
230
|
+
}
|
|
231
|
+
const extExec = this._framework.pluginManager.get('extension-executor');
|
|
232
|
+
if (!extExec) {
|
|
233
|
+
return { success: false, error: 'extension-executor not found' };
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
let pluginName, toolName;
|
|
237
|
+
if (typeof toolNameOrArgs === 'object') {
|
|
238
|
+
// 第一种:只传工具名和参数
|
|
239
|
+
pluginName = this.name;
|
|
240
|
+
toolName = pluginNameOrToolName;
|
|
241
|
+
args = toolNameOrArgs;
|
|
242
|
+
} else {
|
|
243
|
+
// 第二种:传插件名、工具名和参数
|
|
244
|
+
pluginName = pluginNameOrToolName;
|
|
245
|
+
toolName = toolNameOrArgs;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const result = await extExec._executeTool(pluginName, toolName, args, this._framework);
|
|
249
|
+
return result;
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
return this._extensionAccessor;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* 注册工作流到 WorkflowEngine
|
|
258
|
+
* 支持 register/remove/execute 方法
|
|
259
|
+
*
|
|
260
|
+
* 用法:
|
|
261
|
+
* this.workflow.register('my-workflow', {
|
|
262
|
+
* name: 'my-workflow',
|
|
263
|
+
* steps: [...]
|
|
264
|
+
* });
|
|
265
|
+
*/
|
|
266
|
+
get workflow() {
|
|
267
|
+
if (!this._workflowAccessor) {
|
|
268
|
+
this._workflowAccessor = {
|
|
269
|
+
register: (name, workflowDef) => {
|
|
270
|
+
if (!this._framework) {
|
|
271
|
+
log.warn(`[${this.name}] workflow.register() called before plugin installed`);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const workflowPlugin = this._framework.pluginManager?.get('workflow');
|
|
275
|
+
if (!workflowPlugin) {
|
|
276
|
+
log.warn(`[${this.name}] workflow plugin not found`);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
if (!name || !workflowDef) {
|
|
280
|
+
log.warn(`[${this.name}] workflow.register() missing name or definition`);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
// 记录,用于卸载时清理
|
|
284
|
+
this._registeredWorkflows.push({ name, def: workflowDef });
|
|
285
|
+
// 注册到 workflow plugin
|
|
286
|
+
workflowPlugin._workflows.set(name, workflowDef);
|
|
287
|
+
// 注册为工具
|
|
288
|
+
workflowPlugin._registerWorkflowTools();
|
|
289
|
+
log.debug(`[${this.name}] Registered workflow: ${name}`);
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
remove: (name) => {
|
|
293
|
+
if (!this._framework) {
|
|
294
|
+
log.warn(`[${this.name}] workflow.remove() called before plugin installed`);
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const workflowPlugin = this._framework.pluginManager?.get('workflow');
|
|
298
|
+
if (!workflowPlugin) return;
|
|
299
|
+
workflowPlugin.remove(name);
|
|
300
|
+
this._registeredWorkflows = this._registeredWorkflows.filter(w => w.name !== name);
|
|
301
|
+
log.debug(`[${this.name}] Removed workflow: ${name}`);
|
|
302
|
+
},
|
|
303
|
+
|
|
304
|
+
execute: async (name, input = {}) => {
|
|
305
|
+
if (!this._framework) {
|
|
306
|
+
throw new Error(`[${this.name}] workflow.execute() called before plugin installed`);
|
|
307
|
+
}
|
|
308
|
+
const workflowPlugin = this._framework.pluginManager?.get('workflow');
|
|
309
|
+
if (!workflowPlugin) {
|
|
310
|
+
throw new Error(`[${this.name}] workflow plugin not found`);
|
|
311
|
+
}
|
|
312
|
+
return await workflowPlugin.executeWorkflow(name, input);
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return this._workflowAccessor;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* 注册子代理到框架
|
|
321
|
+
* 支持 register/remove/execute 方法
|
|
322
|
+
*
|
|
323
|
+
* 用法:
|
|
324
|
+
* this.agent.register({
|
|
325
|
+
* name: 'my-agent',
|
|
326
|
+
* role: '助手',
|
|
327
|
+
* goal: '描述',
|
|
328
|
+
* tools: {},
|
|
329
|
+
* parentTools: []
|
|
330
|
+
* });
|
|
331
|
+
*/
|
|
332
|
+
get agent() {
|
|
333
|
+
if (!this._agentAccessor) {
|
|
334
|
+
this._agentAccessor = {
|
|
335
|
+
register: (config) => {
|
|
336
|
+
if (!this._framework) {
|
|
337
|
+
log.warn(`[${this.name}] agent.register() called before plugin installed`);
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
if (!config || !config.name) {
|
|
341
|
+
log.warn(`[${this.name}] agent.register() missing config or name`);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
const subAgentManager = this._framework.pluginManager.get('subagent-manager');
|
|
345
|
+
if (!subAgentManager) {
|
|
346
|
+
log.warn(`[${this.name}] subagent-manager plugin not found`);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const agent = this._framework.createSubAgent({
|
|
351
|
+
name: config.name,
|
|
352
|
+
role: config.role,
|
|
353
|
+
goal: config.description || config.role,
|
|
354
|
+
tools: config.tools || {},
|
|
355
|
+
parentTools: config.parentTools || [],
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
subAgentManager.registerPlugin({
|
|
359
|
+
name: config.name,
|
|
360
|
+
role: config.role,
|
|
361
|
+
goal: config.description || config.role,
|
|
362
|
+
agent,
|
|
363
|
+
tools: config.tools || {},
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// 记录,用于卸载时清理
|
|
367
|
+
this._registeredAgents.push({ name: config.name, agent });
|
|
368
|
+
|
|
369
|
+
// 也添加到 _subAgents 供基类管理
|
|
370
|
+
this._subAgents.push({
|
|
371
|
+
name: config.name,
|
|
372
|
+
agent,
|
|
373
|
+
plugin: null,
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
log.debug(`[${this.name}] Registered agent: ${config.name}`);
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
remove: (name) => {
|
|
380
|
+
if (!this._framework) {
|
|
381
|
+
log.warn(`[${this.name}] agent.remove() called before plugin installed`);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
const subAgentManager = this._framework.pluginManager.get('subagent-manager');
|
|
385
|
+
if (!subAgentManager) return;
|
|
386
|
+
|
|
387
|
+
subAgentManager.unregisterPlugin(name);
|
|
388
|
+
|
|
389
|
+
// 从 _subAgents 移除
|
|
390
|
+
this._subAgents = this._subAgents.filter(s => s.name !== name);
|
|
391
|
+
// 从 _registeredAgents 移除
|
|
392
|
+
this._registeredAgents = this._registeredAgents.filter(a => a.name !== name);
|
|
393
|
+
|
|
394
|
+
log.debug(`[${this.name}] Removed agent: ${name}`);
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
get: (name) => {
|
|
398
|
+
return this._registeredAgents.find(a => a.name === name)?.agent;
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
return this._agentAccessor;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* 注册 Prompt Part 到 framework.prompts
|
|
407
|
+
* 支持 register/remove 方法
|
|
408
|
+
*
|
|
409
|
+
* 用法:
|
|
410
|
+
* this.prompt.register('my-prompt', () => 'prompt content', { scope: 'global', priority: 50 });
|
|
411
|
+
*/
|
|
412
|
+
get prompt() {
|
|
413
|
+
if (!this._promptAccessor) {
|
|
414
|
+
this._promptAccessor = {
|
|
415
|
+
register: (name, provider, options = {}) => {
|
|
416
|
+
if (!this._framework) {
|
|
417
|
+
log.warn(`[${this.name}] prompt.register() called before plugin installed`);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const reg = this._framework.prompts;
|
|
421
|
+
if (!reg) {
|
|
422
|
+
log.warn(`[${this.name}] prompts registry not found`);
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
if (!name || !provider) {
|
|
426
|
+
log.warn(`[${this.name}] prompt.register() missing name or provider`);
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
// 记录,用于卸载时清理
|
|
430
|
+
this._registeredPrompts.push({ name, provider, options });
|
|
431
|
+
// 注册到 framework.prompts
|
|
432
|
+
reg.register(this.name, name, provider, options);
|
|
433
|
+
log.debug(`[${this.name}] Registered prompt: ${name}`);
|
|
434
|
+
},
|
|
435
|
+
|
|
436
|
+
remove: (name) => {
|
|
437
|
+
if (!this._framework) {
|
|
438
|
+
log.warn(`[${this.name}] prompt.remove() called before plugin installed`);
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
const reg = this._framework.prompts;
|
|
442
|
+
if (!reg) return;
|
|
443
|
+
reg.clearOwner(this.name, name);
|
|
444
|
+
this._registeredPrompts = this._registeredPrompts.filter(p => p.name !== name);
|
|
445
|
+
log.debug(`[${this.name}] Removed prompt: ${name}`);
|
|
446
|
+
},
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
return this._promptAccessor;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* 注册/查询/删除 Skill(程序化管理技能)
|
|
454
|
+
* 会在 uninstall/reload 时自动清理
|
|
455
|
+
*
|
|
456
|
+
* 用法:
|
|
457
|
+
* // 注册
|
|
458
|
+
* this.skill.register('my-skill', '技能内容(markdown)', {
|
|
459
|
+
* description: '技能描述',
|
|
460
|
+
* 'allowed-tools': 'tool1,tool2'
|
|
461
|
+
* }, {
|
|
462
|
+
* commands: [{ name: 'cmd1', description: '...', execute: async (args) => {...} }],
|
|
463
|
+
* references: { 'extra': 'extra.md' },
|
|
464
|
+
* path: '/path/to/skill' // 可选
|
|
465
|
+
* });
|
|
466
|
+
*
|
|
467
|
+
* // 查询
|
|
468
|
+
* const skill = this.skill.get('my-skill');
|
|
469
|
+
* const details = this.skill.details('my-skill');
|
|
470
|
+
*
|
|
471
|
+
* // 删除
|
|
472
|
+
* this.skill.remove('my-skill');
|
|
473
|
+
*/
|
|
474
|
+
get skill() {
|
|
475
|
+
if (!this._skillAccessor) {
|
|
476
|
+
this._skillAccessor = {
|
|
477
|
+
/**
|
|
478
|
+
* 注册一个技能
|
|
479
|
+
* @param {string} name 技能名称
|
|
480
|
+
* @param {string} content 技能内容(markdown)
|
|
481
|
+
* @param {Object} [metadata] 元数据 { description, license, compatibility, allowedTools, 'allowed-tools' }
|
|
482
|
+
* @param {Object} [options] 选项 { commands, references, scripts, path }
|
|
483
|
+
* @returns {Object|null} 注册结果
|
|
484
|
+
*/
|
|
485
|
+
register: (name, content, metadata = {}, options = {}) => {
|
|
486
|
+
if (!this._framework) {
|
|
487
|
+
log.warn(`[${this.name}] skill.register() called before plugin installed`);
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
if (!name) {
|
|
491
|
+
log.warn(`[${this.name}] skill.register() missing name`);
|
|
492
|
+
return null;
|
|
493
|
+
}
|
|
494
|
+
const sm = this._framework.pluginManager?.get('skill-manager');
|
|
495
|
+
if (!sm) {
|
|
496
|
+
log.warn(`[${this.name}] skill-manager plugin not found`);
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
// 记录,用于卸载时清理
|
|
500
|
+
this._registeredSkills.push({ name, content, metadata, options });
|
|
501
|
+
// 调用 skill-manager 的 register
|
|
502
|
+
const result = sm.register(name, content || '', metadata, options);
|
|
503
|
+
log.debug(`[${this.name}] Registered skill: ${name}`);
|
|
504
|
+
return result;
|
|
505
|
+
},
|
|
506
|
+
|
|
507
|
+
/**
|
|
508
|
+
* 获取技能信息
|
|
509
|
+
* @param {string} name 技能名
|
|
510
|
+
* @returns {Object|null}
|
|
511
|
+
*/
|
|
512
|
+
get: (name) => {
|
|
513
|
+
const sm = this._framework?.pluginManager?.get('skill-manager');
|
|
514
|
+
return sm?.getSkill?.(name) || null;
|
|
515
|
+
},
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* 检查技能是否存在
|
|
519
|
+
*/
|
|
520
|
+
has: (name) => {
|
|
521
|
+
const sm = this._framework?.pluginManager?.get('skill-manager');
|
|
522
|
+
return sm?.hasSkill?.(name) || false;
|
|
523
|
+
},
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* 获取技能详情(description、metadata、content、path、commands、references、scripts)
|
|
527
|
+
*/
|
|
528
|
+
details: (name) => {
|
|
529
|
+
const sm = this._framework?.pluginManager?.get('skill-manager');
|
|
530
|
+
return sm?.getSkillDetails?.(name) || null;
|
|
531
|
+
},
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* 列出所有已注册的技能名
|
|
535
|
+
*/
|
|
536
|
+
list: () => {
|
|
537
|
+
const sm = this._framework?.pluginManager?.get('skill-manager');
|
|
538
|
+
return sm?.getAllSkills?.() || [];
|
|
539
|
+
},
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* 列出本插件注册的技能
|
|
543
|
+
*/
|
|
544
|
+
listOwned: () => {
|
|
545
|
+
return this._registeredSkills.map((s) => s.name);
|
|
546
|
+
},
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* 删除技能
|
|
550
|
+
* @param {string} name 技能名
|
|
551
|
+
* @returns {boolean} 是否成功删除
|
|
552
|
+
*/
|
|
553
|
+
remove: (name) => {
|
|
554
|
+
if (!this._framework) {
|
|
555
|
+
log.warn(`[${this.name}] skill.remove() called before plugin installed`);
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
const sm = this._framework.pluginManager?.get('skill-manager');
|
|
559
|
+
if (!sm) return false;
|
|
560
|
+
const removed = sm.remove(name);
|
|
561
|
+
if (removed) {
|
|
562
|
+
this._registeredSkills = this._registeredSkills.filter((s) => s.name !== name);
|
|
563
|
+
log.debug(`[${this.name}] Removed skill: ${name}`);
|
|
564
|
+
}
|
|
565
|
+
return removed;
|
|
566
|
+
},
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* 直接执行技能命令(通过 ext_call)
|
|
570
|
+
* @param {string} command 格式 `<skill>:<command>`,如 'my-skill:cmd1'
|
|
571
|
+
* @param {Object} [args] 参数对象
|
|
572
|
+
*/
|
|
573
|
+
execute: async (command, args = {}) => {
|
|
574
|
+
if (!this._framework) {
|
|
575
|
+
throw new Error(`[${this.name}] skill.execute() called before plugin installed`);
|
|
576
|
+
}
|
|
577
|
+
if (!command || !command.includes(':')) {
|
|
578
|
+
throw new Error(`[${this.name}] skill.execute() requires '<skill>:<command>' format`);
|
|
579
|
+
}
|
|
580
|
+
const [skillName, ...rest] = command.split(':');
|
|
581
|
+
const cmdName = rest.join(':');
|
|
582
|
+
return await this._framework.executeTool('ext_call', {
|
|
583
|
+
plugin: `skill:${skillName}`,
|
|
584
|
+
tool: cmdName,
|
|
585
|
+
args: args || {},
|
|
586
|
+
});
|
|
587
|
+
},
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
return this._skillAccessor;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/**
|
|
594
|
+
* 插件启动钩子 - 子类可重写
|
|
595
|
+
* 注意:不需要调用 super.start(framework),基类自动处理
|
|
596
|
+
*/
|
|
597
|
+
onStart(framework) {
|
|
598
|
+
// 子类可重写此方法注册工具/扩展
|
|
599
|
+
}
|
|
600
|
+
|
|
77
601
|
start(framework) {
|
|
602
|
+
this._framework = framework;
|
|
78
603
|
this._autoRegisterSubAgents();
|
|
79
604
|
this._autoRegisterPrompts();
|
|
605
|
+
this._reRegisterTools();
|
|
606
|
+
this._reRegisterExtensions();
|
|
607
|
+
this._reRegisterWorkflows();
|
|
608
|
+
this._reRegisterAgents();
|
|
609
|
+
this._reRegisterPrompts();
|
|
610
|
+
this._reRegisterSkills();
|
|
611
|
+
this.onStart(framework);
|
|
80
612
|
return this;
|
|
81
613
|
}
|
|
82
614
|
|
|
615
|
+
/**
|
|
616
|
+
* 重新注册所有通过 tool.register 注册的工具
|
|
617
|
+
*/
|
|
618
|
+
_reRegisterTools() {
|
|
619
|
+
const registry = this._framework?.toolRegistry;
|
|
620
|
+
if (!registry) return;
|
|
621
|
+
for (const { name, def } of this._registeredTools) {
|
|
622
|
+
if (!registry.has(name)) {
|
|
623
|
+
registry.register(def);
|
|
624
|
+
log.debug(`[${this.name}] Re-registered tool: ${name}`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* 重新注册所有通过 extension.register 注册的扩展工具
|
|
631
|
+
*/
|
|
632
|
+
_reRegisterExtensions() {
|
|
633
|
+
const extExec = this._framework?.pluginManager?.get('extension-executor');
|
|
634
|
+
if (!extExec) return;
|
|
635
|
+
for (const { extName, toolDef } of this._registeredExtensions) {
|
|
636
|
+
extExec.registerTool(
|
|
637
|
+
extName,
|
|
638
|
+
{
|
|
639
|
+
name: extName,
|
|
640
|
+
description: this.description,
|
|
641
|
+
version: this.version,
|
|
642
|
+
},
|
|
643
|
+
toolDef
|
|
644
|
+
);
|
|
645
|
+
log.debug(`[${this.name}] Re-registered extension tool: ${extName}/${toolDef.name}`);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* 重新注册所有通过 workflow.register 注册的工作流
|
|
651
|
+
*/
|
|
652
|
+
_reRegisterWorkflows() {
|
|
653
|
+
const workflowPlugin = this._framework?.pluginManager?.get('workflow');
|
|
654
|
+
if (!workflowPlugin) return;
|
|
655
|
+
for (const { name, def } of this._registeredWorkflows) {
|
|
656
|
+
workflowPlugin._workflows.set(name, def);
|
|
657
|
+
workflowPlugin._registerWorkflowTools();
|
|
658
|
+
log.debug(`[${this.name}] Re-registered workflow: ${name}`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* 重新注册所有通过 agent.register 注册的子代理
|
|
664
|
+
*/
|
|
665
|
+
_reRegisterAgents() {
|
|
666
|
+
const subAgentManager = this._framework?.pluginManager?.get('subagent-manager');
|
|
667
|
+
if (!subAgentManager) return;
|
|
668
|
+
for (const { name, agent } of this._registeredAgents) {
|
|
669
|
+
subAgentManager.registerPlugin({
|
|
670
|
+
name,
|
|
671
|
+
agent,
|
|
672
|
+
role: agent.role || '',
|
|
673
|
+
goal: agent.goal || '',
|
|
674
|
+
tools: {},
|
|
675
|
+
});
|
|
676
|
+
log.debug(`[${this.name}] Re-registered agent: ${name}`);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* 重新注册所有通过 prompt.register 注册的 prompt parts
|
|
682
|
+
*/
|
|
683
|
+
_reRegisterPrompts() {
|
|
684
|
+
const reg = this._framework?.prompts;
|
|
685
|
+
if (!reg) return;
|
|
686
|
+
for (const { name, provider, options } of this._registeredPrompts) {
|
|
687
|
+
reg.register(this.name, name, provider, options);
|
|
688
|
+
log.debug(`[${this.name}] Re-registered prompt: ${name}`);
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* 重新注册所有通过 skill.register 注册的技能
|
|
694
|
+
*/
|
|
695
|
+
_reRegisterSkills() {
|
|
696
|
+
const sm = this._framework?.pluginManager?.get('skill-manager');
|
|
697
|
+
if (!sm) return;
|
|
698
|
+
for (const { name, content, metadata, options } of this._registeredSkills) {
|
|
699
|
+
// 如果技能已经存在(可能未在 reload 中清理),先移除
|
|
700
|
+
if (sm.hasSkill(name)) {
|
|
701
|
+
sm.remove(name);
|
|
702
|
+
}
|
|
703
|
+
sm.register(name, content || '', metadata, options);
|
|
704
|
+
log.debug(`[${this.name}] Re-registered skill: ${name}`);
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* 插件停止钩子 - 子类可重写
|
|
710
|
+
* 注意:不需要调用 super.stop(),基类自动处理
|
|
711
|
+
*/
|
|
712
|
+
onStop() {
|
|
713
|
+
// 子类可重写此方法进行清理
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* 停止插件(disable 时调用)
|
|
718
|
+
* 移除工具和扩展,但保留注册记录以便 re-enable 时重新注册
|
|
719
|
+
*/
|
|
720
|
+
stop() {
|
|
721
|
+
// 移除 tool.register 注册的工具
|
|
722
|
+
for (const { name } of this._registeredTools) {
|
|
723
|
+
try {
|
|
724
|
+
this._framework?.toolRegistry?.unregister(name);
|
|
725
|
+
log.debug(`[${this.name}] Stopped tool: ${name}`);
|
|
726
|
+
} catch (err) {
|
|
727
|
+
// ignore
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
// 移除 extension.register 注册的扩展工具
|
|
731
|
+
for (const { extName, toolName } of this._registeredExtensions) {
|
|
732
|
+
try {
|
|
733
|
+
const extExec = this._framework?.pluginManager?.get('extension-executor');
|
|
734
|
+
if (extExec) {
|
|
735
|
+
extExec._unregisterTool(extName, toolName);
|
|
736
|
+
log.debug(`[${this.name}] Stopped extension tool: ${extName}/${toolName}`);
|
|
737
|
+
}
|
|
738
|
+
} catch (err) {
|
|
739
|
+
// ignore
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
// 移除 workflow.register 注册的工作流
|
|
743
|
+
for (const { name } of this._registeredWorkflows) {
|
|
744
|
+
try {
|
|
745
|
+
const workflowPlugin = this._framework?.pluginManager?.get('workflow');
|
|
746
|
+
if (workflowPlugin) {
|
|
747
|
+
workflowPlugin.remove(name);
|
|
748
|
+
log.debug(`[${this.name}] Stopped workflow: ${name}`);
|
|
749
|
+
}
|
|
750
|
+
} catch (err) {
|
|
751
|
+
// ignore
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
// 移除 agent.register 注册的子代理
|
|
755
|
+
for (const { name } of this._registeredAgents) {
|
|
756
|
+
try {
|
|
757
|
+
const subAgentManager = this._framework?.pluginManager?.get('subagent-manager');
|
|
758
|
+
if (subAgentManager) {
|
|
759
|
+
subAgentManager.unregisterPlugin(name);
|
|
760
|
+
log.debug(`[${this.name}] Stopped agent: ${name}`);
|
|
761
|
+
}
|
|
762
|
+
} catch (err) {
|
|
763
|
+
// ignore
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
// 移除 prompt.register 注册的 prompt parts
|
|
767
|
+
for (const { name } of this._registeredPrompts) {
|
|
768
|
+
try {
|
|
769
|
+
const reg = this._framework?.prompts;
|
|
770
|
+
if (reg) {
|
|
771
|
+
reg.clearOwner(this.name, name);
|
|
772
|
+
log.debug(`[${this.name}] Stopped prompt: ${name}`);
|
|
773
|
+
}
|
|
774
|
+
} catch (err) {
|
|
775
|
+
// ignore
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
// 移除 skill.register 注册的技能
|
|
779
|
+
for (const { name } of this._registeredSkills) {
|
|
780
|
+
try {
|
|
781
|
+
const sm = this._framework?.pluginManager?.get('skill-manager');
|
|
782
|
+
if (sm) {
|
|
783
|
+
sm.remove(name);
|
|
784
|
+
log.debug(`[${this.name}] Stopped skill: ${name}`);
|
|
785
|
+
}
|
|
786
|
+
} catch (err) {
|
|
787
|
+
// ignore
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
this.onStop();
|
|
791
|
+
}
|
|
792
|
+
|
|
83
793
|
/**
|
|
84
794
|
* 自动注册 this.prompts 声明的所有 part 到 framework.prompts
|
|
85
795
|
* - 失败一个不影响其他
|
|
86
796
|
* - 'this' 在 provider 中绑定到 plugin 实例(必须用普通 function,箭头函数的 this 是词法的)
|
|
797
|
+
*
|
|
798
|
+
* 支持两种声明方式:
|
|
799
|
+
* 1. provider 函数(原有方式):
|
|
800
|
+
* { name: 'xxx', provider() { return this._buildSomething(); } }
|
|
801
|
+
*
|
|
802
|
+
* 2. 文件方式(新增):
|
|
803
|
+
* { name: 'xxx', file: './PROMPT.md', vars: { key: 'value' } }
|
|
804
|
+
* - file 相对于插件文件路径解析
|
|
805
|
+
* - vars 支持静态对象或 () => object 动态函数
|
|
806
|
+
* - {{varName}} 在渲染时被替换
|
|
87
807
|
*/
|
|
88
808
|
_autoRegisterPrompts() {
|
|
89
809
|
const reg = this._framework?.prompts;
|
|
@@ -91,14 +811,23 @@ class Plugin {
|
|
|
91
811
|
if (!Array.isArray(this.prompts) || this.prompts.length === 0) return;
|
|
92
812
|
|
|
93
813
|
for (const entry of this.prompts) {
|
|
94
|
-
if (!entry || !entry.name
|
|
95
|
-
log.warn(`[${this.name}] Invalid prompt entry: missing name
|
|
814
|
+
if (!entry || !entry.name) {
|
|
815
|
+
log.warn(`[${this.name}] Invalid prompt entry: missing name`);
|
|
96
816
|
continue;
|
|
97
817
|
}
|
|
98
818
|
try {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
819
|
+
let provider;
|
|
820
|
+
if (entry.file) {
|
|
821
|
+
// 文件方式:读取 PROMPT.md 并支持变量插值
|
|
822
|
+
provider = this._createFilePromptProvider(entry.file, entry.vars, entry.interpolate);
|
|
823
|
+
} else if (typeof entry.provider === 'function') {
|
|
824
|
+
// 函数方式
|
|
825
|
+
provider = entry.provider.bind(this);
|
|
826
|
+
} else {
|
|
827
|
+
log.warn(`[${this.name}] Prompt entry "${entry.name}" missing provider or file`);
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
reg.register(this.name, entry.name, provider, {
|
|
102
831
|
scope: entry.scope,
|
|
103
832
|
priority: entry.priority,
|
|
104
833
|
description: entry.description,
|
|
@@ -109,6 +838,70 @@ class Plugin {
|
|
|
109
838
|
}
|
|
110
839
|
}
|
|
111
840
|
|
|
841
|
+
/**
|
|
842
|
+
* 创建文件 prompt provider
|
|
843
|
+
* @param {string} filePath 文件路径(绝对或相对)
|
|
844
|
+
* @param {Object|Function} vars 变量(静态或动态函数)
|
|
845
|
+
* @param {Function} interpolate 后处理函数,用于注入动态内容
|
|
846
|
+
*/
|
|
847
|
+
_createFilePromptProvider(filePath, vars, interpolate) {
|
|
848
|
+
const fs = require('fs');
|
|
849
|
+
const path = require('path');
|
|
850
|
+
|
|
851
|
+
// 解析文件路径:相对路径基于插件文件目录,绝对路径直接用
|
|
852
|
+
let fullPath = filePath;
|
|
853
|
+
if (!path.isAbsolute(filePath)) {
|
|
854
|
+
// 使用栈追踪获取插件文件路径
|
|
855
|
+
// 栈结构: Error, _createFilePromptProvider, _autoRegisterPrompts, 子类start, plugin-manager调用start, ...
|
|
856
|
+
const stack = new Error().stack.split('\n');
|
|
857
|
+
let pluginFile;
|
|
858
|
+
for (let i = 3; i < stack.length; i++) { // 从索引3开始跳过 base.js 的方法
|
|
859
|
+
const line = stack[i];
|
|
860
|
+
// 排除 base.js 自身和 node: 内置模块
|
|
861
|
+
if (line.includes('plugin/base.js') || line.includes('node:')) continue;
|
|
862
|
+
// 提取文件路径 (filename:line:col) 或 (filename)
|
|
863
|
+
const match = line.match(/\((.*?):\d+:\d+\)/) || line.match(/at\s+(.*?):\d+:\d+/);
|
|
864
|
+
if (match && match[1] !== '[eval]') {
|
|
865
|
+
pluginFile = match[1];
|
|
866
|
+
break;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
const pluginDir = pluginFile ? path.dirname(path.resolve(pluginFile)) : process.cwd();
|
|
870
|
+
fullPath = path.resolve(pluginDir, filePath);
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// 缓存原始文件内容(只读一次)
|
|
874
|
+
if (!fs.existsSync(fullPath)) {
|
|
875
|
+
return () => { throw new Error(`Prompt file not found: ${fullPath}`); };
|
|
876
|
+
}
|
|
877
|
+
const rawContent = fs.readFileSync(fullPath, 'utf-8');
|
|
878
|
+
|
|
879
|
+
return () => {
|
|
880
|
+
let content = rawContent;
|
|
881
|
+
|
|
882
|
+
// 变量插值
|
|
883
|
+
if (vars) {
|
|
884
|
+
const varsObj = typeof vars === 'function' ? vars() : vars;
|
|
885
|
+
if (varsObj && typeof varsObj === 'object') {
|
|
886
|
+
content = content.replace(/\{\{(\w+)\}\}/g, (match, varName) => {
|
|
887
|
+
if (Object.prototype.hasOwnProperty.call(varsObj, varName)) {
|
|
888
|
+
const val = varsObj[varName];
|
|
889
|
+
return val == null ? match : String(val);
|
|
890
|
+
}
|
|
891
|
+
return match;
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// 后处理:用于注入动态生成的列表(如 {{subagents}}, {{skills}} 等)
|
|
897
|
+
if (typeof interpolate === 'function') {
|
|
898
|
+
content = interpolate(content, this);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
return content;
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
|
|
112
905
|
/**
|
|
113
906
|
* 清理当前 plugin 在 framework.prompts 中注册的所有 part
|
|
114
907
|
*/
|
|
@@ -206,7 +999,36 @@ class Plugin {
|
|
|
206
999
|
this._cleanupPrompts();
|
|
207
1000
|
this._autoRegisterPrompts();
|
|
208
1001
|
|
|
1002
|
+
// 清理 tool.register 注册的工具(reload 后需重新注册)
|
|
1003
|
+
for (const { name: toolName } of this._registeredTools) {
|
|
1004
|
+
try {
|
|
1005
|
+
framework.toolRegistry?.unregister(toolName);
|
|
1006
|
+
} catch (err) {
|
|
1007
|
+
// ignore
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
// 注:_registeredTools 保留,reload 后 framework 重新设置后仍可重新注册
|
|
1011
|
+
|
|
1012
|
+
// 清理 extension.register 注册的扩展工具
|
|
1013
|
+
for (const { extName, toolName } of this._registeredExtensions) {
|
|
1014
|
+
try {
|
|
1015
|
+
const extExec = framework.pluginManager?.get('extension-executor');
|
|
1016
|
+
if (extExec) {
|
|
1017
|
+
extExec._unregisterTool(extName, toolName);
|
|
1018
|
+
}
|
|
1019
|
+
} catch (err) {
|
|
1020
|
+
// ignore
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
209
1024
|
this._autoRegisterSubAgents();
|
|
1025
|
+
this._reRegisterTools();
|
|
1026
|
+
this._reRegisterExtensions();
|
|
1027
|
+
this._reRegisterWorkflows();
|
|
1028
|
+
this._reRegisterAgents();
|
|
1029
|
+
this._reRegisterPrompts();
|
|
1030
|
+
this._reRegisterSkills();
|
|
1031
|
+
this.onStart(framework);
|
|
210
1032
|
}
|
|
211
1033
|
|
|
212
1034
|
onCwdChanged(oldCwd, newCwd, framework) {
|
|
@@ -217,6 +1039,87 @@ class Plugin {
|
|
|
217
1039
|
// 清理 framework.prompts 中该插件注册的所有 part
|
|
218
1040
|
this._cleanupPrompts();
|
|
219
1041
|
|
|
1042
|
+
// 清理通过 tool.register 注册的工具
|
|
1043
|
+
for (const { name: toolName } of this._registeredTools) {
|
|
1044
|
+
try {
|
|
1045
|
+
framework.toolRegistry?.unregister(toolName);
|
|
1046
|
+
log.debug(`[${this.name}] Unregistered tool: ${toolName}`);
|
|
1047
|
+
} catch (err) {
|
|
1048
|
+
log.warn(`[${this.name}] Failed to unregister tool '${toolName}':`, err.message);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
this._registeredTools = [];
|
|
1052
|
+
|
|
1053
|
+
// 清理通过 extension.register 注册的扩展工具
|
|
1054
|
+
for (const { extName, toolName } of this._registeredExtensions) {
|
|
1055
|
+
try {
|
|
1056
|
+
const extExec = framework.pluginManager?.get('extension-executor');
|
|
1057
|
+
if (extExec) {
|
|
1058
|
+
extExec._unregisterTool(extName, toolName);
|
|
1059
|
+
log.debug(`[${this.name}] Unregistered extension tool: ${extName}/${toolName}`);
|
|
1060
|
+
}
|
|
1061
|
+
} catch (err) {
|
|
1062
|
+
log.warn(`[${this.name}] Failed to unregister extension tool '${extName}/${toolName}':`, err.message);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
this._registeredExtensions = [];
|
|
1066
|
+
|
|
1067
|
+
// 清理通过 workflow.register 注册的工作流
|
|
1068
|
+
for (const { name } of this._registeredWorkflows) {
|
|
1069
|
+
try {
|
|
1070
|
+
const workflowPlugin = framework.pluginManager?.get('workflow');
|
|
1071
|
+
if (workflowPlugin) {
|
|
1072
|
+
workflowPlugin.remove(name);
|
|
1073
|
+
log.debug(`[${this.name}] Unregistered workflow: ${name}`);
|
|
1074
|
+
}
|
|
1075
|
+
} catch (err) {
|
|
1076
|
+
log.warn(`[${this.name}] Failed to unregister workflow '${name}':`, err.message);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
this._registeredWorkflows = [];
|
|
1080
|
+
|
|
1081
|
+
// 清理通过 agent.register 注册的子代理
|
|
1082
|
+
for (const { name } of this._registeredAgents) {
|
|
1083
|
+
try {
|
|
1084
|
+
const subAgentManager = framework.pluginManager?.get('subagent-manager');
|
|
1085
|
+
if (subAgentManager) {
|
|
1086
|
+
subAgentManager.unregisterPlugin(name);
|
|
1087
|
+
log.debug(`[${this.name}] Unregistered agent: ${name}`);
|
|
1088
|
+
}
|
|
1089
|
+
} catch (err) {
|
|
1090
|
+
log.warn(`[${this.name}] Failed to unregister agent '${name}':`, err.message);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
this._registeredAgents = [];
|
|
1094
|
+
|
|
1095
|
+
// 清理通过 prompt.register 注册的 prompt parts
|
|
1096
|
+
for (const { name } of this._registeredPrompts) {
|
|
1097
|
+
try {
|
|
1098
|
+
const reg = framework.prompts;
|
|
1099
|
+
if (reg) {
|
|
1100
|
+
reg.clearOwner(this.name, name);
|
|
1101
|
+
log.debug(`[${this.name}] Unregistered prompt: ${name}`);
|
|
1102
|
+
}
|
|
1103
|
+
} catch (err) {
|
|
1104
|
+
log.warn(`[${this.name}] Failed to unregister prompt '${name}':`, err.message);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
this._registeredPrompts = [];
|
|
1108
|
+
|
|
1109
|
+
// 清理通过 skill.register 注册的技能
|
|
1110
|
+
for (const { name } of this._registeredSkills) {
|
|
1111
|
+
try {
|
|
1112
|
+
const sm = framework.pluginManager?.get('skill-manager');
|
|
1113
|
+
if (sm) {
|
|
1114
|
+
sm.remove(name);
|
|
1115
|
+
log.debug(`[${this.name}] Unregistered skill: ${name}`);
|
|
1116
|
+
}
|
|
1117
|
+
} catch (err) {
|
|
1118
|
+
log.warn(`[${this.name}] Failed to unregister skill '${name}':`, err.message);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
this._registeredSkills = [];
|
|
1122
|
+
|
|
220
1123
|
for (const plugin of this._subAgents) {
|
|
221
1124
|
try {
|
|
222
1125
|
plugin.uninstall(framework);
|