foliko 2.0.4 → 2.0.5
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/.editorconfig +56 -56
- package/.lintstagedrc +7 -7
- package/.prettierignore +29 -29
- package/.prettierrc +11 -11
- package/Dockerfile +63 -63
- package/docs/public-api.md +924 -6
- package/install.ps1 +129 -129
- package/install.sh +121 -121
- package/package.json +1 -1
- package/plugins/core/default/bootstrap.js +21 -0
- package/plugins/core/skill-manager/index.js +158 -0
- package/plugins/core/workflow/index.js +24 -0
- package/skills/find-skills/SKILL.md +133 -133
- package/src/agent/prompt-registry.js +95 -89
- package/src/framework/framework.js +616 -2
- package/src/plugin/base.js +5 -5
- package/tests/core/plugin-prompts.test.js +13 -13
- package/tests/core/prompt-registry.test.js +59 -51
package/docs/public-api.md
CHANGED
|
@@ -113,12 +113,12 @@ framework.tools.execute(name, args);
|
|
|
113
113
|
|
|
114
114
|
// 扩展工具
|
|
115
115
|
framework.registerExtensionTools(pluginName, tools);
|
|
116
|
-
framework.
|
|
117
|
-
framework.
|
|
118
|
-
framework.
|
|
119
|
-
framework.
|
|
120
|
-
framework.
|
|
121
|
-
framework.
|
|
116
|
+
framework.extensions.tool(pluginName, tools);
|
|
117
|
+
framework.extensions.execute(plugin, tool, args);
|
|
118
|
+
framework.extensions.list();
|
|
119
|
+
framework.extensions.listPlugins();
|
|
120
|
+
framework.extensions.listTools(pluginName);
|
|
121
|
+
framework.extensions.getTool(pluginName, toolName);
|
|
122
122
|
|
|
123
123
|
// 事件
|
|
124
124
|
framework.on('email:received', handler);
|
|
@@ -195,6 +195,924 @@ module.exports = function (foliko) {
|
|
|
195
195
|
| `foliko.z` | Zod schema 创建 |
|
|
196
196
|
| `foliko.Plugin` | Plugin 基类 |
|
|
197
197
|
|
|
198
|
+
## 插件开发 API
|
|
199
|
+
|
|
200
|
+
在 `install(framework)` / `start(framework)` / `reload(framework)` 生命周期方法中,可通过 `framework` 参数使用以下 API:
|
|
201
|
+
|
|
202
|
+
### framework.plugins
|
|
203
|
+
|
|
204
|
+
```js
|
|
205
|
+
// 检查插件是否存在
|
|
206
|
+
framework.plugins.has('my-plugin'); // true/false
|
|
207
|
+
|
|
208
|
+
// 获取插件实例
|
|
209
|
+
framework.plugins.get('my-plugin'); // Plugin 实例
|
|
210
|
+
|
|
211
|
+
// 列出所有已加载的插件名
|
|
212
|
+
framework.plugins.list(); // ['ai', 'shell', 'telegram', ...]
|
|
213
|
+
|
|
214
|
+
// 创建 Plugin 实例(不注册/加载)
|
|
215
|
+
// 支持四种插件定义形式
|
|
216
|
+
const plugin = framework.plugins.create(pluginDef);
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
#### createPluginInstance 支持的形式
|
|
220
|
+
|
|
221
|
+
```js
|
|
222
|
+
// 1. Plugin 实例(直接返回)
|
|
223
|
+
const plugin = framework.plugins.create(existingPluginInstance);
|
|
224
|
+
|
|
225
|
+
// 2. Plugin 子类(自动实例化)
|
|
226
|
+
const plugin = framework.plugins.create(class MyPlugin extends Plugin {
|
|
227
|
+
name = 'my-plugin';
|
|
228
|
+
// ...
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// 3. function(foliko) 函数(包装为 Plugin)
|
|
232
|
+
const plugin = framework.plugins.create(function(foliko) {
|
|
233
|
+
foliko.tools.tool({ name: 'hello', execute: () => 'hi' });
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// 4. 普通对象(拷贝字段)
|
|
237
|
+
const plugin = framework.plugins.create({
|
|
238
|
+
name: 'my-plugin',
|
|
239
|
+
version: '1.0.0',
|
|
240
|
+
install(framework) { /* ... */ },
|
|
241
|
+
start(framework) { /* ... */ },
|
|
242
|
+
});
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
#### plugins.register — 注册并加载插件
|
|
246
|
+
|
|
247
|
+
```js
|
|
248
|
+
// 注册并自动加载(install + start)
|
|
249
|
+
await framework.plugins.register({
|
|
250
|
+
name: 'my-plugin',
|
|
251
|
+
version: '1.0.0',
|
|
252
|
+
install(fw) { /* 初始化 */ },
|
|
253
|
+
start(fw) { /* 启动 */ },
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// 仅创建实例,不自动加载
|
|
257
|
+
await framework.plugins.register(myPluginDef, { autoLoad: false });
|
|
258
|
+
|
|
259
|
+
// 注册后手动加载
|
|
260
|
+
await framework.plugins.register(myPluginDef, { autoLoad: false });
|
|
261
|
+
await framework.loadPlugin(myPluginDef);
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
265
|
+
|------|------|--------|------|
|
|
266
|
+
| `autoLoad` | boolean | `true` | 是否自动调用 `install` + `start` |
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
### framework.prompts
|
|
271
|
+
|
|
272
|
+
`framework.prompts` 是全局系统提示词注册表,简化设计:只用 **priority 优先级** + **scope 作用域**。
|
|
273
|
+
|
|
274
|
+
```js
|
|
275
|
+
// 注册 prompt
|
|
276
|
+
framework.prompts.register(owner, name, provider, options);
|
|
277
|
+
|
|
278
|
+
// 从文件注册(支持变量插值)
|
|
279
|
+
framework.prompts.registerFile('my-plugin', './system.md', { name: 'Alice', version: '1.0' });
|
|
280
|
+
|
|
281
|
+
// 注销
|
|
282
|
+
framework.prompts.unregister('my-plugin', 'my-rules');
|
|
283
|
+
|
|
284
|
+
// 清空 owner 所有 parts
|
|
285
|
+
framework.prompts.clearOwner('my-plugin');
|
|
286
|
+
|
|
287
|
+
// 检查存在
|
|
288
|
+
framework.prompts.has('my-plugin', 'my-rules'); // true/false
|
|
289
|
+
|
|
290
|
+
// 获取 part
|
|
291
|
+
framework.prompts.getPart('my-plugin', 'my-rules');
|
|
292
|
+
|
|
293
|
+
// 列出所有(按 priority 排序)
|
|
294
|
+
framework.prompts.list();
|
|
295
|
+
|
|
296
|
+
// 按 scope 过滤
|
|
297
|
+
framework.prompts.listByScope('global');
|
|
298
|
+
|
|
299
|
+
// 获取适用于指定 agent 的 parts
|
|
300
|
+
framework.prompts.listForAgent('sub-agent-1');
|
|
301
|
+
|
|
302
|
+
// 渲染(适用于当前调用者)
|
|
303
|
+
framework.prompts.build();
|
|
304
|
+
framework.prompts.preview(); // build() 别名
|
|
305
|
+
|
|
306
|
+
// 渲染适用于特定 agent
|
|
307
|
+
framework.prompts.buildForAgent('sub-agent-1');
|
|
308
|
+
|
|
309
|
+
// 结构化检查(调试用)
|
|
310
|
+
framework.prompts.inspect();
|
|
311
|
+
// [{ owner, name, scope, priority, hasContent, length, contentPreview, ... }]
|
|
312
|
+
|
|
313
|
+
// 失效(下次 build 重新计算)
|
|
314
|
+
framework.prompts.invalidate('my-plugin', 'my-rules');
|
|
315
|
+
framework.prompts.invalidateAll();
|
|
316
|
+
|
|
317
|
+
// 数量
|
|
318
|
+
framework.prompts.count();
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
#### 选项
|
|
322
|
+
|
|
323
|
+
| 选项 | 默认值 | 说明 |
|
|
324
|
+
|------|--------|------|
|
|
325
|
+
| `scope` | `'global'` | 作用域:`'global'`(公用)\| `'main'`(主 agent)\| agentId |
|
|
326
|
+
| `priority` | `100` | 优先级,数字越小越靠前 |
|
|
327
|
+
| `description` | `''` | 描述(用于调试)|
|
|
328
|
+
|
|
329
|
+
#### 注册示例
|
|
330
|
+
|
|
331
|
+
```js
|
|
332
|
+
// 在插件 install() 中注册
|
|
333
|
+
install(framework) {
|
|
334
|
+
// 公用 prompt(所有 agent 可用)
|
|
335
|
+
framework.prompts.register('my-plugin', 'rules', () => {
|
|
336
|
+
return '## 我的规则\n1. 规则一\n2. 规则二';
|
|
337
|
+
}, {
|
|
338
|
+
scope: 'global',
|
|
339
|
+
priority: 50,
|
|
340
|
+
description: '我的规则',
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
// 主 agent 专用
|
|
344
|
+
framework.prompts.register('my-plugin', 'main-only', () => {
|
|
345
|
+
return '仅主 agent 可见的内容';
|
|
346
|
+
}, {
|
|
347
|
+
scope: 'main',
|
|
348
|
+
priority: 30,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// 特定子 agent 专用
|
|
352
|
+
framework.prompts.register('my-plugin', 'sub-agent-rules', () => {
|
|
353
|
+
return '子 agent 专用规则';
|
|
354
|
+
}, {
|
|
355
|
+
scope: 'sub-agent-1',
|
|
356
|
+
priority: 40,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// 动态内容(带缓存)
|
|
360
|
+
framework.prompts.register('my-plugin', 'dynamic', () => {
|
|
361
|
+
return `当前连接数: ${this._getConnectionCount()}`;
|
|
362
|
+
}, {
|
|
363
|
+
priority: 60,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// 从文件注册(支持变量插值)
|
|
367
|
+
// 文件内容如:# System {{name}} v{{version}}
|
|
368
|
+
framework.prompts.registerFile('my-plugin', './system.md', {
|
|
369
|
+
name: 'MyApp',
|
|
370
|
+
version: '2.0',
|
|
371
|
+
}, {
|
|
372
|
+
scope: 'global',
|
|
373
|
+
priority: 10,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
#### provider 规范
|
|
379
|
+
|
|
380
|
+
- 返回 `string` — 正常内容
|
|
381
|
+
- 返回 `null` / 空字符串 — build 时被跳过
|
|
382
|
+
- 抛出异常 — 警告但不中断渲染
|
|
383
|
+
|
|
384
|
+
---
|
|
385
|
+
|
|
386
|
+
### framework.skills
|
|
387
|
+
|
|
388
|
+
`framework.skills` 是技能管理器接口(需要加载 `skill-manager` 插件)。提供技能的查询、命令注册、reference 和 scripts 管理。
|
|
389
|
+
|
|
390
|
+
**注意**:需要先加载 `skill-manager` 插件才能使用:
|
|
391
|
+
|
|
392
|
+
```js
|
|
393
|
+
const { SkillManagerPlugin } = require('foliko');
|
|
394
|
+
await framework.loadPlugin(new SkillManagerPlugin({ skillsDir: './skills' }));
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
#### 基本查询
|
|
398
|
+
|
|
399
|
+
```js
|
|
400
|
+
// 检查 skills 模块是否可用
|
|
401
|
+
framework.skills.available; // true/false
|
|
402
|
+
|
|
403
|
+
// 列出所有已加载的技能
|
|
404
|
+
framework.skills.list();
|
|
405
|
+
// [{ name, metadata, content, path, references, scripts, fromPlugin }, ...]
|
|
406
|
+
|
|
407
|
+
// 获取单个技能
|
|
408
|
+
framework.skills.get('my-skill');
|
|
409
|
+
// { name, metadata, content, path, references, scripts, fromPlugin }
|
|
410
|
+
|
|
411
|
+
// 检查技能是否存在
|
|
412
|
+
framework.skills.has('my-skill'); // true/false
|
|
413
|
+
|
|
414
|
+
// 获取技能详细信息(包含完整 metadata)
|
|
415
|
+
framework.skills.details('my-skill');
|
|
416
|
+
// {
|
|
417
|
+
// name: 'my-skill',
|
|
418
|
+
// description: '技能描述',
|
|
419
|
+
// content: '技能内容(markdown)',
|
|
420
|
+
// path: '/path/to/skill',
|
|
421
|
+
// metadata: SkillMetadata 实例,
|
|
422
|
+
// references: ['ref1', 'ref2'],
|
|
423
|
+
// scripts: ['script1.sh', 'script2.js'],
|
|
424
|
+
// fromPlugin: false
|
|
425
|
+
// }
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
#### 注册 Skill(程序化)
|
|
429
|
+
|
|
430
|
+
```js
|
|
431
|
+
// 注册一个新 skill(不依赖文件)
|
|
432
|
+
const skill = framework.skills.register('my-skill', '# 我的技能\n\n这是技能内容', {
|
|
433
|
+
description: '技能描述',
|
|
434
|
+
allowedTools: ['Read', 'Write'],
|
|
435
|
+
}, {
|
|
436
|
+
commands: [
|
|
437
|
+
{
|
|
438
|
+
name: 'hello',
|
|
439
|
+
description: '打招呼',
|
|
440
|
+
options: [
|
|
441
|
+
{ flags: '-n, --name <value>', description: '名字', defaultValue: 'World' }
|
|
442
|
+
],
|
|
443
|
+
execute: async (args, ctx) => {
|
|
444
|
+
return `Hello ${args.name}!`;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
]
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// 返回注册的 skill 对象
|
|
451
|
+
// { name, metadata, content, instance, path, references, scripts, fromPlugin, registered }
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
| 参数 | 类型 | 说明 |
|
|
455
|
+
|------|------|------|
|
|
456
|
+
| `name` | string | 技能名称(唯一标识) |
|
|
457
|
+
| `content` | string | 技能内容(markdown 格式) |
|
|
458
|
+
| `metadata` | object | 可选,元数据 `{ description, allowedTools, license, compatibility }` |
|
|
459
|
+
| `options.commands` | array | 可选,命令定义数组 |
|
|
460
|
+
| `options.path` | string | 可选,技能路径(用于扫描 references/scripts) |
|
|
461
|
+
|
|
462
|
+
#### 移除 Skill
|
|
463
|
+
|
|
464
|
+
```js
|
|
465
|
+
// 移除已注册的程序化 skill
|
|
466
|
+
framework.skills.remove('my-skill'); // true/false
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
**注意**:只能移除程序化注册的 skill(通过 `framework.skills.register()` 创建),从文件加载的 skill 无法通过此方法移除。
|
|
470
|
+
|
|
471
|
+
#### 重载技能
|
|
472
|
+
|
|
473
|
+
```js
|
|
474
|
+
// 重载所有技能(重新扫描 skills 目录)
|
|
475
|
+
framework.skills.reload();
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
#### 命令管理(Commands)
|
|
479
|
+
|
|
480
|
+
技能可以注册命令供 LLM 调用,命令格式为 `{skill}:{cmd}`:
|
|
481
|
+
|
|
482
|
+
```js
|
|
483
|
+
// 列出技能的所有命令
|
|
484
|
+
framework.skills.listCommands('my-skill');
|
|
485
|
+
// [{ name: 'cmd1', description: '...', options: [...] }, ...]
|
|
486
|
+
|
|
487
|
+
// 获取单个命令详情
|
|
488
|
+
framework.skills.getCommand('my-skill', 'run');
|
|
489
|
+
// { name: 'my-skill:run', description: '...', options: [...], inputSchema: {...} }
|
|
490
|
+
|
|
491
|
+
// 调用命令(通过 extension 执行)
|
|
492
|
+
await framework.extensions.execute('skill', 'my-skill:run', { command: '--arg value' });
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
#### Reference 管理(按需加载的附加文档)
|
|
496
|
+
|
|
497
|
+
Reference 文件放在技能的 `references/` 子目录下:
|
|
498
|
+
|
|
499
|
+
```js
|
|
500
|
+
// 列出可用的 reference 文件
|
|
501
|
+
framework.skills.listReferences('my-skill'); // ['api-docs', 'guide']
|
|
502
|
+
|
|
503
|
+
// 获取 reference 文件内容(按需加载)
|
|
504
|
+
framework.skills.getReference('my-skill', 'api-docs');
|
|
505
|
+
// 返回文件内容(不含 .md 后缀)
|
|
506
|
+
```
|
|
507
|
+
|
|
508
|
+
#### Scripts 管理
|
|
509
|
+
|
|
510
|
+
Scripts 文件放在技能的 `scripts/` 子目录下:
|
|
511
|
+
|
|
512
|
+
```js
|
|
513
|
+
// 列出所有脚本
|
|
514
|
+
framework.skills.listScripts('my-skill');
|
|
515
|
+
// [{ name: 'deploy.sh', path: '/path/to/deploy.sh', isExecutable: true }, ...]
|
|
516
|
+
|
|
517
|
+
// 获取脚本内容
|
|
518
|
+
framework.skills.getScript('my-skill', 'deploy.sh');
|
|
519
|
+
// 返回脚本文件内容
|
|
520
|
+
```
|
|
521
|
+
|
|
522
|
+
#### Skills 工具(内置工具)
|
|
523
|
+
|
|
524
|
+
`skill-manager` 插件注册了以下内置工具:
|
|
525
|
+
|
|
526
|
+
| 工具 | 说明 |
|
|
527
|
+
|------|------|
|
|
528
|
+
| `skill_load` | 加载指定技能,获取技能内容和命令文档 |
|
|
529
|
+
| `skill_reload` | 重载所有技能 |
|
|
530
|
+
| `skill_load_reference` | 加载技能的附加参考文档 |
|
|
531
|
+
| `skill_list_scripts` | 列出技能的所有脚本 |
|
|
532
|
+
| `skill_load_script` | 读取脚本文件内容 |
|
|
533
|
+
|
|
534
|
+
---
|
|
535
|
+
|
|
536
|
+
### 创建新 Skill
|
|
537
|
+
|
|
538
|
+
Skill 存放位置有两种方式:
|
|
539
|
+
|
|
540
|
+
#### 方式一:`.foliko/skills/` 目录
|
|
541
|
+
|
|
542
|
+
```
|
|
543
|
+
项目目录/
|
|
544
|
+
├── .foliko/
|
|
545
|
+
│ └── skills/
|
|
546
|
+
│ └── my-skill/ # 技能文件夹
|
|
547
|
+
│ ├── SKILL.md # 必需,技能定义文件
|
|
548
|
+
│ ├── index.js # 可选,命令定义
|
|
549
|
+
│ ├── references/ # 可选,附加文档
|
|
550
|
+
│ └── scripts/ # 可选,脚本文件
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
#### 方式二:插件目录下的 SKILL.md
|
|
554
|
+
|
|
555
|
+
```
|
|
556
|
+
项目目录/
|
|
557
|
+
└── plugins/
|
|
558
|
+
└── my-plugin/
|
|
559
|
+
├── index.js
|
|
560
|
+
└── SKILL.md # 自动加载为技能
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
#### 1. 创建 SKILL.md
|
|
564
|
+
|
|
565
|
+
```markdown
|
|
566
|
+
---
|
|
567
|
+
name: my-skill
|
|
568
|
+
description: 这是一个自定义技能的描述。当用户说"..."时调用此技能。
|
|
569
|
+
allowed-tools: Read, Write, Glob, Grep
|
|
570
|
+
license: MIT
|
|
571
|
+
compatibility: v2.0.0
|
|
572
|
+
---
|
|
573
|
+
|
|
574
|
+
# 我的技能
|
|
575
|
+
|
|
576
|
+
这里是技能的详细说明内容。
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
**frontmatter 字段:**
|
|
580
|
+
|
|
581
|
+
| 字段 | 必需 | 说明 |
|
|
582
|
+
|------|------|------|
|
|
583
|
+
| `name` | 是 | 技能唯一标识,1-64字符,字母数字、下划线、横杠 |
|
|
584
|
+
| `description` | 是 | 技能描述,用于意图匹配 |
|
|
585
|
+
| `allowed-tools` | 否 | 允许使用的工具列表 |
|
|
586
|
+
| `license` | 否 | 许可证 |
|
|
587
|
+
| `compatibility` | 否 | 兼容版本 |
|
|
588
|
+
|
|
589
|
+
#### 2. 添加命令(可选)
|
|
590
|
+
|
|
591
|
+
在技能目录下创建 `index.js`:
|
|
592
|
+
|
|
593
|
+
```javascript
|
|
594
|
+
module.exports = [
|
|
595
|
+
{
|
|
596
|
+
name: 'hello',
|
|
597
|
+
description: '打招呼命令',
|
|
598
|
+
options: [
|
|
599
|
+
{ flags: '-n, --name <value>', description: '名字', defaultValue: 'World' }
|
|
600
|
+
],
|
|
601
|
+
execute: async (args, ctx) => {
|
|
602
|
+
return `Hello ${args.name}!`;
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
name: 'status',
|
|
607
|
+
description: '查看状态',
|
|
608
|
+
execute: async (args, ctx) => {
|
|
609
|
+
return JSON.stringify({ session: ctx.sessionId });
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
];
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
**命令调用方式:**
|
|
616
|
+
|
|
617
|
+
```js
|
|
618
|
+
// 通过 extension 执行
|
|
619
|
+
await framework.extensions.execute('skill', 'my-skill:hello', { command: '-n Alice' });
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
#### 3. 添附加文档(可选)
|
|
623
|
+
|
|
624
|
+
在 `references/` 目录下放 `.md` 文件:
|
|
625
|
+
|
|
626
|
+
```
|
|
627
|
+
my-skill/
|
|
628
|
+
├── SKILL.md
|
|
629
|
+
└── references/
|
|
630
|
+
├── api.md
|
|
631
|
+
└── guide.md
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
使用 `skill_load_reference` 工具加载:
|
|
635
|
+
```js
|
|
636
|
+
framework.skills.getReference('my-skill', 'api'); // 返回 api.md 内容
|
|
637
|
+
```
|
|
638
|
+
|
|
639
|
+
#### 4. 重载技能
|
|
640
|
+
|
|
641
|
+
创建或修改技能后,调用 `skill_reload` 工具重载:
|
|
642
|
+
|
|
643
|
+
```js
|
|
644
|
+
await framework.executeTool('skill_reload', {});
|
|
645
|
+
|
|
646
|
+
// 或通过 API
|
|
647
|
+
const skillManager = framework.pluginManager.get('skill-manager');
|
|
648
|
+
await skillManager.reload(framework);
|
|
649
|
+
```
|
|
650
|
+
|
|
651
|
+
#### 完整示例
|
|
652
|
+
|
|
653
|
+
创建 `skills/my-skill/` 目录:
|
|
654
|
+
|
|
655
|
+
```
|
|
656
|
+
skills/my-skill/
|
|
657
|
+
├── SKILL.md
|
|
658
|
+
├── index.js
|
|
659
|
+
└── references/
|
|
660
|
+
└── usage.md
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
**SKILL.md:**
|
|
664
|
+
```markdown
|
|
665
|
+
---
|
|
666
|
+
name: my-skill
|
|
667
|
+
description: 我的自定义技能
|
|
668
|
+
---
|
|
669
|
+
|
|
670
|
+
# 我的自定义技能
|
|
671
|
+
|
|
672
|
+
这是一个示例技能,用于...
|
|
673
|
+
|
|
674
|
+
## 使用方法
|
|
675
|
+
|
|
676
|
+
1. 第一步
|
|
677
|
+
2. 第二步
|
|
678
|
+
```
|
|
679
|
+
|
|
680
|
+
**index.js:**
|
|
681
|
+
```javascript
|
|
682
|
+
module.exports = [
|
|
683
|
+
{
|
|
684
|
+
name: 'run',
|
|
685
|
+
description: '执行任务',
|
|
686
|
+
options: [
|
|
687
|
+
{ flags: '-t, --task <value>', description: '任务名', required: true }
|
|
688
|
+
],
|
|
689
|
+
execute: async (args, ctx) => {
|
|
690
|
+
return `Running: ${args.task}`;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
];
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
---
|
|
697
|
+
|
|
698
|
+
### framework.workflows
|
|
699
|
+
|
|
700
|
+
`framework.workflows` 是工作流引擎接口(需要加载 `workflow` 插件)。用于定义和执行结构化工作流。
|
|
701
|
+
|
|
702
|
+
**注意**:需要先加载 `workflow` 插件才能使用:
|
|
703
|
+
|
|
704
|
+
```js
|
|
705
|
+
const { WorkflowPlugin } = require('foliko');
|
|
706
|
+
await framework.loadPlugin(new WorkflowPlugin({ workflowsDir: '.foliko/workflows' }));
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
#### 基本查询
|
|
710
|
+
|
|
711
|
+
```js
|
|
712
|
+
// 检查 workflows 模块是否可用
|
|
713
|
+
framework.workflows.available; // true/false
|
|
714
|
+
|
|
715
|
+
// 列出所有已加载的工作流
|
|
716
|
+
framework.workflows.list();
|
|
717
|
+
// [{ name, description, stepCount, type }, ...]
|
|
718
|
+
|
|
719
|
+
// 获取单个工作流定义
|
|
720
|
+
framework.workflows.get('my-workflow');
|
|
721
|
+
// { name, description, steps: [...], type }
|
|
722
|
+
|
|
723
|
+
// 检查工作流是否存在
|
|
724
|
+
framework.workflows.has('my-workflow'); // true/false
|
|
725
|
+
|
|
726
|
+
// 获取工作流引擎(用于创建步骤)
|
|
727
|
+
framework.workflows.getEngine(); // WorkflowEngine 实例
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
#### 执行工作流
|
|
731
|
+
|
|
732
|
+
```js
|
|
733
|
+
// 执行工作流(通过名称/JSON/JS 代码)
|
|
734
|
+
const result = await framework.workflows.execute('my-workflow', { input: 'value' });
|
|
735
|
+
|
|
736
|
+
// 直接传入工作流定义
|
|
737
|
+
const result = await framework.workflows.execute({
|
|
738
|
+
name: 'my-workflow',
|
|
739
|
+
description: '我的工作流',
|
|
740
|
+
steps: [
|
|
741
|
+
{ id: 'step1', tool: 'some_tool', args: { msg: 'hello' } },
|
|
742
|
+
{ id: 'step2', tool: 'another_tool', args: { msg: '{{step1.result}}' } },
|
|
743
|
+
]
|
|
744
|
+
}, { input: 'value' });
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
#### 注册工作流(程序化)
|
|
748
|
+
|
|
749
|
+
```js
|
|
750
|
+
// 注册工作流定义
|
|
751
|
+
const result = framework.workflows.register('greet', {
|
|
752
|
+
description: '打招呼工作流',
|
|
753
|
+
steps: [
|
|
754
|
+
{ tool: 'echo', args: { message: 'Hello {{input.name}}' } }
|
|
755
|
+
]
|
|
756
|
+
});
|
|
757
|
+
// { success: true, data: { name: 'greet', toolName: 'workflow_greet' } }
|
|
758
|
+
|
|
759
|
+
// 移除工作流
|
|
760
|
+
framework.workflows.remove('greet'); // true/false
|
|
761
|
+
|
|
762
|
+
// 重载工作流
|
|
763
|
+
framework.workflows.reload();
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
#### 工作流步骤类型
|
|
767
|
+
|
|
768
|
+
| 类型 | 说明 | 示例 |
|
|
769
|
+
|------|------|------|
|
|
770
|
+
| `tool` | 执行工具 | `{ tool: 'my_tool', args: { ... } }` |
|
|
771
|
+
| `script` | 执行脚本 | `{ script: 'return input * 2' }` |
|
|
772
|
+
| `condition` | 条件分支 | `{ branches: [{ condition: 'x > 0', steps: [...] }] }` |
|
|
773
|
+
| `switch` | 多值分支 | `{ value: '{{status}}', branches: [...] }` |
|
|
774
|
+
| `try` | 异常捕获 | `{ try: { steps: [...] }, catch: { steps: [...] } }` |
|
|
775
|
+
| `parallel` | 并行执行 | `{ parallel: true, steps: [...] }` |
|
|
776
|
+
| `loop` | 循环执行 | `{ loop: { maxIterations: 10, steps: [...] } }` |
|
|
777
|
+
| `delay` | 延迟等待 | `{ delayMs: 1000 }` |
|
|
778
|
+
| `workflow` | 嵌套工作流 | `{ workflow: subWorkflowDef }` |
|
|
779
|
+
|
|
780
|
+
#### 工作流工具(内置工具)
|
|
781
|
+
|
|
782
|
+
| 工具 | 说明 |
|
|
783
|
+
|------|------|
|
|
784
|
+
| `execute_workflow` | 执行指定的工作流定义 |
|
|
785
|
+
| `workflow_list` | 列出所有已加载的工作流 |
|
|
786
|
+
| `workflow_reload` | 重载所有工作流 |
|
|
787
|
+
|
|
788
|
+
---
|
|
789
|
+
|
|
790
|
+
### framework.rules
|
|
791
|
+
|
|
792
|
+
`framework.rules` 是规则引擎接口(需要加载 `rules` 插件)。用于控制工具调用权限、内容过滤、触发动作。
|
|
793
|
+
|
|
794
|
+
**注意**:需要先加载 `rules` 插件才能使用:
|
|
795
|
+
|
|
796
|
+
```js
|
|
797
|
+
const RulesPlugin = require('foliko').RulesPlugin || require('foliko/plugins/core/rules');
|
|
798
|
+
await framework.loadPlugin(new RulesPlugin({ rulesDir: '.foliko/rules' }));
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
#### 基本操作
|
|
802
|
+
|
|
803
|
+
```js
|
|
804
|
+
// 检查 rules 模块是否可用
|
|
805
|
+
framework.rules.available; // true/false
|
|
806
|
+
|
|
807
|
+
// 列出所有规则
|
|
808
|
+
framework.rules.list();
|
|
809
|
+
// [{ name, type, target, description, enabled }, ...]
|
|
810
|
+
|
|
811
|
+
// 添加规则
|
|
812
|
+
framework.rules.add({
|
|
813
|
+
name: 'deny-shell',
|
|
814
|
+
type: 'deny', // 'allow' | 'deny' | 'transform'
|
|
815
|
+
target: 'shell_execute', // 工具名或 '*'
|
|
816
|
+
pattern: 'rm -rf', // 可选,正则表达式
|
|
817
|
+
description: '禁止危险 shell 命令',
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
// 移除规则
|
|
821
|
+
framework.rules.remove('deny-shell'); // true/false
|
|
822
|
+
|
|
823
|
+
// 获取规则统计
|
|
824
|
+
framework.rules.stats();
|
|
825
|
+
// { total, allowed, denied, transformed }
|
|
826
|
+
|
|
827
|
+
// 重载规则
|
|
828
|
+
framework.rules.reload();
|
|
829
|
+
```
|
|
830
|
+
|
|
831
|
+
#### 规则检查
|
|
832
|
+
|
|
833
|
+
```js
|
|
834
|
+
// 测试规则匹配
|
|
835
|
+
const result = framework.rules.test('shell_execute', { command: 'ls' });
|
|
836
|
+
// { allowed: true, ruleName: null, ruleType: null }
|
|
837
|
+
|
|
838
|
+
// 检查工具调用
|
|
839
|
+
const result = framework.rules.checkToolCall('my_tool', { arg: 'value' });
|
|
840
|
+
// { allowed: true, rule: Rule | null }
|
|
841
|
+
|
|
842
|
+
// 检查消息
|
|
843
|
+
const result = framework.rules.checkMessage('some message', { context: 'info' });
|
|
844
|
+
// { allowed: true, rule: Rule | null }
|
|
845
|
+
```
|
|
846
|
+
|
|
847
|
+
#### 规则类型
|
|
848
|
+
|
|
849
|
+
| 类型 | 说明 | 用途 |
|
|
850
|
+
|------|------|------|
|
|
851
|
+
| `allow` | 允许 | 白名单,放行匹配的调用 |
|
|
852
|
+
| `deny` | 拒绝 | 黑名单,阻止匹配的调用 |
|
|
853
|
+
| `transform` | 转换 | 修改输入参数后放行 |
|
|
854
|
+
|
|
855
|
+
#### 规则工具(内置工具)
|
|
856
|
+
|
|
857
|
+
| 工具 | 说明 |
|
|
858
|
+
|------|------|
|
|
859
|
+
| `rules_list` | 列出所有规则 |
|
|
860
|
+
| `rules_add` | 添加规则 |
|
|
861
|
+
| `rules_remove` | 移除规则 |
|
|
862
|
+
| `rules_stats` | 获取规则统计 |
|
|
863
|
+
| `rules_test` | 测试规则匹配 |
|
|
864
|
+
|
|
865
|
+
---
|
|
866
|
+
|
|
867
|
+
### framework.storage
|
|
868
|
+
|
|
869
|
+
`framework.storage` 是键值存储接口(需要加载 `storage` 插件)。提供 JSONL 格式的持久化存储,支持自动 compaction。
|
|
870
|
+
|
|
871
|
+
**注意**:需要先加载 `storage` 插件才能使用:
|
|
872
|
+
|
|
873
|
+
```js
|
|
874
|
+
const { StoragePlugin } = require('foliko');
|
|
875
|
+
await framework.loadPlugin(new StoragePlugin({ path: '.foliko/data', namespace: 'default' }));
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
#### 基本操作
|
|
879
|
+
|
|
880
|
+
```js
|
|
881
|
+
// 检查 storage 模块是否可用
|
|
882
|
+
framework.storage.available; // true/false
|
|
883
|
+
|
|
884
|
+
// 设置值
|
|
885
|
+
framework.storage.set('my-key', { data: 'value' }, 'namespace'); // namespace 可选
|
|
886
|
+
|
|
887
|
+
// 获取值
|
|
888
|
+
framework.storage.get('my-key', 'namespace'); // null 或值
|
|
889
|
+
|
|
890
|
+
// 删除键(软删除)
|
|
891
|
+
framework.storage.delete('my-key', 'namespace');
|
|
892
|
+
|
|
893
|
+
// 列出命名空间下的所有键
|
|
894
|
+
framework.storage.list('namespace'); // ['key1', 'key2', ...]
|
|
895
|
+
|
|
896
|
+
// 清空命名空间
|
|
897
|
+
framework.storage.clear('namespace'); // { cleared: 10 }
|
|
898
|
+
|
|
899
|
+
// 获取存储统计
|
|
900
|
+
framework.storage.stats();
|
|
901
|
+
// { entryCount, tombstoneCount, fileSize, ... }
|
|
902
|
+
|
|
903
|
+
// 手动触发 compaction
|
|
904
|
+
framework.storage.compact();
|
|
905
|
+
|
|
906
|
+
// 获取底层 StorageManager
|
|
907
|
+
framework.storage.getStore();
|
|
908
|
+
```
|
|
909
|
+
|
|
910
|
+
#### Storage 工具(内置工具)
|
|
911
|
+
|
|
912
|
+
| 工具 | 说明 |
|
|
913
|
+
|------|------|
|
|
914
|
+
| `storage_set` | 存储数据 |
|
|
915
|
+
| `storage_get` | 获取数据 |
|
|
916
|
+
| `storage_delete` | 删除数据 |
|
|
917
|
+
| `storage_list` | 列出键 |
|
|
918
|
+
| `storage_clear` | 清空数据 |
|
|
919
|
+
| `storage_watch` | 监听键值变化 |
|
|
920
|
+
| `storage_compact` | 手动触发 compaction |
|
|
921
|
+
| `storage_stats` | 获取统计信息 |
|
|
922
|
+
|
|
923
|
+
---
|
|
924
|
+
|
|
925
|
+
### framework.sessions
|
|
926
|
+
|
|
927
|
+
`framework.sessions` 是会话管理接口,提供会话的查询和操作。
|
|
928
|
+
|
|
929
|
+
```js
|
|
930
|
+
// 列出所有会话 ID
|
|
931
|
+
framework.sessions.list(); // ['session1', 'session2', ...]
|
|
932
|
+
|
|
933
|
+
// 获取会话上下文
|
|
934
|
+
framework.sessions.get('session-id'); // SessionManager 或 null
|
|
935
|
+
|
|
936
|
+
// 创建或获取会话
|
|
937
|
+
framework.sessions.getOrCreate('session-id', options);
|
|
938
|
+
|
|
939
|
+
// 销毁会话
|
|
940
|
+
framework.sessions.destroy('session-id');
|
|
941
|
+
|
|
942
|
+
// 获取当前会话 ID
|
|
943
|
+
framework.sessions.currentId(); // 'session-id' 或 null
|
|
944
|
+
|
|
945
|
+
// 在会话中执行代码
|
|
946
|
+
await framework.sessions.runIn('session-id', options, async () => {
|
|
947
|
+
// 在此作用域内可通过 framework.getCurrentSessionContext() 获取会话
|
|
948
|
+
});
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
---
|
|
952
|
+
|
|
953
|
+
### framework.mcps
|
|
954
|
+
|
|
955
|
+
`framework.mcps` 是 MCP (Model Context Protocol) 服务器管理接口(需要加载 `mcp` 插件)。
|
|
956
|
+
|
|
957
|
+
**注意**:需要先加载 `mcp` 插件才能使用:
|
|
958
|
+
|
|
959
|
+
```js
|
|
960
|
+
const { MCPExecutorPlugin } = require('foliko');
|
|
961
|
+
await framework.loadPlugin(new MCPExecutorPlugin({
|
|
962
|
+
servers: [
|
|
963
|
+
{ name: 'my-server', command: 'npx', args: ['-y', 'some-mcp-server'] }
|
|
964
|
+
]
|
|
965
|
+
}));
|
|
966
|
+
```
|
|
967
|
+
|
|
968
|
+
#### 基本操作
|
|
969
|
+
|
|
970
|
+
```js
|
|
971
|
+
// 检查 mcps 模块是否可用
|
|
972
|
+
framework.mcps.available; // true/false
|
|
973
|
+
|
|
974
|
+
// 列出所有 MCP 服务器
|
|
975
|
+
framework.mcps.list();
|
|
976
|
+
// [{ name, enabled, connected, tools: [{ name, description }, ...] }, ...]
|
|
977
|
+
|
|
978
|
+
// 获取单个服务器信息
|
|
979
|
+
framework.mcps.get('my-server');
|
|
980
|
+
// { name, enabled, connected, tools: [...] } 或 null
|
|
981
|
+
|
|
982
|
+
// 检查服务器是否存在
|
|
983
|
+
framework.mcps.has('my-server'); // true/false
|
|
984
|
+
|
|
985
|
+
// 添加 MCP 服务器
|
|
986
|
+
await framework.mcps.add({
|
|
987
|
+
name: 'my-server',
|
|
988
|
+
command: 'npx',
|
|
989
|
+
args: ['-y', 'some-mcp-server'],
|
|
990
|
+
env: { API_KEY: 'xxx' }
|
|
991
|
+
});
|
|
992
|
+
// addServer 是 add 的别名
|
|
993
|
+
await framework.mcps.addServer({ name: 'another-server', ... });
|
|
994
|
+
|
|
995
|
+
// 移除 MCP 服务器
|
|
996
|
+
await framework.mcps.removeServer('my-server');
|
|
997
|
+
|
|
998
|
+
// 动态开启/关闭服务器
|
|
999
|
+
await framework.mcps.setEnabled('my-server', false);
|
|
1000
|
+
|
|
1001
|
+
// 重载 MCP 配置
|
|
1002
|
+
await framework.mcps.reload();
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
#### 调用 MCP 工具
|
|
1006
|
+
|
|
1007
|
+
```js
|
|
1008
|
+
// 调用 MCP 服务器上的工具
|
|
1009
|
+
await framework.mcps.call('my-server', 'tool-name', { arg1: 'value' });
|
|
1010
|
+
|
|
1011
|
+
// 获取工具的调用示例和参数说明
|
|
1012
|
+
await framework.mcps.getToolSchema('my-server', 'tool-name');
|
|
1013
|
+
// { success, data: { name, description, parameters, example, fullExample } }
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
#### MCP 工具(内置工具)
|
|
1017
|
+
|
|
1018
|
+
| 工具 | 说明 |
|
|
1019
|
+
|------|------|
|
|
1020
|
+
| `mcp_list_servers` | 列出所有 MCP 服务器 |
|
|
1021
|
+
| `mcp_call` | 调用 MCP 服务器工具 |
|
|
1022
|
+
| `mcp_tool_schema` | 获取工具调用示例 |
|
|
1023
|
+
| `mcp_set_enabled` | 动态开启/关闭服务器 |
|
|
1024
|
+
| `mcp_reload` | 重载 MCP 配置 |
|
|
1025
|
+
|
|
1026
|
+
---
|
|
1027
|
+
|
|
1028
|
+
### 声明式 prompts(推荐)
|
|
1029
|
+
|
|
1030
|
+
除手动调用 `framework.prompts.register()` 外,还可在 Plugin 子类中用 `prompts` 字段声明:
|
|
1031
|
+
|
|
1032
|
+
```js
|
|
1033
|
+
class MyPlugin extends Plugin {
|
|
1034
|
+
name = 'my-plugin';
|
|
1035
|
+
|
|
1036
|
+
prompts = [
|
|
1037
|
+
{
|
|
1038
|
+
name: 'my-rules',
|
|
1039
|
+
scope: 'global',
|
|
1040
|
+
priority: 50,
|
|
1041
|
+
description: '我的自定义规则',
|
|
1042
|
+
provider: function () {
|
|
1043
|
+
// this 绑定到 Plugin 实例
|
|
1044
|
+
return `## 规则\n当前版本: ${this.version}`;
|
|
1045
|
+
},
|
|
1046
|
+
},
|
|
1047
|
+
];
|
|
1048
|
+
|
|
1049
|
+
start(framework) {
|
|
1050
|
+
// 基类 start() 会自动注册 prompts
|
|
1051
|
+
// 无需手动调用 framework.prompts.register()
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
```
|
|
1055
|
+
|
|
1056
|
+
- `provider` 必须用 `function`,不能用箭头函数(需要绑定 `this` 到 Plugin 实例)
|
|
1057
|
+
- 生命周期自动管理:卸载/重载时自动清理
|
|
1058
|
+
- 失败一个 entry 不影响其他
|
|
1059
|
+
|
|
1060
|
+
---
|
|
1061
|
+
|
|
1062
|
+
### 完整的插件示例
|
|
1063
|
+
|
|
1064
|
+
```js
|
|
1065
|
+
const { Plugin } = require('foliko');
|
|
1066
|
+
|
|
1067
|
+
class MyPlugin extends Plugin {
|
|
1068
|
+
name = 'my-plugin';
|
|
1069
|
+
version = '1.0.0';
|
|
1070
|
+
description = '我的插件';
|
|
1071
|
+
|
|
1072
|
+
prompts = [
|
|
1073
|
+
{
|
|
1074
|
+
name: 'plugin-info',
|
|
1075
|
+
scope: 'global',
|
|
1076
|
+
priority: 50,
|
|
1077
|
+
provider: function () {
|
|
1078
|
+
return `## 我的插件\n版本: ${this.version}`;
|
|
1079
|
+
},
|
|
1080
|
+
},
|
|
1081
|
+
];
|
|
1082
|
+
|
|
1083
|
+
install(framework) {
|
|
1084
|
+
// 注册工具
|
|
1085
|
+
framework.tools.tool({
|
|
1086
|
+
name: 'my_tool',
|
|
1087
|
+
description: '做某事',
|
|
1088
|
+
inputSchema: framework.z.object({ msg: framework.z.string() }),
|
|
1089
|
+
execute: async (args) => ({ result: args.msg }),
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
// 注册 Prompt Part
|
|
1093
|
+
framework.prompts.register('my-plugin', 'my-rules', () => {
|
|
1094
|
+
return '## 我的规则';
|
|
1095
|
+
}, { scope: 'global', priority: 50 });
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
start(framework) {
|
|
1099
|
+
// 声明式 prompts 在此自动注册
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
reload(framework) {
|
|
1103
|
+
// prompts 会自动清理并重新注册
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
uninstall(framework) {
|
|
1107
|
+
// prompts 会自动清理
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
module.exports = MyPlugin;
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
---
|
|
1115
|
+
|
|
198
1116
|
## 导出类型
|
|
199
1117
|
|
|
200
1118
|
```js
|