107 lines
3.3 KiB
JavaScript
107 lines
3.3 KiB
JavaScript
|
|
#!/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');
|