179 lines
7.3 KiB
JavaScript
179 lines
7.3 KiB
JavaScript
|
|
#!/usr/bin/env node
|
|||
|
|
/**
|
|||
|
|
* patch-route-precision-10x-batch-a.js
|
|||
|
|
* 路由精度10项改进 — Batch A: disambiguation-rules.json 变更
|
|||
|
|
* Item 3: R27 移除 bookworm|自检 关键词
|
|||
|
|
* Item 4: 新增 R90 sre-expert boost
|
|||
|
|
* Item 5: 新增 R91 impact-analyst boost
|
|||
|
|
* Item 7: 新增 R92 Google Sheets 数据分析再路由
|
|||
|
|
* Item 8: R58 补充 evolution-tracker boost
|
|||
|
|
* Item 10: 新增 R93 MCP browser consolidation
|
|||
|
|
*
|
|||
|
|
* 安全性: .bak 备份 + sentinel 幂等检查 + UTF-8 无 BOM 写入
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const fs = require('fs');
|
|||
|
|
const path = require('path');
|
|||
|
|
|
|||
|
|
// SENTINEL: 防止重复运行
|
|||
|
|
const SENTINEL = 'PATCH_ROUTE_PRECISION_10X_BATCH_A_APPLIED';
|
|||
|
|
|
|||
|
|
const SCRIPTS_DIR = path.join(__dirname, '..');
|
|||
|
|
const RULES_FILE = path.join(SCRIPTS_DIR, 'disambiguation-rules.json');
|
|||
|
|
const BAK_FILE = RULES_FILE + '.bak';
|
|||
|
|
|
|||
|
|
// ── 读取原文件 ─────────────────────────────────────────
|
|||
|
|
if (!fs.existsSync(RULES_FILE)) {
|
|||
|
|
console.error('[ERROR] disambiguation-rules.json not found:', RULES_FILE);
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const raw = fs.readFileSync(RULES_FILE, 'utf8');
|
|||
|
|
const data = JSON.parse(raw);
|
|||
|
|
|
|||
|
|
// ── 幂等检查 ───────────────────────────────────────────
|
|||
|
|
if (data._meta && data._meta[SENTINEL]) {
|
|||
|
|
console.log('[SKIP] Patch already applied (sentinel found). Nothing to do.');
|
|||
|
|
process.exit(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 另一种幂等检查: 如果 R90 已存在,也跳过
|
|||
|
|
if (data.rules && data.rules.some(r => r.id === 'R90')) {
|
|||
|
|
console.log('[SKIP] R90 already exists. Patch appears already applied.');
|
|||
|
|
process.exit(0);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 备份 ───────────────────────────────────────────────
|
|||
|
|
fs.writeFileSync(BAK_FILE, raw, 'utf8');
|
|||
|
|
console.log('[BAK] Backed up to', BAK_FILE);
|
|||
|
|
|
|||
|
|
// ── Item 3: R27 — 移除 bookworm 和 自检 关键词 ─────────
|
|||
|
|
let r27Modified = false;
|
|||
|
|
for (const rule of data.rules) {
|
|||
|
|
if (rule.id === 'R27') {
|
|||
|
|
const before = rule.trigger;
|
|||
|
|
// 移除整个 pipe-delimited token: bookworm 和 自检
|
|||
|
|
// 策略: 将 trigger 按 | 分割,过滤掉目标词,再重新 join
|
|||
|
|
// 这样避免跨词的正则副作用(如 系统自检|系统健康 → 系统系统健康)
|
|||
|
|
const tokens = rule.trigger.split('|');
|
|||
|
|
const filtered = tokens.filter(tok => tok !== 'bookworm' && tok !== '自检');
|
|||
|
|
let t = filtered.join('|');
|
|||
|
|
// 清理多余的 | 分隔符(防御性)
|
|||
|
|
t = t.replace(/\|{2,}/g, '|').replace(/^\||\|$/g, '');
|
|||
|
|
rule.trigger = t;
|
|||
|
|
r27Modified = (before !== t);
|
|||
|
|
console.log('[ITEM3] R27 trigger before:', before);
|
|||
|
|
console.log('[ITEM3] R27 trigger after :', t);
|
|||
|
|
console.log('[ITEM3]', r27Modified ? 'MODIFIED' : 'NO_CHANGE (keywords not found)');
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── Item 8: R58 — 补充 evolution-tracker boost ─────────
|
|||
|
|
let r58Modified = false;
|
|||
|
|
for (const rule of data.rules) {
|
|||
|
|
if (rule.id === 'R58') {
|
|||
|
|
if (!rule.boost) {
|
|||
|
|
rule.boost = 'evolution-tracker';
|
|||
|
|
r58Modified = true;
|
|||
|
|
console.log('[ITEM8] R58 added boost: evolution-tracker');
|
|||
|
|
} else {
|
|||
|
|
console.log('[ITEM8] R58 already has boost:', rule.boost, '— no change');
|
|||
|
|
}
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── Items 4/5/7/10: 追加新规则 R90–R93 ─────────────────
|
|||
|
|
const newRules = [
|
|||
|
|
{
|
|||
|
|
id: 'R90',
|
|||
|
|
note: 'SRE 专属场景 → sre-expert,从 devops-expert 分离',
|
|||
|
|
trigger: 'sli|slo|sla.*(?:监控|告警)|on.?call|postmortem|error.*budget|toil|事故响应|incident.*response|runbook|alert.*rule',
|
|||
|
|
boost: 'sre-expert',
|
|||
|
|
penalty: ['devops-expert'],
|
|||
|
|
weight: 0.35,
|
|||
|
|
note2: 'SRE专属场景从devops-expert中分离',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'R91',
|
|||
|
|
note: '变更影响分析 → impact-analyst,与架构咨询消歧',
|
|||
|
|
trigger: '变更影响|影响分析|影响范围|爆炸半径|依赖分析|改.*(?:会|有).*影响|change.*impact|blast.*radius|downstream.*impact|调用链.*分析|谁在.*(?:用|调用)',
|
|||
|
|
boost: 'impact-analyst',
|
|||
|
|
penalty: ['architect-expert', 'developer-expert'],
|
|||
|
|
weight: 0.35,
|
|||
|
|
note2: '变更影响分析与架构咨询消歧',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'R92',
|
|||
|
|
note: 'Google Sheets 数据分析 → data-analyst-expert (MCP: google-drive)',
|
|||
|
|
trigger: '(?:google\\s*sheets?|谷歌表格).*(?:分析|统计|可视化|透视|图表|数据清洗|pivot)',
|
|||
|
|
boost: 'data-analyst-expert',
|
|||
|
|
penalty: ['developer-expert'],
|
|||
|
|
weight: 0.30,
|
|||
|
|
preferred_mcp: 'google-drive',
|
|||
|
|
note2: 'Google Sheets数据分析场景从developer-expert分流',
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
id: 'R93',
|
|||
|
|
note: '浏览器MCP统一路由 → browser-automation-expert (playwright为主)',
|
|||
|
|
trigger: '(?:browser.?mcp|computer.?control|桌面控制).*(?:测试|自动化|操作)',
|
|||
|
|
boost: 'browser-automation-expert',
|
|||
|
|
penalty: [],
|
|||
|
|
weight: 0.25,
|
|||
|
|
preferred_mcp: 'playwright',
|
|||
|
|
note2: '浏览器MCP统一路由: playwright为主, chrome-devtools为辅, browser-mcp/computer-control-mcp为备选',
|
|||
|
|
},
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
for (const r of newRules) {
|
|||
|
|
data.rules.push(r);
|
|||
|
|
console.log(`[ITEM${r.id === 'R90' ? '4' : r.id === 'R91' ? '5' : r.id === 'R92' ? '7' : '10'}] Added rule ${r.id}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 更新元数据 ──────────────────────────────────────────
|
|||
|
|
const totalRules = data.rules.length;
|
|||
|
|
data._meta.ruleCount = totalRules;
|
|||
|
|
data._meta[SENTINEL] = true;
|
|||
|
|
data._meta.patchedAt_batchA = new Date().toISOString();
|
|||
|
|
|
|||
|
|
// 更新 changelog
|
|||
|
|
if (!data._meta.changelog) data._meta.changelog = [];
|
|||
|
|
data._meta.changelog.push(
|
|||
|
|
'R27: 移除 bookworm|自检 关键词 (已由 R81-R89 覆盖)',
|
|||
|
|
'R58: 补充 boost: evolution-tracker',
|
|||
|
|
'R90: 新增 — SRE 专属场景 sre-expert (postmortem/on-call/runbook等)',
|
|||
|
|
'R91: 新增 — 变更影响分析 impact-analyst (爆炸半径/依赖分析等)',
|
|||
|
|
'R92: 新增 — Google Sheets 数据分析 data-analyst-expert (preferred_mcp: google-drive)',
|
|||
|
|
'R93: 新增 — 浏览器MCP统一路由 browser-automation-expert (preferred_mcp: playwright)',
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
// 更新 description 中的规则数量引用 (89条 → 实际数)
|
|||
|
|
if (data._meta.description) {
|
|||
|
|
data._meta.description = data._meta.description.replace(/\d+ 条/g, `${totalRules} 条`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 写入 (UTF-8 无 BOM) ────────────────────────────────
|
|||
|
|
const output = JSON.stringify(data, null, 2) + '\n';
|
|||
|
|
fs.writeFileSync(RULES_FILE, output, 'utf8');
|
|||
|
|
|
|||
|
|
console.log('\n[DONE] disambiguation-rules.json updated.');
|
|||
|
|
console.log(` Total rules: ${totalRules}`);
|
|||
|
|
console.log(` R27 modified: ${r27Modified}`);
|
|||
|
|
console.log(` R58 modified: ${r58Modified}`);
|
|||
|
|
console.log(` New rules added: R90, R91, R92, R93`);
|
|||
|
|
|
|||
|
|
// ── JSON 验证 ──────────────────────────────────────────
|
|||
|
|
try {
|
|||
|
|
const verify = JSON.parse(fs.readFileSync(RULES_FILE, 'utf8'));
|
|||
|
|
console.log(`[VERIFY] JSON valid. rules.length=${verify.rules.length}, ruleCount=${verify._meta.ruleCount}`);
|
|||
|
|
} catch (e) {
|
|||
|
|
console.error('[VERIFY ERROR] JSON parse failed:', e.message);
|
|||
|
|
// 回滚
|
|||
|
|
fs.copyFileSync(BAK_FILE, RULES_FILE);
|
|||
|
|
console.error('[ROLLBACK] Restored from .bak');
|
|||
|
|
process.exit(1);
|
|||
|
|
}
|