#!/usr/bin/env node /** * patch-p2-handoff-mechanism.js * * P2 /handoff 机制改造 patch: * 1. context-pressure-monitor.js: CRITICAL 消息改为建议 /handoff * 2. pre-compact-handoff.js: 添加过期 handoff 文件清理 (保留最新 5 个) * 3. CLAUDE.md: 上下文管理节添加 /handoff 说明 * 4. SKILL-REGISTRY.md: 添加 handoff 技能条目 + 更新统计 * * 幂等: 每处修改前检查 sentinel,已 patch 则跳过 */ 'use strict'; const fs = require('fs'); const path = require('path'); const CLAUDE_ROOT = path.join(require('os').homedir(), '.claude'); const HOOKS_DIR = path.join(CLAUDE_ROOT, 'hooks'); const results = []; function patchFile(filePath, label, patches) { if (!fs.existsSync(filePath)) { results.push(`[SKIP] ${label}: 文件不存在 ${filePath}`); return false; } const bak = filePath + '.bak'; let content = fs.readFileSync(filePath, 'utf8'); let changed = false; for (const p of patches) { if (p.sentinel && content.includes(p.sentinel)) { results.push(`[SKIP] ${label}/${p.name}: sentinel 已存在`); continue; } if (p.find && !content.includes(p.find)) { results.push(`[SKIP] ${label}/${p.name}: 未找到目标文本`); continue; } if (p.find) { content = content.replace(p.find, p.replace); } else if (p.insertAfter) { const idx = content.indexOf(p.insertAfter); if (idx === -1) { results.push(`[SKIP] ${label}/${p.name}: 未找到插入锚点`); continue; } const insertAt = idx + p.insertAfter.length; content = content.slice(0, insertAt) + p.insert + content.slice(insertAt); } changed = true; results.push(`[OK] ${label}/${p.name}`); } if (changed) { if (!fs.existsSync(bak)) fs.copyFileSync(filePath, bak); fs.writeFileSync(filePath, content, 'utf8'); } return changed; } // === 1. context-pressure-monitor.js: CRITICAL 消息 === patchFile( path.join(HOOKS_DIR, 'context-pressure-monitor.js'), 'context-pressure-monitor', [{ name: 'critical-msg-handoff', sentinel: '/handoff', find: 'dump 当前关键决策到 .bookworm-progress.md, 然后请用户 /clear', replace: '调用 /handoff 保存当前进度到 .bookworm-progress.md, 然后请用户 /clear' }] ); // === 2. pre-compact-handoff.js: 添加过期 handoff 清理 === patchFile( path.join(HOOKS_DIR, 'pre-compact-handoff.js'), 'pre-compact-handoff', [{ name: 'cleanup-old-handoffs', sentinel: 'PATCH-P2-HANDOFF-CLEANUP', insertAfter: 'fs.renameSync(_tmpHandoff, HANDOFF_PATH);', insert: ` // [PATCH-P2-HANDOFF-CLEANUP] 清理过期 handoff 时间戳文件, 保留最新 5 个 try { const files = fs.readdirSync(SESSION_STATE_DIR) .filter(f => /^handoff-\\d+\\.json$/.test(f)) .map(f => ({ name: f, time: parseInt(f.match(/\\d+/)[0], 10) })) .sort((a, b) => b.time - a.time); const toDelete = files.slice(5); for (const f of toDelete) { try { fs.unlinkSync(path.join(SESSION_STATE_DIR, f.name)); } catch {} } } catch {} ` }] ); // === 3. CLAUDE.md: 上下文管理节添加 /handoff === patchFile( path.join(CLAUDE_ROOT, 'CLAUDE.md'), 'CLAUDE.md', [{ name: 'handoff-doc', sentinel: '/handoff', insertAfter: '- 长会话(>20 轮工具调用)时主动建议 `/clear` 重置上下文', insert: '\n- **主动交接** (P2): 上下文压力 CRITICAL 时自动建议 `/handoff`; 手动 `/handoff` 可随时调用, 将进度写入 `.bookworm-progress.md` + 生成继续提示词 + 清理过期 handoff JSON' }] ); // === 4. SKILL-REGISTRY.md: 添加 handoff 条目 === const registryPath = path.join(CLAUDE_ROOT, 'SKILL-REGISTRY.md'); patchFile(registryPath, 'SKILL-REGISTRY', [ { name: 'add-handoff-entry', sentinel: '| 102 | handoff', insertAfter: '| 101 | mcp-prune | stable | MCP 剪枝分析工具 (Phase 1 · T1.4)。基于使用率识别低频 MCP 候选,生成剪枝 plan。永不自动修改 .claude.json。 |', insert: '\n| 102 | handoff | stable | 上下文交接/进度保存/.bookworm-progress.md 生成/继续提示词 |' }, { name: 'update-skill-count', sentinel: '95 (', find: '- **总计**: 94 (', replace: '- **总计**: 95 (' } ]); // === 输出摘要 === console.log('=== patch-p2-handoff-mechanism ==='); for (const r of results) console.log(r); console.log('=== done ===');