#!/usr/bin/env node /** * patch-r5-merge-matcher.js · 2026-04-26 * 合并 agent-isolation-gate.js 的 3 个独立 PreToolUse 注册 (Bash/Write/Edit) * 为单一 matcher 'Bash|Write|Edit', 减少 hook 启动开销 3x → 1x * * Idempotent: 检测合并后的单一条目存在则跳过 * 操作 settings.json AST (JSON.parse), 不用文本替换防破坏 */ 'use strict'; const fs = require('fs'); const path = require('path'); const SETTINGS = path.join(process.env.HOME || process.env.USERPROFILE || 'C:/Users/leesu', '.claude', 'settings.json'); const GATE_CMD = 'node C:/Users/leesu/.claude/hooks/agent-isolation-gate.js'; const MERGED_MATCHER = 'Bash|Write|Edit'; try { const raw = fs.readFileSync(SETTINGS, 'utf8'); const cfg = JSON.parse(raw); const pre = (cfg.hooks && cfg.hooks.PreToolUse) || []; // 找出 agent-isolation-gate 相关条目 const gateIdx = []; pre.forEach((entry, i) => { const cmds = (entry.hooks || []).map(h => h.command || ''); if (cmds.some(c => c.includes('agent-isolation-gate.js'))) gateIdx.push(i); }); if (gateIdx.length === 0) { console.error('[r5-merge] no agent-isolation-gate entries found, abort'); process.exit(1); } if (gateIdx.length === 1 && pre[gateIdx[0]].matcher === MERGED_MATCHER) { console.log('[r5-merge] already applied, skip'); process.exit(0); } fs.writeFileSync(SETTINGS + '.bak.r5merge.' + Date.now(), raw, 'utf8'); // 删除所有旧条目, 在第一个位置插入合并条目 const merged = { matcher: MERGED_MATCHER, hooks: [{ type: 'command', command: GATE_CMD, timeout: 1500 }] }; // 倒序删除 const sortedDesc = [...gateIdx].sort((a, b) => b - a); const insertAt = gateIdx[0]; for (const i of sortedDesc) pre.splice(i, 1); pre.splice(insertAt, 0, merged); fs.writeFileSync(SETTINGS, JSON.stringify(cfg, null, 2), 'utf8'); console.log('[r5-merge] applied (merged ' + gateIdx.length + ' entries → 1)'); } catch (e) { console.error('[r5-merge] error:', e.message); process.exit(1); }