TodayAI

GuidesAI 编程

用 Claude Code Hooks 拦截关键操作

在 settings.json 里配置 PreToolUse 等 hooks,在工具执行前拦截危险命令或保护敏感文件。

基于 Claude Code Docs 整理 · 官方资料 ↗

权限规则适合声明“允许 / 询问 / 拒绝”哪些工具。Hooks 则更进一步:在 Claude Code 生命周期的确定点,自动跑你的命令或脚本,用确定性逻辑拦截、审计或补充上下文。

官方 Hooks reference 列出全部事件;入门最有用的是 PreToolUse——在工具真正执行前触发,可以阻断。配套的 Hooks guide 提供了通知、格式化、保护文件等现成例子。

这篇先注册一个可验证的 PreToolUse 拦截,再说明 matcher、exit code 与 settings 放哪里。

Hooks 解决什么问题

Hooks 是用户定义的 shell 命令、HTTP 端点或 LLM prompt,在会话生命周期固定点执行。终端、IDE、Desktop 与 Claude Code on the web 都会触发同一套事件。

常见节奏:SessionStart / SessionEnd 每会话一次;UserPromptSubmit / Stop 每轮一次;PreToolUse / PostToolUse 在 agent 循环的每次工具调用时触发。要拦截危险操作,优先 PreToolUse。

与只靠 CLAUDE.md 叮嘱不同,hook 是强制执行的:匹配到就跑你的脚本,不依赖模型是否“想起来”遵守规则。

配置写在哪里

在 settings 的 hooks 字段注册。常用位置:

  • ~/.claude/settings.json:对本机所有项目生效
  • .claude/settings.json:仅当前项目,可提交仓库共享
  • .claude/settings.local.json:本机个人覆盖(注意勿提交密钥)

用会话内 /hooks 浏览已注册事件(只读)。新增或修改需改 JSON,或让 Claude 帮你改 settings。

用 PreToolUse 拦截危险 Bash

官方示例用 matcher: "Bash",再配合 if: "Bash(rm *)" 收窄到删除类命令。脚本从 stdin 读 JSON,若命令含 rm -rf,则返回 permissionDecision: "deny"。

创建拦截脚本并赋予可执行权限

bash
mkdir -p .claude/hooks
cat > .claude/hooks/block-rm.sh <<'EOF'
#!/bin/bash
COMMAND=$(jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q 'rm -rf'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by hook"
    }
  }'
else
  exit 0
fi
EOF
chmod +x .claude/hooks/block-rm.sh

写入 .claude/settings.json 的 hooks

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

解析 JSON 依赖 jq,需已安装并在 PATH 中。改完 settings 后重新进入项目会话,再让 Claude 执行含 rm -rf 的命令验证是否被拦。

保护 .env 等敏感文件

Hooks guide 的另一常见模式:在 Edit|Write 前检查 file_path,命中 .env、package-lock.json、.git/ 等模式就 exit 2 阻断。

protect-files.sh(官方 guide 思路)

bash
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
FILE_PATH="${FILE_PATH//\\//}"

PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
    exit 2
  fi
done

exit 0

PreToolUse matcher: Edit|Write

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

exit 2 会阻断工具调用,并把 stderr 反馈给 Claude。exit 0 且不输出 deny 决策时,仍走正常权限流程。

stdin / exit code 怎么用

事件触发时,Claude Code 把 JSON 喂给命令 stdin。PreToolUse 常见字段包括 hook_event_name、tool_name、tool_input(Bash 时含 command)。

官方示例:Bash npm test 的 hook 输入形态

json
{
  "session_id": "abc123",
  "cwd": "/Users/sarah/myproject",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  }
}
  • exit 0:不通过 exit code 表示反对;可配合 JSON permissionDecision
  • exit 2:阻断(PreToolUse 会阻止工具调用)
  • 多个 hook 同时匹配时都会跑完,再按最严格决策合并(deny > ask > allow)

怎么确认 hook 生效

  • 会话内 /hooks 能看到对应事件与命令
  • 故意触发 rm -rf 或编辑 .env 时被阻断
  • 阻断原因出现在反馈里(JSON reason 或 stderr)
  • 脚本路径错误时会出现非阻塞失败提示——策略型 hook 首次运行要盯住这个

需要完整 schema、HTTP hooks、async hooks 时,回到 Hooks reference。日常自动化示例优先看 Hooks guide。

最容易踩的坑

hook 从未运行

检查 settings 路径与 JSON 是否合法;确认 matcher 匹配工具名;用 /hooks 核对是否加载;脚本需 chmod +x。

jq: command not found

安装 jq(macOS: brew install jq)。官方 Bash 示例依赖它解析 stdin JSON。

脚本失败但危险命令仍执行

许多事件上,hook 启动失败是非阻塞的。策略 hook 首次一定要确认路径可执行,不要假设“写了 JSON 就生效”。

想拦截却用了 PostToolUse

PostToolUse 在成功之后。要阻止执行,必须用 PreToolUse(或相应决策事件)。

官方资料

Claude Code Docs

Hooks reference