#!/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();