#!/usr/bin/env node /** * patch-evo-log-dedup.js * P2 去噪: evolution-log.jsonl 去重 + seq 重编号 * 规则: * - consistency-sentinel 同日同内容只保留首条 * - sig-drift(auto) 有 fix_count>0 的保留 * - 所有条目按时间排序后重编号 seq * 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 EVO_LOG = path.join(CLAUDE_ROOT, 'evolution-log.jsonl'); const STATE_DIR = path.join(CLAUDE_ROOT, 'session-state'); const SENTINEL = path.join(STATE_DIR, 'patch-evo-log-dedup.done'); if (fs.existsSync(SENTINEL)) { console.log('[SKIP] patch-evo-log-dedup already applied'); process.exit(0); } if (!fs.existsSync(EVO_LOG)) { console.error('[FAIL] evolution-log.jsonl not found'); process.exit(1); } try { const bak = EVO_LOG + '.bak-dedup-' + Date.now(); fs.copyFileSync(EVO_LOG, bak); console.log('[OK] Backup: ' + path.basename(bak)); const raw = fs.readFileSync(EVO_LOG, 'utf8').trim(); const lines = raw.split('\n').map(l => { try { return JSON.parse(l); } catch { return null; } }).filter(Boolean); const before = lines.length; const kept = []; const sentinelSeen = new Map(); for (const entry of lines) { if (entry.scope === 'consistency-sentinel' && entry.trigger === 'stop-dispatcher') { const key = entry.ts + '::' + entry.summary; if (sentinelSeen.has(key)) continue; const dayKey = entry.ts; const dayEntries = lines.filter(e => e.scope === 'consistency-sentinel' && e.trigger === 'stop-dispatcher' && e.ts === dayKey ); const hasSameDay = kept.some(e => e.scope === 'consistency-sentinel' && e.trigger === 'stop-dispatcher' && e.ts === dayKey ); if (hasSameDay && (!entry.fix_count || entry.fix_count === 0)) { sentinelSeen.set(key, true); continue; } sentinelSeen.set(key, true); } kept.push(entry); } let seq = 1; for (const entry of kept) { entry.seq = seq++; } const output = kept.map(e => JSON.stringify(e)).join('\n') + '\n'; const tmp = EVO_LOG + '.tmp.' + process.pid; fs.writeFileSync(tmp, output, 'utf8'); fs.renameSync(tmp, EVO_LOG); const after = kept.length; const removed = before - after; console.log('[OK] evolution-log: ' + before + ' → ' + after + ' entries (removed ' + removed + ' duplicates)'); console.log('[OK] seq renumbered 1..' + after); if (!fs.existsSync(STATE_DIR)) fs.mkdirSync(STATE_DIR, { recursive: true }); fs.writeFileSync(SENTINEL, JSON.stringify({ applied: new Date().toISOString(), before: before, after: after, removed: removed, backup: path.basename(bak) }, null, 2), 'utf8'); console.log('[DONE] patch-evo-log-dedup applied'); } catch (err) { console.error('[FAIL] ' + err.message); process.exit(1); }