#!/usr/bin/env node /** * P0-3 Patch: Skill 三层分层 * 基于 actual-skills.jsonl 频率 + criticality 维度, 给 skills-index.json 每个 skill 追加: * - tier: 1 (常驻 ~20) / 2 (按需 ~30) / 3 (归档 ~45) * - criticality: "HIGH" / "MEDIUM" / "LOW" * 幂等: 检查首个 skill 是否已有 tier 字段 */ 'use strict'; const fs = require('fs'); const path = require('path'); const CLAUDE_ROOT = path.resolve(__dirname, '..', '..'); const INDEX_PATH = path.join(CLAUDE_ROOT, 'skills-index.json'); const ACTUAL_PATH = path.join(CLAUDE_ROOT, 'debug', 'actual-skills.jsonl'); // --- Criticality 定义 --- const CRITICALITY_HIGH = new Set([ 'security-expert', 'guardian', 'zero-defect-guardian', 'review', 'debugger-expert', 'developer-expert', 'project-audit-expert', ]); const TIER1_ESSENTIAL = new Set([ 'frontend-expert', 'backend-builder', 'qa', 'prompt-optimizer', 'mobile-expert', 'architect-expert', ]); const TIER2_USEFUL = new Set([ 'cloud-architect', 'database-tuning-expert', 'notification-system-expert', 'finance-advisor', 'sre-expert', 'data-analyst-expert', 'vue-expert', 'nextjs-developer', 'python-pro', 'typescript-pro', 'rust-engineer', 'golang-pro', 'swift-expert', 'flutter-expert', 'terraform-engineer', 'kubernetes-specialist', 'graphql-architect', 'websocket-engineer', 'angular-architect', 'cloud-native-expert', ]); const TIER1_FREQ_THRESHOLD = 3; // --- 频率统计 --- function countFrequency() { const counts = {}; try { const lines = fs.readFileSync(ACTUAL_PATH, 'utf8').trim().split('\n'); for (const line of lines) { try { const obj = JSON.parse(line); const skill = obj.actualSkill; if (skill) counts[skill] = (counts[skill] || 0) + 1; } catch {} } } catch {} return counts; } // --- 分层逻辑 --- function assignTier(name, freq) { const isHigh = CRITICALITY_HIGH.has(name); const isEssential = TIER1_ESSENTIAL.has(name); const criticality = isHigh ? 'HIGH' : (isEssential || TIER2_USEFUL.has(name) || freq >= 2) ? 'MEDIUM' : 'LOW'; let tier; if (freq >= TIER1_FREQ_THRESHOLD || isHigh || isEssential) { tier = 1; } else if (freq >= 1 || TIER2_USEFUL.has(name) || criticality === 'MEDIUM') { tier = 2; } else { tier = 3; } return { tier, criticality }; } // --- Execute --- if (!fs.existsSync(INDEX_PATH)) { process.stderr.write('[FAIL] skills-index.json not found\n'); process.exit(1); } const index = JSON.parse(fs.readFileSync(INDEX_PATH, 'utf8')); // 幂等检查 if (index.skills[0] && typeof index.skills[0].tier === 'number') { process.stderr.write('[SKIP] skills-index.json already has tier field\n'); process.exit(0); } const freq = countFrequency(); const tierCounts = { 1: 0, 2: 0, 3: 0 }; for (const skill of index.skills) { const f = freq[skill.name] || 0; const { tier, criticality } = assignTier(skill.name, f); skill.tier = tier; skill.criticality = criticality; skill.callCount30d = f; tierCounts[tier]++; } // 备份 fs.writeFileSync(INDEX_PATH + '.bak-p03.' + Date.now(), fs.readFileSync(INDEX_PATH)); // 原子写 const tmp = INDEX_PATH + '.tmp.' + process.pid; fs.writeFileSync(tmp, JSON.stringify(index, null, 2), 'utf8'); fs.renameSync(tmp, INDEX_PATH); process.stderr.write('[DONE] skills-index.json tiered: T1=' + tierCounts[1] + ' T2=' + tierCounts[2] + ' T3=' + tierCounts[3] + '\n');