bookworm-smart-assistant/scripts/patches/patch-stop-dispatcher-24h-dedup.js

109 lines
3.8 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
/**
* patch-stop-dispatcher-24h-dedup.js
* P2: stop-dispatcher.js consistency-sentinel 24h 日级去重
* 修复: (1) 同日不重复写 sentinel (2) version stats-compiled.json 动态读取
* Idempotent: sentinel 防重复
*/
'use strict';
const fs = require('fs');
const path = require('path');
const CLAUDE_ROOT = path.join(process.env.USERPROFILE || process.env.HOME, '.claude');
const HOOKS_DIR = path.join(CLAUDE_ROOT, 'hooks');
const STATE_DIR = path.join(CLAUDE_ROOT, 'session-state');
const SENTINEL = path.join(STATE_DIR, 'patch-stop-dispatcher-24h-dedup.done');
const TARGET = path.join(HOOKS_DIR, 'stop-dispatcher.js');
if (fs.existsSync(SENTINEL)) {
console.log('[SKIP] patch-stop-dispatcher-24h-dedup already applied');
process.exit(0);
}
if (!fs.existsSync(TARGET)) {
console.error('[FAIL] Target not found: ' + TARGET);
process.exit(1);
}
const ANCHOR_DEDUP = ` if (findings.length > 0 && fs.existsSync(evoLog)) {`;
const PATCH_DEDUP = ` if (findings.length > 0 && fs.existsSync(evoLog)) {
// 24h 日级去重: 同日已有 consistency-sentinel 则跳过 (fix_count>0 的除外)
const today = new Date().toISOString().slice(0, 10);
try {
const tail = fs.readFileSync(evoLog, 'utf8').trim().split('\\n').slice(-30);
const fixable = findings.some(f => f.fix === 'auto');
const hasTodaySentinel = tail.some(l => {
try { const e = JSON.parse(l); return e.scope === 'consistency-sentinel' && e.ts === today; } catch { return false; }
});
if (hasTodaySentinel && !fixable) return;
} catch {}`;
const ANCHOR_VER = ` version: 'v6.5.1',`;
const PATCH_VER = ` version: (() => { try { return JSON.parse(fs.readFileSync(path.join(sRoot, 'stats-compiled.json'), 'utf8')).version || 'v6.6.0'; } catch { return 'v6.6.0'; } })(),`;
try {
const bak = TARGET + '.bak-24h-dedup-' + Date.now();
fs.copyFileSync(TARGET, bak);
console.log('[OK] Backup: ' + path.basename(bak));
let src = fs.readFileSync(TARGET, 'utf8');
let patches = 0;
// Patch 1: 24h dedup
if (src.includes('hasTodaySentinel')) {
console.log('[SKIP] 24h dedup already present');
} else {
const anchorLF = ANCHOR_DEDUP;
const anchorCRLF = ANCHOR_DEDUP.replace(/\n/g, '\r\n');
if (src.includes(anchorCRLF)) {
src = src.replace(anchorCRLF, PATCH_DEDUP.replace(/\n/g, '\r\n'));
patches++;
} else if (src.includes(anchorLF)) {
src = src.replace(anchorLF, PATCH_DEDUP);
patches++;
} else {
console.error('[WARN] Dedup anchor not found');
}
}
// Patch 2: dynamic version
if (src.includes("version: 'v6.5.1'")) {
const anchorLF = ANCHOR_VER;
const anchorCRLF = ANCHOR_VER.replace(/\n/g, '\r\n');
if (src.includes(anchorCRLF)) {
src = src.replace(anchorCRLF, PATCH_VER.replace(/\n/g, '\r\n'));
patches++;
} else if (src.includes(anchorLF)) {
src = src.replace(anchorLF, PATCH_VER);
patches++;
}
} else {
console.log('[SKIP] version already dynamic or updated');
}
if (patches === 0) {
console.log('[SKIP] No changes needed');
process.exit(0);
}
const tmp = TARGET + '.tmp.' + process.pid;
fs.writeFileSync(tmp, src, 'utf8');
fs.renameSync(tmp, TARGET);
console.log('[OK] ' + path.basename(TARGET) + ' updated (' + patches + ' patches)');
if (!fs.existsSync(STATE_DIR)) fs.mkdirSync(STATE_DIR, { recursive: true });
fs.writeFileSync(SENTINEL, JSON.stringify({
applied: new Date().toISOString(),
target: path.basename(TARGET),
backup: path.basename(bak),
patches: patches,
fixes: ['24h-dedup', 'dynamic-version']
}, null, 2), 'utf8');
console.log('[DONE] patch-stop-dispatcher-24h-dedup applied');
} catch (err) {
console.error('[FAIL] ' + err.message);
process.exit(1);
}