#!/usr/bin/env node /** * PreToolUse:Bash 合并调度器 * 合并 block-dangerous-commands + code-quality-gate + commit-message-lint * 减少 3 次 Node.js 进程启动为 1 次 (节省 ~80-100ms) * * 优先级: * 1. block-dangerous-commands (安全, fail-close) * 2. code-quality-gate (构建追踪, fail-open) * 3. commit-message-lint (commit 规范, fail-open) * * 退出码: 0=放行, 2=阻断/ask (stderr 输出 JSON) */ const readStdin = require('./lib/read-stdin.js'); readStdin({ maxSize: 1024 * 1024 }).then(input => { const command = (input.tool_input && input.tool_input.command) || ''; if (!command) { process.exit(0); return; } // ─── 1. block-dangerous-commands (最高优先级, fail-close) ─── // 安全检查: 异常时 ask (fail-closed) try { const bdc = require('./block-dangerous-commands.js'); if (bdc.checkCommand) { const result = bdc.checkCommand(command, input); if (result) { // B5: CLAUDE_SAFETY_MODE=strict 时 ask 升级为 deny let decision = result.decision; // 'deny' 或 'ask' if (decision === 'ask' && process.env.CLAUDE_SAFETY_MODE === 'strict') { decision = 'deny'; result.message += '\n(CLAUDE_SAFETY_MODE=strict: ask 已升级为 deny)'; } const output = { hookSpecificOutput: { permissionDecision: decision, }, systemMessage: result.message, }; process.stderr.write(JSON.stringify(output)); process.exit(2); return; } } } catch (e) { // fail-closed: 安全模块异常时请求用户确认 (strict 模式下直接 deny) const failDecision = process.env.CLAUDE_SAFETY_MODE === 'strict' ? 'deny' : 'ask'; const failSuffix = failDecision === 'deny' ? '\n(CLAUDE_SAFETY_MODE=strict: 异常时直接拒绝)' : ''; process.stderr.write(JSON.stringify({ hookSpecificOutput: { permissionDecision: failDecision }, systemMessage: `[安全防护] 命令检查模块异常(${(e.message || '').slice(0, 80)}),${failDecision === 'deny' ? '已拒绝执行' : '请用户确认是否执行'}。${failSuffix}`, })); process.exit(2); return; } // ─── 2. code-quality-gate (构建命令, fail-open) ─── try { const cqg = require('./code-quality-gate.js'); if (cqg.checkBuild) { const result = cqg.checkBuild(command, input); if (result) { if (result.decision === 'deny') { // enforce 模式阻断 process.stderr.write(JSON.stringify({ hookSpecificOutput: { permissionDecision: 'deny' }, systemMessage: result.message, })); process.exit(2); return; } if (result.decision === 'warn') { // warn 模式: 放行但附带提醒,继续后续检查 // (warn 消息将与后续检查结果合并) const output = { continue: true, systemMessage: result.message, }; process.stdout.write(JSON.stringify(output)); process.exit(0); return; } } } } catch { // fail-open: 构建检查异常时静默放行 } // ─── 3. commit-message-lint (git commit, fail-open) ─── try { const cml = require('./commit-message-lint.js'); if (cml.checkCommit) { const result = cml.checkCommit(command, input); if (result) { process.stderr.write(JSON.stringify({ hookSpecificOutput: { permissionDecision: result.decision, // 'ask' }, systemMessage: result.message, })); process.exit(2); return; } } } catch { // fail-open: commit-lint 异常时静默放行 } // 全部通过 → 放行 process.exit(0); }).catch(() => { // stdin 读取/解析失败 → fail-closed (strict 模式下升级为 deny) const isStrict = process.env.CLAUDE_SAFETY_MODE === 'strict'; const decision = isStrict ? 'deny' : 'ask'; process.stderr.write(JSON.stringify({ hookSpecificOutput: { permissionDecision: decision }, systemMessage: `[bash-precheck-dispatcher] stdin 解析异常,${isStrict ? '已拒绝执行' : '请用户确认是否执行'}。`, })); process.exit(2); });