Hooks 机制:在工具调用的前后插你自己的逻辑

为什么需要 Hooks

前面讲过 CLAUDE.md 是"软引导"——模型可能遵守也可能忘。当你需要的是确定性——每次编辑后必须格式化、碰敏感文件必须拦截、会话开始必须注入上下文——就需要 Hooks。Hook 是你定义的 shell 命令,绑定在 Claude Code 的生命周期事件上,到点必执行,不经过模型的自由裁量。

打个比方:CLAUDE.md 像贴在墙上的操作规程,Hook 像流水线上的机械臂——规程靠人自觉,机械臂不管你自觉不自觉,该焊就焊。

事件全景:挂在哪里

Hook 事件覆盖了会话的完整生命周期,常用的有:

事件 触发时机 典型用途
SessionStart 会话启动(含 compact 后) 注入关键上下文
UserPromptSubmit 你提交一条输入 注入数据、校验输入
PreToolUse 工具调用执行前 拦截危险操作
PostToolUse 工具调用完成后 自动格式化、触发检查
Notification Claude 需要你的注意 桌面通知
Stop Claude 完成响应 通知、统计
SubagentStop 子代理完成 收尾动作
PreCompact 压缩前 抢救重要上下文
FileChanged 文件变化时 监控特定文件
SessionEnd 会话结束 归档转录、清理

完整清单见官方 Hooks 参考。配置写在 settings.json 的 hooks 键下,事件名作键名,值是"匹配器 + 命令"数组。

三个实战配置,从易到难

第一例:桌面通知(最简单的入门 Hook)。Claude 干完活等你回来时提醒你,摸鱼神器:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code 需要你\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

matcher 留空表示所有通知类型都触发。Linux 用 notify-send,Windows 用 PowerShell 弹窗,官方文档都有现成命令。

第二例:编辑后自动格式化(最常用的生产力 Hook):

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}

结构解读:PostToolUse 事件 + Edit|Write 匹配器(正则,限定只在文件编辑类工具后触发);命令通过 jq 从 stdin 的 JSON 里抽出被编辑文件路径,喂给 Prettier。从此 Claude 的每次改动自动统一格式,代码审查里再也不会出现"格式不齐"的噪音。

第三例:保护敏感文件(安全向 Hook)。用脚本拦截对 .env 等文件的修改:

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

for pattern in ".env" "package-lock.json" ".git/"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "Blocked: $FILE_PATH 命中保护规则 '$pattern'" >&2
    exit 2    # 退出码 2 = 阻止该操作,stderr 内容会反馈给 Claude
  fi
done
exit 0
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}

关键机制在退出码:正常退出(0)放行;退出码 2 阻止该工具调用,stderr 的文字会作为反馈传给 Claude,它会理解原因并换路。$CLAUDE_PROJECT_DIR 是项目根路径变量,让脚本路径不依赖启动目录。

stdin JSON 与 stdout 的玩法

每个 Hook 执行时都会从 stdin 收到一个 JSON,包含事件详情(工具名、参数、会话信息等),上面用 jq 抽字段就是读它。反过来,命令写到 stdout 的纯文本会被注入对话——这让 Hook 变成了上下文注入器。经典案例:压缩后重注入关键信息(SessionStart 事件配 compact 匹配器):

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo '提醒:本项目用 Bun 不用 npm;提交前跑 bun test;当前冲刺:鉴权重构'"
          }
        ]
      }
    ]
  }
}

压缩会丢对话里的细节,这个 Hook 保证每次压缩后关键约束自动回来——和 CLAUDE.md 双保险。

让 Claude 帮你写 Hook

写 hook 配置本身也是个编码任务,完全可以让 Claude 代劳——直接在会话里描述需求:"给我加一个 hook:每次编辑 TS 文件后跑 eslint --fix"。它会把 JSON 配置和脚本一起写好。写完的验证三板斧:

  1. /hooks 面板确认注册成功;
  2. 切到 Manual 模式,触发一次对应动作,观察行为;
  3. 不对劲时 claude --debug 启动看 hook 的执行日志,会话内也可以 /debug 开启调试日志。

常见坑与安全边界

踩坑清单按出现频率排序:

  • 脚本没有执行权限:macOS/Linux 上忘了 chmod +x,hook 静默失败。挂了先查这个;
  • Windows 路径分隔符:JSON 输入里的路径是反斜杠,脚本里先做 \\ → / 归一(官方示例里有现成写法);
  • jq 依赖:示例都靠 jq 解析 stdin JSON,新机器记得装(brew install jq / apt-get install jq);
  • 通知不响:macOS 的 osascript 走"脚本编辑器"应用转发,要去系统设置里给它开通知权限——第一次配置必踩;
  • Hook 太慢拖累每次编辑:把重活改成异步或降频,工具调用路径上的 hook 是热路径。

安全边界方面:hook 本质是"仓库里声明、在你机器上执行的代码",所以共享项目的 hook 和 MCP 服务器一样受工作区信任机制保护——信任确认前不执行。PreToolUse 的拦截决定由 Claude Code 强制执行而非模型自觉,这让 hook 成了比 CLAUDE.md 更可靠的守门员;但也因此 hook 脚本要进代码评审——它和 CI 脚本同级,出 bug 影响所有开发者。写 hook 时遵守最小权限:脚本只做一件事,别在拦截器里顺便干别的。

进阶形态与工程纪律

Hook 还有两种"聪明形态":prompt 型 Hook(用模型判断条件是否满足)和 agent 型 Hook(用一个小代理执行判断逻辑),适合"判断是否允许"这类需要理解语义而非匹配字符串的场景。Hook 也可以配置成后台运行不阻塞主流程。这些进阶配置字段见官方参考。

工程纪律三条,都是踩过坑的总结:

  1. Hook 要快。PreToolUse / PostToolUse 在工具调用路径上,Hook 慢 = 每次编辑都慢。格式化类 Hook 控制在秒级;
  2. /hooks 是验证面板。写完配置先跑 /hooks 确认注册成功,再实际触发一次验证行为;
  3. 脚本体放 .claude/hooks/ 目录,settings.json 里只留一行命令引用——配置文件保持可读,脚本单独演进。

安全提醒:Hook 是仓库 settings 里可提交的,克隆别人仓库时会遇到工作区信任确认——确认前那些 Hook 不会执行,这个闸门别嫌烦,它是防"恶意仓库用 Hook 干坏事"的关键设计。

本章由 WorkBuddy 原创整理,事实性内容参考 Claude Code 官方文档。