ai-helper-agent 1.4.0 → 1.5.1
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/package.json +1 -1
- package/src/security.js +18 -3
- package/src/server.js +42 -1
- package/src/skill/loader.js +42 -281
- package/src/skill/registry.js +6 -1
- package/src/skill/skill-creator-seed.js +144 -0
package/package.json
CHANGED
package/src/security.js
CHANGED
|
@@ -81,9 +81,24 @@ function checkPath(pathStr) {
|
|
|
81
81
|
realPath = realpathSync(resolved);
|
|
82
82
|
} catch (err) {
|
|
83
83
|
if (err.code === 'ENOENT') {
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
// 文件/目录不存在时,向上查找直到找到存在的父目录
|
|
85
|
+
let existing = resolved;
|
|
86
|
+
while (existing !== sep && existing !== '' && existing !== '/') {
|
|
87
|
+
const parent = resolve(existing, '..');
|
|
88
|
+
try {
|
|
89
|
+
realPath = realpathSync(parent) + sep + normalized.slice(resolve(parent).length).replace(/^[/\\]/, '');
|
|
90
|
+
break;
|
|
91
|
+
} catch (parentErr) {
|
|
92
|
+
if (parentErr.code === 'ENOENT') {
|
|
93
|
+
existing = parent;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
return { allowed: false, reason: `无法解析路径: ${parentErr.message}` };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (!realPath) {
|
|
100
|
+
return { allowed: false, reason: `路径检查异常: ${err.message}` };
|
|
101
|
+
}
|
|
87
102
|
} else {
|
|
88
103
|
return { allowed: false, reason: `无法解析路径: ${err.message}` };
|
|
89
104
|
}
|
package/src/server.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// agent/src/server.js - HTTP Router + WebSocket 服务器
|
|
2
2
|
import http from 'http';
|
|
3
3
|
import { WebSocketServer } from 'ws';
|
|
4
|
-
import { readFileSync, writeFileSync, readdirSync, statSync, unlinkSync, rmdirSync, existsSync, chmodSync } from 'fs';
|
|
4
|
+
import { readFileSync, writeFileSync, readdirSync, statSync, unlinkSync, rmdirSync, existsSync, chmodSync, mkdirSync } from 'fs';
|
|
5
5
|
import { join, dirname } from 'path';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { homedir } from 'os';
|
|
8
|
+
import { spawn } from 'child_process';
|
|
8
9
|
import os from 'os';
|
|
9
10
|
import { loadConfig } from './config.js';
|
|
10
11
|
import { verifyToken, getCurrentPairCode, startPairCodeRotation, stopPairCodeRotation, handlePairRequest } from './auth.js';
|
|
@@ -303,6 +304,8 @@ export function startServer() {
|
|
|
303
304
|
const rawContent = 'content' in body ? String(body.content) : '';
|
|
304
305
|
const buf = Buffer.from(rawContent, 'utf-8');
|
|
305
306
|
if (buf.length > maxSize) return jsonResponse(res, 400, { success: false, error: `内容过大 (${buf.length} > ${maxSize})` });
|
|
307
|
+
// 确保父目录存在
|
|
308
|
+
mkdirSync(dirname(check.resolved), { recursive: true });
|
|
306
309
|
writeFileSync(check.resolved, rawContent, 'utf-8');
|
|
307
310
|
|
|
308
311
|
// 如果写入的是脚本文件,剥离执行权限防止直接运行
|
|
@@ -360,6 +363,44 @@ export function startServer() {
|
|
|
360
363
|
return jsonResponse(res, 200, { success: true, path: check.resolved });
|
|
361
364
|
}
|
|
362
365
|
|
|
366
|
+
// 在本地浏览器中打开文件
|
|
367
|
+
if (pathname === '/api/browser/open') {
|
|
368
|
+
const targetPath = body.path;
|
|
369
|
+
if (!targetPath || typeof targetPath !== 'string') {
|
|
370
|
+
return jsonResponse(res, 400, { success: false, error: '缺少 path 参数' });
|
|
371
|
+
}
|
|
372
|
+
const check = checkPath(targetPath);
|
|
373
|
+
if (!check.allowed) {
|
|
374
|
+
logSecurity('browser_open_blocked', { path: targetPath, reason: check.reason });
|
|
375
|
+
return jsonResponse(res, 403, { success: false, error: check.reason });
|
|
376
|
+
}
|
|
377
|
+
if (!existsSync(check.resolved)) {
|
|
378
|
+
return jsonResponse(res, 404, { success: false, error: '文件不存在' });
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const fileUrl = `file://${check.resolved}`;
|
|
382
|
+
const platform = os.platform();
|
|
383
|
+
let cmd, args, spawnOpts;
|
|
384
|
+
if (platform === 'darwin') {
|
|
385
|
+
cmd = 'open';
|
|
386
|
+
args = [fileUrl];
|
|
387
|
+
spawnOpts = { detached: true, stdio: 'ignore' };
|
|
388
|
+
} else if (platform === 'win32') {
|
|
389
|
+
cmd = 'start';
|
|
390
|
+
args = ['""', `"${fileUrl}"`];
|
|
391
|
+
spawnOpts = { detached: true, stdio: 'ignore', shell: true, windowsHide: true };
|
|
392
|
+
} else {
|
|
393
|
+
cmd = 'xdg-open';
|
|
394
|
+
args = [fileUrl];
|
|
395
|
+
spawnOpts = { detached: true, stdio: 'ignore' };
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
spawn(cmd, args, spawnOpts).unref();
|
|
399
|
+
|
|
400
|
+
logFs('browser_open', { path: check.resolved, platform });
|
|
401
|
+
return jsonResponse(res, 200, { success: true, path: check.resolved, platform });
|
|
402
|
+
}
|
|
403
|
+
|
|
363
404
|
// === 命令执行 ===
|
|
364
405
|
|
|
365
406
|
if (pathname === '/api/exec') {
|
package/src/skill/loader.js
CHANGED
|
@@ -5,6 +5,7 @@ import { readFileSync, readdirSync, existsSync, statSync, unlinkSync, mkdirSync,
|
|
|
5
5
|
import { join, extname } from 'path';
|
|
6
6
|
import { homedir } from 'os';
|
|
7
7
|
import { loadAllMarkdownSkills, saveMarkdownSkill, deleteMarkdownSkillDir, importMarkdownSkillFromZip, importMarkdownSkillFromUrl } from './markdown-loader.js';
|
|
8
|
+
import { SKILL_CREATOR_SEED_MD, SKILL_CREATOR_DIR } from './skill-creator-seed.js';
|
|
8
9
|
|
|
9
10
|
const SKILLS_DIR = join(homedir(), '.ai-helper-agent', 'skills');
|
|
10
11
|
|
|
@@ -255,292 +256,52 @@ export function deleteSkillFile(name, type) {
|
|
|
255
256
|
}
|
|
256
257
|
|
|
257
258
|
/**
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
* - 不可编辑、不可删除,但可以停用
|
|
263
|
-
* - 最终用户通过 skill-creator 生成的技能仍存放在 SKILLS_DIR 中
|
|
264
|
-
* @returns {Object[]}
|
|
259
|
+
* 种子 skill-creator 内置技能到文件系统
|
|
260
|
+
* 首次运行时将 skill-creator 的 SKILL.md 写入 ~/.ai-helper-agent/skills/skill-creator/
|
|
261
|
+
* 之后用户可在工具箱中编辑、优化此技能(如调整为 Windows cmd 兼容)
|
|
262
|
+
* @returns {boolean} 是否执行了种子写入
|
|
265
263
|
*/
|
|
266
|
-
export function
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
type: 'agent',
|
|
270
|
-
name: 'skill-creator',
|
|
271
|
-
description: '创建新技能的元技能。当用户要求创建技能、沉淀技能、新增技能、或任何创建/添加技能的意图时,必须立即调用此技能。',
|
|
272
|
-
version: '1.0.0',
|
|
273
|
-
enabled: true,
|
|
274
|
-
builtin: true,
|
|
275
|
-
editable: false,
|
|
276
|
-
deletable: false,
|
|
277
|
-
resources: [],
|
|
278
|
-
dirPath: null,
|
|
279
|
-
skillMdPath: null,
|
|
280
|
-
_filePath: null,
|
|
281
|
-
prompt: `## When to Use
|
|
282
|
-
|
|
283
|
-
**必须在以下情况立即调用此技能:**
|
|
284
|
-
- 用户说"创建技能"、"沉淀技能"、"新增技能"、"添加技能"
|
|
285
|
-
- 用户说"把这个模式保存为技能"、"把这个流程沉淀为技能"
|
|
286
|
-
- 用户说"我想以后也能用这个流程"
|
|
287
|
-
- 用户要求将某个操作规范化为可复用的技能
|
|
288
|
-
- 用户说"更新技能"、"完善技能"、"改进技能"、"优化技能"、"增强技能"
|
|
289
|
-
- 用户说"更新 XX 技能"(XX 是已有技能名称)
|
|
290
|
-
- 用户在对话中完善了已有技能的操作流程后说"更新这个技能"
|
|
291
|
-
- 用户发现已有技能的缺陷并提供了改进方案
|
|
292
|
-
|
|
293
|
-
**不要做:**
|
|
294
|
-
- 仅解释如何创建技能而不实际创建
|
|
295
|
-
- 在未调用此技能的情况下手动指导创建
|
|
296
|
-
- 推迟技能创建到后续步骤
|
|
297
|
-
|
|
298
|
-
**重要安全约束**:\`~/.ai-helper-agent/\` 根目录是受限目录,AI 不能访问。唯一允许操作的是 \`~/.ai-helper-agent/skills/\` 子目录。禁止先探索父目录再找子目录。
|
|
299
|
-
|
|
300
|
-
## 技能目录结构
|
|
301
|
-
|
|
302
|
-
所有用户创建的技能存放在 \`~/.ai-helper-agent/skills/\` 目录下(SKILLS_DIR):
|
|
303
|
-
|
|
304
|
-
\`\`\`
|
|
305
|
-
~/.ai-helper-agent/skills/<skill-name>/
|
|
306
|
-
├── SKILL.md # 技能定义文件(必需)
|
|
307
|
-
└── _meta.json # 元数据(可选)
|
|
308
|
-
\`\`\`
|
|
309
|
-
|
|
310
|
-
**禁止**:不要用 ls/readdir 探索 ~/.ai-helper-agent/ 或 ~/.ai-helper-agent/skills/。直接到目标目录操作。
|
|
311
|
-
|
|
312
|
-
## SKILL.md 标准格式
|
|
313
|
-
|
|
314
|
-
\`\`\`markdown
|
|
315
|
-
---
|
|
316
|
-
name: <skill-name>
|
|
317
|
-
description: "<简明描述,包含:(1) 技能做什么,(2) 何时触发调用。控制在200字符以内>"
|
|
318
|
-
enabled: true
|
|
319
|
-
---
|
|
320
|
-
|
|
321
|
-
# <技能标题>
|
|
322
|
-
|
|
323
|
-
## When to Use This Skill
|
|
324
|
-
|
|
325
|
-
- 触发条件1
|
|
326
|
-
- 触发条件2
|
|
327
|
-
|
|
328
|
-
## Core Capabilities
|
|
329
|
-
|
|
330
|
-
- 能力1
|
|
331
|
-
- 能力2
|
|
332
|
-
|
|
333
|
-
## Usage
|
|
334
|
-
|
|
335
|
-
### Step-by-Step
|
|
336
|
-
|
|
337
|
-
1. 步骤1
|
|
338
|
-
2. 步骤2
|
|
339
|
-
|
|
340
|
-
## Examples
|
|
341
|
-
|
|
342
|
-
[具体示例]
|
|
343
|
-
|
|
344
|
-
## Source
|
|
345
|
-
|
|
346
|
-
从对话中沉淀,创建日期:YYYY-MM-DD
|
|
347
|
-
\`\`\`
|
|
348
|
-
|
|
349
|
-
## 创建流程
|
|
350
|
-
|
|
351
|
-
**重要安全约束**:\`~/.ai-helper-agent/\` 根目录是受限目录,AI 不能访问。唯一允许操作的是 \`~/.ai-helper-agent/skills/\` 子目录。禁止先探索父目录再找子目录。
|
|
352
|
-
|
|
353
|
-
1. **确认意图**:与用户简短确认技能名称和核心用途
|
|
354
|
-
- 技能名称:小写字母 + 连字符(如 code-reviewer、api-error-handler)
|
|
355
|
-
- 一句话描述这个技能解决什么问题
|
|
356
|
-
|
|
357
|
-
2. **生成 description**:description 是触发匹配的关键,必须包含:
|
|
358
|
-
- 技能做什么(功能描述)
|
|
359
|
-
- 什么时候触发调用(触发条件/场景关键词)
|
|
360
|
-
- 示例格式:"Does X. Invoke when Y happens or user asks for Z."
|
|
361
|
-
|
|
362
|
-
3. **收集内容**:基于当前对话上下文,提取核心内容:
|
|
363
|
-
- 触发条件(什么时候该用这个技能)
|
|
364
|
-
- 核心能力(这个技能提供什么)
|
|
365
|
-
- 操作步骤(具体怎么做)
|
|
366
|
-
- 示例(正例和反例)
|
|
367
|
-
- 注意事项(容易踩的坑)
|
|
368
|
-
|
|
369
|
-
4. **直接创建文件**:一步到位,直接在 \`~/.ai-helper-agent/skills/<skill-name>/\` 下操作。
|
|
370
|
-
- **禁止**先 \`ls\` 查看 \`~/.ai-helper-agent/\` 或 \`~/.ai-helper-agent/skills/\` 目录(前者无权限,后者不必要)
|
|
371
|
-
- 直接 \`mkdir -p ~/.ai-helper-agent/skills/<skill-name>/\` 创建子目录
|
|
372
|
-
- 直接写入 \`~/.ai-helper-agent/skills/<skill-name>/SKILL.md\`
|
|
373
|
-
- 创建完成后,告知用户在工具箱页面点击「重新加载」使技能生效
|
|
374
|
-
|
|
375
|
-
5. **告知用户**:创建完成后,告知用户技能已就绪,说明触发关键词
|
|
376
|
-
|
|
377
|
-
## 更新流程(已有技能)
|
|
378
|
-
|
|
379
|
-
当用户要求更新/完善已有技能时:
|
|
380
|
-
1. 确认技能名称
|
|
381
|
-
2. 直接读取 ~/.ai-helper-agent/skills/<skill-name>/SKILL.md
|
|
382
|
-
3. 分析对话上下文,识别需要更新的部分(新增触发条件、修改步骤、添加示例等)
|
|
383
|
-
4. 合并内容,保留核心结构,融入新内容
|
|
384
|
-
5. 更新 frontmatter 中的 description(如有必要)
|
|
385
|
-
6. 覆盖写入 SKILL.md
|
|
386
|
-
7. 告知用户更新完成,建议重新加载
|
|
387
|
-
|
|
388
|
-
## 技能质量标准
|
|
389
|
-
|
|
390
|
-
创建技能前验证:
|
|
391
|
-
|
|
392
|
-
- [ ] 解决方案经过验证且有效
|
|
393
|
-
- [ ] 内容脱离原始对话上下文后依然清晰可理解
|
|
394
|
-
- [ ] 触发条件明确,模型能准确识别
|
|
395
|
-
- [ ] description 包含功能描述和触发场景两部分
|
|
396
|
-
- [ ] 名称符合小写+连字符规范
|
|
397
|
-
- [ ] 代码示例完整且可独立运行
|
|
398
|
-
- [ ] 如有参考文档,在文中提供链接`,
|
|
399
|
-
fullPrompt: `# Skill Creator
|
|
400
|
-
|
|
401
|
-
此技能用于在对话过程中即时创建和沉淀新技能。当用户在对话中发现有价值的操作模式、最佳实践或知识时,通过此技能将其固化为可复用的 SKILL.md 文件。
|
|
402
|
-
|
|
403
|
-
## When to Use
|
|
404
|
-
|
|
405
|
-
**必须在以下情况立即调用此技能:**
|
|
406
|
-
- 用户说"创建技能"、"沉淀技能"、"新增技能"、"添加技能"
|
|
407
|
-
- 用户说"把这个模式保存为技能"、"把这个流程沉淀为技能"
|
|
408
|
-
- 用户说"我想以后也能用这个流程"
|
|
409
|
-
- 用户要求将某个操作规范化为可复用的技能
|
|
410
|
-
- 用户说"更新技能"、"完善技能"、"改进技能"、"优化技能"、"增强技能"
|
|
411
|
-
- 用户说"更新 XX 技能"(XX 是已有技能名称)
|
|
412
|
-
- 用户在对话中完善了已有技能的操作流程后说"更新这个技能"
|
|
413
|
-
- 用户发现已有技能的缺陷并提供了改进方案
|
|
414
|
-
|
|
415
|
-
**不要做:**
|
|
416
|
-
- 仅解释如何创建技能而不实际创建
|
|
417
|
-
- 在未调用此技能的情况下手动指导创建
|
|
418
|
-
- 推迟技能创建到后续步骤
|
|
419
|
-
|
|
420
|
-
**重要安全约束**:~/.ai-helper-agent/ 根目录是受限目录,AI 不能访问。唯一允许操作的是 ~/.ai-helper-agent/skills/ 子目录。禁止先探索父目录再找子目录。
|
|
421
|
-
|
|
422
|
-
## 技能目录结构
|
|
423
|
-
|
|
424
|
-
所有用户创建的技能存放在 ~/.ai-helper-agent/skills/ 目录下:
|
|
425
|
-
|
|
426
|
-
\`\`\`
|
|
427
|
-
~/.ai-helper-agent/skills/<skill-name>/
|
|
428
|
-
├── SKILL.md
|
|
429
|
-
└── _meta.json
|
|
430
|
-
\`\`\`
|
|
431
|
-
|
|
432
|
-
**禁止**:不要用 ls/readdir 探索 ~/.ai-helper-agent/ 或 ~/.ai-helper-agent/skills/。直接到目标目录操作。
|
|
264
|
+
export function seedSkillCreator() {
|
|
265
|
+
const skillDir = join(SKILLS_DIR, SKILL_CREATOR_DIR);
|
|
266
|
+
const skillMdPath = join(skillDir, 'SKILL.md');
|
|
433
267
|
|
|
434
|
-
|
|
268
|
+
console.log(`[Skill Loader] seedSkillCreator: 检查路径 ${skillMdPath}`);
|
|
269
|
+
console.log(`[Skill Loader] seedSkillCreator: 文件存在? ${existsSync(skillMdPath)}`);
|
|
435
270
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
## Core Capabilities
|
|
451
|
-
|
|
452
|
-
- 能力1
|
|
453
|
-
- 能力2
|
|
454
|
-
|
|
455
|
-
## Usage
|
|
456
|
-
|
|
457
|
-
### Step-by-Step
|
|
458
|
-
|
|
459
|
-
1. 步骤1
|
|
460
|
-
2. 步骤2
|
|
461
|
-
|
|
462
|
-
## Examples
|
|
463
|
-
|
|
464
|
-
[具体示例]
|
|
465
|
-
|
|
466
|
-
## Source
|
|
467
|
-
|
|
468
|
-
从对话中沉淀,创建日期:YYYY-MM-DD
|
|
469
|
-
\`\`\`
|
|
470
|
-
|
|
471
|
-
## 创建流程
|
|
472
|
-
|
|
473
|
-
1. **确认意图**:与用户简短确认技能名称和核心用途
|
|
474
|
-
- 技能名称:小写字母 + 连字符(如 code-reviewer、api-error-handler)
|
|
475
|
-
- 一句话描述这个技能解决什么问题
|
|
476
|
-
|
|
477
|
-
2. **生成 description**:description 是触发匹配的关键,必须包含:
|
|
478
|
-
- 技能做什么(功能描述)
|
|
479
|
-
- 什么时候触发调用(触发条件/场景关键词)
|
|
480
|
-
- 示例格式:"Does X. Invoke when Y happens or user asks for Z."
|
|
481
|
-
|
|
482
|
-
3. **收集内容**:基于当前对话上下文,提取核心内容:
|
|
483
|
-
- 触发条件(什么时候该用这个技能)
|
|
484
|
-
- 核心能力(这个技能提供什么)
|
|
485
|
-
- 操作步骤(具体怎么做)
|
|
486
|
-
- 示例(正例和反例)
|
|
487
|
-
- 注意事项(容易踩的坑)
|
|
488
|
-
|
|
489
|
-
4. **直接创建文件**:一步到位,直接在 ~/.ai-helper-agent/skills/<skill-name>/ 下操作。
|
|
490
|
-
- **禁止**先 ls 查看 ~/.ai-helper-agent/ 或 ~/.ai-helper-agent/skills/ 目录(前者无权限,后者不必要)
|
|
491
|
-
- 直接 mkdir -p ~/.ai-helper-agent/skills/<skill-name>/ 创建子目录
|
|
492
|
-
- 直接写入 ~/.ai-helper-agent/skills/<skill-name>/SKILL.md
|
|
493
|
-
- 创建完成后,告知用户在工具箱页面点击「重新加载」使技能生效
|
|
494
|
-
|
|
495
|
-
5. **告知用户**:创建完成后,告知用户技能已就绪,说明触发关键词
|
|
496
|
-
|
|
497
|
-
## 更新流程(已有技能)
|
|
498
|
-
|
|
499
|
-
当用户要求更新/完善已有技能时:
|
|
500
|
-
|
|
501
|
-
1. **确认技能名称**:与用户确认要更新的技能名称(如 "code-reviewer")
|
|
502
|
-
|
|
503
|
-
2. **读取现有内容**:直接读取 ~/.ai-helper-agent/skills/<skill-name>/SKILL.md 文件
|
|
504
|
-
|
|
505
|
-
3. **分析更新需求**:结合当前对话上下文,识别需要更新的部分:
|
|
506
|
-
- 新增触发条件
|
|
507
|
-
- 修改操作步骤
|
|
508
|
-
- 添加新示例
|
|
509
|
-
- 修复错误或改进流程
|
|
510
|
-
- 更新 description 使其更准确
|
|
511
|
-
|
|
512
|
-
4. **合并内容**:保留原技能的核心结构,将新内容融入适当位置:
|
|
513
|
-
- 将新的触发条件追加到 "When to Use This Skill" 部分
|
|
514
|
-
- 将改进的步骤更新到 "Usage" 部分
|
|
515
|
-
- 将新示例添加到 "Examples" 部分
|
|
516
|
-
- 如果流程有重大变化,重新组织整个技能结构
|
|
517
|
-
|
|
518
|
-
5. **更新 description**:如果触发条件或核心能力有变化,更新 frontmatter 中的 description
|
|
519
|
-
|
|
520
|
-
6. **直接写入文件**:覆盖写入 ~/.ai-helper-agent/skills/<skill-name>/SKILL.md
|
|
521
|
-
|
|
522
|
-
7. **告知用户**:告知用户技能已更新,说明更新了哪些内容,并建议点击「重新加载」使更新生效
|
|
523
|
-
|
|
524
|
-
## 更新注意事项
|
|
525
|
-
|
|
526
|
-
- 保留原技能的名称(frontmatter 中的 name 字段不变)
|
|
527
|
-
- 保留原技能的核心结构,只在必要时重构
|
|
528
|
-
- 更新后技能的触发条件应比以前更完善,不应遗漏原有条件
|
|
529
|
-
- 每次更新都应使技能质量提升,而不是倒退
|
|
530
|
-
|
|
531
|
-
## 技能质量标准
|
|
532
|
-
|
|
533
|
-
创建技能前验证:
|
|
271
|
+
if (existsSync(skillMdPath)) {
|
|
272
|
+
// 已存在,检查是否需要升级(版本号不同则覆盖)
|
|
273
|
+
try {
|
|
274
|
+
const content = readFileSync(skillMdPath, 'utf-8');
|
|
275
|
+
const versionMatch = content.match(/^version:\s*([\d.]+)/m);
|
|
276
|
+
const currentVersion = versionMatch ? versionMatch[1] : '0.0.0';
|
|
277
|
+
console.log(`[Skill Loader] seedSkillCreator: 已有文件版本=${currentVersion}, 目标版本=1.1.0`);
|
|
278
|
+
if (currentVersion === '1.1.0') {
|
|
279
|
+
console.log('[Skill Loader] seedSkillCreator: 版本匹配,跳过写入');
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
} catch { /* 读取失败则覆盖 */ }
|
|
283
|
+
}
|
|
534
284
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
- [ ] description 包含功能描述和触发场景两部分
|
|
539
|
-
- [ ] 名称符合小写+连字符规范
|
|
540
|
-
- [ ] 代码示例完整且可独立运行
|
|
541
|
-
- [ ] 如有参考文档,在文中提供链接`
|
|
285
|
+
try {
|
|
286
|
+
if (!existsSync(skillDir)) {
|
|
287
|
+
mkdirSync(skillDir, { recursive: true });
|
|
542
288
|
}
|
|
543
|
-
|
|
289
|
+
writeFileSync(skillMdPath, SKILL_CREATOR_SEED_MD, 'utf-8');
|
|
290
|
+
console.log('[Skill Loader] 已种子写入 skill-creator SKILL.md (v1.1.0)');
|
|
291
|
+
return true;
|
|
292
|
+
} catch (err) {
|
|
293
|
+
console.warn('[Skill Loader] 种子写入 skill-creator 失败:', err.message);
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* 获取代码内置技能列表(已废弃,改用 seedSkillCreator 写入磁盘)
|
|
300
|
+
* 保留此函数为空以兼容旧代码调用
|
|
301
|
+
* @returns {Object[]}
|
|
302
|
+
*/
|
|
303
|
+
export function getBuiltinSkills() {
|
|
304
|
+
return [];
|
|
544
305
|
}
|
|
545
306
|
|
|
546
307
|
export { SKILLS_DIR };
|
package/src/skill/registry.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// skill/registry.js - Skill 注册表
|
|
2
2
|
// 管理所有已加载的 Skill(Workflow + Agent),提供查询和执行接口
|
|
3
|
-
import { loadAllSkills, saveSkillFile, deleteSkillFile, getBuiltinSkills, deleteMarkdownSkillDir } from './loader.js';
|
|
3
|
+
import { loadAllSkills, saveSkillFile, deleteSkillFile, getBuiltinSkills, seedSkillCreator, deleteMarkdownSkillDir } from './loader.js';
|
|
4
4
|
import { executeSkill } from './executor.js';
|
|
5
5
|
import { SKILLS_DIR } from './loader.js';
|
|
6
6
|
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
@@ -45,8 +45,13 @@ function saveBuiltinState(state) {
|
|
|
45
45
|
export function initializeSkillRegistry() {
|
|
46
46
|
skills.clear();
|
|
47
47
|
|
|
48
|
+
// 0. 种子写入 skill-creator 到文件系统(首次运行或版本升级时)
|
|
49
|
+
const seeded = seedSkillCreator();
|
|
50
|
+
console.log(`[Skill Registry] seedSkillCreator 返回: ${seeded}`);
|
|
51
|
+
|
|
48
52
|
// 1. 加载文件系统中的技能
|
|
49
53
|
const loaded = loadAllSkills();
|
|
54
|
+
console.log(`[Skill Registry] loadAllSkills 返回 ${loaded.length} 个技能: ${loaded.map(s => s.name).join(', ') || '(无)'}`);
|
|
50
55
|
|
|
51
56
|
for (const skill of loaded) {
|
|
52
57
|
skills.set(skill.name, skill);
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// skill/skill-creator-seed.js
|
|
2
|
+
// skill-creator 技能的种子 SKILL.md 内容
|
|
3
|
+
// 当 ~/.ai-helper-agent/skills/skill-creator/ 目录不存在时自动写入
|
|
4
|
+
|
|
5
|
+
export const SKILL_CREATOR_DIR = 'skill-creator';
|
|
6
|
+
|
|
7
|
+
export const SKILL_CREATOR_SEED_MD = `---
|
|
8
|
+
name: skill-creator
|
|
9
|
+
description: "创建新技能的元技能。当用户要求创建技能、沉淀技能、新增技能、或任何创建/添加技能的意图时,必须立即调用此技能。"
|
|
10
|
+
enabled: true
|
|
11
|
+
version: 1.1.0
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Skill Creator
|
|
15
|
+
|
|
16
|
+
此技能用于在对话过程中即时创建和沉淀新技能。当用户在对话中发现有价值的操作模式、最佳实践或知识时,通过此技能将其固化为可复用的 SKILL.md 文件。
|
|
17
|
+
|
|
18
|
+
## When to Use
|
|
19
|
+
|
|
20
|
+
**必须在以下情况立即调用此技能:**
|
|
21
|
+
- 用户说"创建技能"、"沉淀技能"、"新增技能"、"添加技能"
|
|
22
|
+
- 用户说"把这个模式保存为技能"、"把这个流程沉淀为技能"
|
|
23
|
+
- 用户说"我想以后也能用这个流程"
|
|
24
|
+
- 用户要求将某个操作规范化为可复用的技能
|
|
25
|
+
- 用户说"更新技能"、"完善技能"、"改进技能"、"优化技能"、"增强技能"
|
|
26
|
+
- 用户说"更新 XX 技能"(XX 是已有技能名称)
|
|
27
|
+
- 用户在对话中完善了已有技能的操作流程后说"更新这个技能"
|
|
28
|
+
- 用户发现已有技能的缺陷并提供了改进方案
|
|
29
|
+
|
|
30
|
+
**不要做:**
|
|
31
|
+
- 仅解释如何创建技能而不实际创建
|
|
32
|
+
- 在未调用此技能的情况下手动指导创建
|
|
33
|
+
- 推迟技能创建到后续步骤
|
|
34
|
+
|
|
35
|
+
**重要安全约束**:不探索或列举 ~/.ai-helper-agent/ 根目录的内容。唯一允许操作的是 ~/.ai-helper-agent/skills/ 子目录中的目标技能目录。
|
|
36
|
+
|
|
37
|
+
## 技能目录结构
|
|
38
|
+
|
|
39
|
+
所有用户创建的技能存放在 ~/.ai-helper-agent/skills/ 目录下:
|
|
40
|
+
|
|
41
|
+
\`\`\`
|
|
42
|
+
~/.ai-helper-agent/skills/<skill-name>/
|
|
43
|
+
├── SKILL.md # 技能定义文件(必需)
|
|
44
|
+
└── _meta.json # 元数据(可选)
|
|
45
|
+
\`\`\`
|
|
46
|
+
|
|
47
|
+
## SKILL.md 标准格式
|
|
48
|
+
|
|
49
|
+
\`\`\`markdown
|
|
50
|
+
---
|
|
51
|
+
name: <skill-name>
|
|
52
|
+
description: "<简明描述,包含:(1) 技能做什么,(2) 何时触发调用。控制在200字符以内>"
|
|
53
|
+
enabled: true
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
# <技能标题>
|
|
57
|
+
|
|
58
|
+
## When to Use This Skill
|
|
59
|
+
|
|
60
|
+
- 触发条件1
|
|
61
|
+
- 触发条件2
|
|
62
|
+
|
|
63
|
+
## Core Capabilities
|
|
64
|
+
|
|
65
|
+
- 能力1
|
|
66
|
+
- 能力2
|
|
67
|
+
|
|
68
|
+
## Usage
|
|
69
|
+
|
|
70
|
+
### Step-by-Step
|
|
71
|
+
|
|
72
|
+
1. 步骤1
|
|
73
|
+
2. 步骤2
|
|
74
|
+
|
|
75
|
+
## Examples
|
|
76
|
+
|
|
77
|
+
[具体示例]
|
|
78
|
+
|
|
79
|
+
## Source
|
|
80
|
+
|
|
81
|
+
从对话中沉淀,创建日期:YYYY-MM-DD
|
|
82
|
+
\`\`\`
|
|
83
|
+
|
|
84
|
+
## 创建流程
|
|
85
|
+
|
|
86
|
+
1. **确认意图**:与用户简短确认技能名称和核心用途
|
|
87
|
+
- 技能名称:小写字母 + 连字符(如 code-reviewer、api-error-handler)
|
|
88
|
+
- 一句话描述这个技能解决什么问题
|
|
89
|
+
|
|
90
|
+
2. **生成 description**:description 是触发匹配的关键,必须包含:
|
|
91
|
+
- 技能做什么(功能描述)
|
|
92
|
+
- 什么时候触发调用(触发条件/场景关键词)
|
|
93
|
+
- 示例格式:"Does X. Invoke when Y happens or user asks for Z."
|
|
94
|
+
|
|
95
|
+
3. **收集内容**:基于当前对话上下文,提取核心内容:
|
|
96
|
+
- 触发条件(什么时候该用这个技能)
|
|
97
|
+
- 核心能力(这个技能提供什么)
|
|
98
|
+
- 操作步骤(具体怎么做)
|
|
99
|
+
- 示例(正例和反例)
|
|
100
|
+
- 注意事项(容易踩的坑)
|
|
101
|
+
|
|
102
|
+
4. **创建技能目录**:
|
|
103
|
+
- 使用 \`agent_exec_command\` 创建技能子目录。
|
|
104
|
+
- **macOS/Linux**:\`mkdir -p ~/.ai-helper-agent/skills/<skill-name>\`
|
|
105
|
+
- **Windows (cmd)**:\`mkdir %USERPROFILE%\\.ai-helper-agent\\skills\\<skill-name>\`
|
|
106
|
+
- **Windows (PowerShell)**:\`New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\\.ai-helper-agent\\skills\\<skill-name>"\`
|
|
107
|
+
- 如果 \`mkdir\` 报错,尝试不带参数或使用 \`agent_write_file\` 工具在目标路径写入一个临时文件,让系统自动创建父目录。
|
|
108
|
+
|
|
109
|
+
5. **写入 SKILL.md**:使用 \`agent_write_file\` 工具将内容写入:
|
|
110
|
+
- 路径:\`~/.ai-helper-agent/skills/<skill-name>/SKILL.md\`
|
|
111
|
+
- Windows 上路径中的 \`~\` 会自动解析为用户主目录
|
|
112
|
+
|
|
113
|
+
6. **告知用户**:创建完成后,告知用户技能已就绪,说明触发关键词。建议用户在工具箱页面点击「重新加载」使技能生效。
|
|
114
|
+
|
|
115
|
+
## 更新流程(已有技能)
|
|
116
|
+
|
|
117
|
+
当用户要求更新/完善已有技能时:
|
|
118
|
+
|
|
119
|
+
1. **确认技能名称**:与用户确认要更新的技能名称
|
|
120
|
+
2. **读取现有内容**:使用 \`agent_read_file\` 读取目标 SKILL.md
|
|
121
|
+
3. **分析更新需求**:结合当前对话上下文,识别需要更新的部分
|
|
122
|
+
4. **合并内容**:保留原技能的核心结构,将新内容融入适当位置
|
|
123
|
+
5. **覆盖写入**:使用 \`agent_write_file\` 覆盖写入 SKILL.md
|
|
124
|
+
6. **告知用户**:告知更新内容,建议重新加载
|
|
125
|
+
|
|
126
|
+
## 更新注意事项
|
|
127
|
+
|
|
128
|
+
- 保留原技能的名称(frontmatter 中的 name 字段不变)
|
|
129
|
+
- 保留原技能的核心结构,只在必要时重构
|
|
130
|
+
- 更新后技能的触发条件应比以前更完善
|
|
131
|
+
- 每次更新都应使技能质量提升
|
|
132
|
+
|
|
133
|
+
## 技能质量标准
|
|
134
|
+
|
|
135
|
+
创建技能前验证:
|
|
136
|
+
|
|
137
|
+
- [ ] 解决方案经过验证且有效
|
|
138
|
+
- [ ] 内容脱离原始对话上下文后依然清晰可理解
|
|
139
|
+
- [ ] 触发条件明确,模型能准确识别
|
|
140
|
+
- [ ] description 包含功能描述和触发场景两部分
|
|
141
|
+
- [ ] 名称符合小写+连字符规范
|
|
142
|
+
- [ ] 代码示例完整且可独立运行
|
|
143
|
+
- [ ] 如有参考文档,在文中提供链接
|
|
144
|
+
`;
|