121 lines
4.8 KiB
JavaScript
121 lines
4.8 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
/**
|
|||
|
|
* Task2 补丁 — 2026-04-20 路由精准度审查
|
|||
|
|
*
|
|||
|
|
* 问题: skills-index-lite.json 只有 skills(87条),无 agents 条目。
|
|||
|
|
* R81/R82 的 boost 因 self-auditor/self-healer 不在结果集中而无效。
|
|||
|
|
* generate-skill-index.js 只写 skills-index.json,不写 lite 版本。
|
|||
|
|
*
|
|||
|
|
* 修复方案:
|
|||
|
|
* 1. 修改 generate-skill-index.js — 在 main() 末尾追加:
|
|||
|
|
* a) 扫描 agents/*.md 注入 skills 数组 (type:"agent" 字段)
|
|||
|
|
* b) 写出 skills-index-lite.json (与 skills-index.json 结构相同但含 agents)
|
|||
|
|
* 2. 立即运行生成器刷新 skills-index-lite.json
|
|||
|
|
*
|
|||
|
|
* 幂等: sentinel = 'AGENTS_DIR_SCAN_PATCH_2026_04_20'
|
|||
|
|
*/
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
const SCRIPTS_DIR = path.join(__dirname, '..');
|
|||
|
|
const TARGET = path.join(SCRIPTS_DIR, 'generate-skill-index.js');
|
|||
|
|
const SENTINEL = 'AGENTS_DIR_SCAN_PATCH_2026_04_20';
|
|||
|
|
|
|||
|
|
function main() {
|
|||
|
|
if (!fs.existsSync(TARGET)) {
|
|||
|
|
console.error('[patch-task2] 目标文件不存在:', TARGET);
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const before = fs.readFileSync(TARGET, 'utf8');
|
|||
|
|
|
|||
|
|
// 幂等检查
|
|||
|
|
if (before.includes(SENTINEL)) {
|
|||
|
|
console.log('[patch-task2] 已打过补丁 (发现 sentinel),跳过');
|
|||
|
|
process.exit(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==== 注入1: 在 OUTPUT_FILE 定义后添加 AGENTS_DIR 和 OUTPUT_LITE 常量 ====
|
|||
|
|
const constAnchor = "const OUTPUT_FILE = path.join(CLAUDE_ROOT, 'skills-index.json');";
|
|||
|
|
if (!before.includes(constAnchor)) {
|
|||
|
|
console.error('[patch-task2] 找不到 OUTPUT_FILE 常量锚点,generate-skill-index.js 结构已变更');
|
|||
|
|
process.exit(2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const constInjection =
|
|||
|
|
"const OUTPUT_FILE = path.join(CLAUDE_ROOT, 'skills-index.json');\n" +
|
|||
|
|
"// " + SENTINEL + "\n" +
|
|||
|
|
"const AGENTS_DIR = path.join(CLAUDE_ROOT, 'agents');\n" +
|
|||
|
|
"const OUTPUT_LITE = path.join(CLAUDE_ROOT, 'skills-index-lite.json');";
|
|||
|
|
|
|||
|
|
let after = before.replace(constAnchor, constInjection);
|
|||
|
|
|
|||
|
|
// ==== 注入2: 在 writeFileSync(OUTPUT_FILE...) 之后追加 agents 扫描 + lite 输出 ====
|
|||
|
|
const writeAnchor = " fs.writeFileSync(OUTPUT_FILE, JSON.stringify(index, null, 2) + '\\n');";
|
|||
|
|
if (!after.includes(writeAnchor)) {
|
|||
|
|
console.error('[patch-task2] 找不到 writeFileSync 输出锚点');
|
|||
|
|
process.exit(3);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const liteInjection =
|
|||
|
|
" fs.writeFileSync(OUTPUT_FILE, JSON.stringify(index, null, 2) + '\\n');\n" +
|
|||
|
|
"\n" +
|
|||
|
|
" // === " + SENTINEL + " ===\n" +
|
|||
|
|
" // 扫描 agents/*.md,将 agent 条目注入 skills 数组(type:'agent'),同时写出 skills-index-lite.json\n" +
|
|||
|
|
" try {\n" +
|
|||
|
|
" if (fs.existsSync(AGENTS_DIR)) {\n" +
|
|||
|
|
" const agentFiles = fs.readdirSync(AGENTS_DIR).filter(f => f.endsWith('.md')).sort();\n" +
|
|||
|
|
" let agentCount = 0;\n" +
|
|||
|
|
" for (const file of agentFiles) {\n" +
|
|||
|
|
" const agentPath = path.join(AGENTS_DIR, file);\n" +
|
|||
|
|
" const content = fs.readFileSync(agentPath, 'utf8');\n" +
|
|||
|
|
" const fm = parseFrontmatter(content);\n" +
|
|||
|
|
" if (!fm.name) continue;\n" +
|
|||
|
|
" const keywords = extractKeywords(content);\n" +
|
|||
|
|
" const entry = {\n" +
|
|||
|
|
" name: fm.name,\n" +
|
|||
|
|
" description: (fm.description || '').slice(0, 200),\n" +
|
|||
|
|
" maturity: 'agent',\n" +
|
|||
|
|
" type: 'agent',\n" +
|
|||
|
|
" isComposable: false,\n" +
|
|||
|
|
" allowedTools: (fm['allowed-tools'] || '').split(',').map(s => s.trim()).filter(Boolean),\n" +
|
|||
|
|
" keywords,\n" +
|
|||
|
|
" };\n" +
|
|||
|
|
" index.skills.push(entry);\n" +
|
|||
|
|
" agentCount++;\n" +
|
|||
|
|
" }\n" +
|
|||
|
|
" console.log(' [agents] ' + agentCount + ' agent(s) injected into index');\n" +
|
|||
|
|
" }\n" +
|
|||
|
|
" } catch (e) {\n" +
|
|||
|
|
" console.warn(' [agents] 扫描失败 (非致命):', e.message);\n" +
|
|||
|
|
" }\n" +
|
|||
|
|
"\n" +
|
|||
|
|
" // 写出 skills-index-lite.json (路由引擎实际使用的是 lite 版本)\n" +
|
|||
|
|
" fs.writeFileSync(OUTPUT_LITE, JSON.stringify(index, null, 2) + '\\n');\n" +
|
|||
|
|
" console.log(' [lite] skills-index-lite.json 已同步 (' + index.skills.length + ' entries total)');";
|
|||
|
|
|
|||
|
|
after = after.replace(writeAnchor, liteInjection);
|
|||
|
|
|
|||
|
|
// ==== 原子写入 (.bak 备份 + tmp 文件) ====
|
|||
|
|
const bakFile = TARGET + '.bak.task2.' + Date.now();
|
|||
|
|
fs.copyFileSync(TARGET, bakFile);
|
|||
|
|
console.log('[patch-task2] 备份已写入:', bakFile);
|
|||
|
|
|
|||
|
|
const tmpFile = TARGET + '.tmp';
|
|||
|
|
fs.writeFileSync(tmpFile, after, 'utf8');
|
|||
|
|
fs.renameSync(tmpFile, TARGET);
|
|||
|
|
console.log('[patch-task2] 补丁已应用到:', TARGET);
|
|||
|
|
|
|||
|
|
// 验证 sentinel 存在
|
|||
|
|
const verify = fs.readFileSync(TARGET, 'utf8');
|
|||
|
|
if (!verify.includes(SENTINEL)) {
|
|||
|
|
console.error('[patch-task2] 验证失败: sentinel 未找到!');
|
|||
|
|
process.exit(4);
|
|||
|
|
}
|
|||
|
|
console.log('[patch-task2] PASS — sentinel 验证通过');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
main();
|