92 lines
3.6 KiB
JavaScript
92 lines
3.6 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* SKILL-REGISTRY 自动校验脚本
|
|
* 对比 SKILL-REGISTRY.md 声明 vs 磁盘实际状态
|
|
* 用法: node scripts/validate-registry.js
|
|
*/
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const ROOT = path.join(__dirname, '..');
|
|
const issues = [];
|
|
let passed = 0;
|
|
|
|
function check(name, condition, detail) {
|
|
if (condition) { passed++; }
|
|
else { issues.push({ severity: detail.startsWith('[CRITICAL]') ? 'CRITICAL' : 'WARNING', name, detail }); }
|
|
}
|
|
|
|
// 1. 钩子磁盘文件数
|
|
const hooksDir = path.join(ROOT, 'hooks');
|
|
const hookFiles = fs.readdirSync(hooksDir).filter(f => f.endsWith('.js'));
|
|
const registry = fs.readFileSync(path.join(ROOT, 'SKILL-REGISTRY.md'), 'utf8');
|
|
const hookCountMatch = registry.match(/磁盘文件\s*(\d+)\s*个/);
|
|
if (hookCountMatch) {
|
|
const declared = parseInt(hookCountMatch[1]);
|
|
check('hooks-disk-count', declared === hookFiles.length,
|
|
`[WARNING] REGISTRY 声明 ${declared} 个钩子文件,实际 ${hookFiles.length} 个`);
|
|
} else {
|
|
check('hooks-disk-count', false, '[WARNING] REGISTRY 中未找到钩子磁盘文件数声明');
|
|
}
|
|
|
|
// 2. 注册钩子文件存在性
|
|
const settings = JSON.parse(fs.readFileSync(path.join(ROOT, 'settings.json'), 'utf8'));
|
|
const allHookPaths = [];
|
|
for (const stage of Object.values(settings.hooks || {})) {
|
|
for (const entry of (Array.isArray(stage) ? stage : [])) {
|
|
for (const hook of (entry.hooks || [])) {
|
|
const m = (hook.command || '').match(/hooks\/([^\s"]+\.js)/);
|
|
if (m) allHookPaths.push(m[1]);
|
|
}
|
|
}
|
|
}
|
|
for (const hp of allHookPaths) {
|
|
const exists = fs.existsSync(path.join(hooksDir, hp));
|
|
check(`hook-exists:${hp}`, exists, `[CRITICAL] 注册钩子文件不存在: hooks/${hp}`);
|
|
}
|
|
|
|
// 3. 技能目录数
|
|
const skillsDir = path.join(ROOT, 'skills');
|
|
const skillDirs = fs.readdirSync(skillsDir).filter(f => {
|
|
return !f.startsWith('_') && !f.startsWith('.') && fs.statSync(path.join(skillsDir, f)).isDirectory();
|
|
});
|
|
const stats = JSON.parse(fs.readFileSync(path.join(ROOT, 'stats-compiled.json'), 'utf8'));
|
|
check('skills-count', skillDirs.length === stats.summary.skills,
|
|
`[WARNING] 技能目录 ${skillDirs.length} vs stats 声明 ${stats.summary.skills}`);
|
|
|
|
// 4. Agent 文件数
|
|
const agentsDir = path.join(ROOT, 'agents');
|
|
const agentFiles = fs.readdirSync(agentsDir).filter(f => f.endsWith('.md'));
|
|
check('agents-count', agentFiles.length === stats.summary.agents,
|
|
`[WARNING] Agent 文件 ${agentFiles.length} vs stats 声明 ${stats.summary.agents}`);
|
|
|
|
// 5. 版本一致性
|
|
const claudeMd = fs.readFileSync(path.join(ROOT, 'CLAUDE.md'), 'utf8');
|
|
const claudeVersion = (claudeMd.match(/Smart Assistant[^v]*(v[\d.]+)/) || [])[1] || 'unknown';
|
|
const registryVersion = (registry.match(/v[\d.]+/) || [])[0] || 'unknown';
|
|
const statsVersion = stats.version || 'unknown';
|
|
check('version-consistency',
|
|
claudeVersion === registryVersion && registryVersion === statsVersion,
|
|
`[WARNING] 版本不一致: CLAUDE.md=${claudeVersion} REGISTRY=${registryVersion} stats=${statsVersion}`);
|
|
|
|
// 6. 每个技能都有 SKILL.md
|
|
for (const sd of skillDirs) {
|
|
const skillMd = path.join(skillsDir, sd, 'SKILL.md');
|
|
check(`skill-md:${sd}`, fs.existsSync(skillMd),
|
|
`[WARNING] 技能 ${sd} 缺少 SKILL.md`);
|
|
}
|
|
|
|
// 输出
|
|
console.log(`\n=== SKILL-REGISTRY 校验报告 ===`);
|
|
console.log(`通过: ${passed} 问题: ${issues.length}\n`);
|
|
|
|
if (issues.length === 0) {
|
|
console.log('✓ 全部通过,无偏差');
|
|
process.exit(0);
|
|
} else {
|
|
for (const i of issues) {
|
|
console.log(`[${i.severity}] ${i.name}: ${i.detail}`);
|
|
}
|
|
process.exit(issues.some(i => i.severity === 'CRITICAL') ? 2 : 1);
|
|
}
|