109 lines
3.8 KiB
JavaScript
109 lines
3.8 KiB
JavaScript
|
|
#!/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);
|
||
|
|
}
|