Claude Code Hooks 该怎么用,用好了,事半功倍

0 阅读8分钟

Claude Code Hooks 该怎么用,用好了,事半功倍

我一开始没太注意 Hooks。

刚开始用 Claude Code,注意力基本都在命令、subagent、skills 这些地方。Hooks 这个词很不起眼。

后来用得多了,才慢慢发现自己有一些场景

  • 改完文件,我会想再跑一下格式化;
  • 准备改 .envpackage-lock.json 这种文件,会下意识想先确认一下;
  • 卡在权限确认那里时,我又不想一直守着终端;
  • 有时候一轮回复结束,也会想让它再检查一遍,不要太早停下来。

Hooks 基本就是拿来处理这些事的。Claude Code 到了某些固定时机,会把你提前挂上的动作跑一遍。这样很多原本要手动补的一步,就不用反复做了。


Hooks 是什么

“Hook” 这个词其实不算新。

很多人第一次接触它,可能是在 React 里。像 useEffectuseMemouseRef 这些 Hooks,本质上都是在组件运行的某个时机,把一段逻辑挂进去。

别的地方也有类似的东西。比如 Git 里有 pre-commitpre-push,你可以在提交前、推送前自动跑一些脚本。思路都差不多:先约定一个时机,再把动作挂上去。

官方文档里的说法很简单:Hooks 是在 Claude Code 生命周期特定节点自动执行的用户自定义命令。

拆开看,也就两层意思:

第一层,你可以挑“什么时候触发”。

比如:

  • 会话开始时
  • Claude 准备调用某个工具之前
  • 某个工具执行完成之后
  • Claude 停止回复时
  • Claude 发通知时
  • 工作目录变化时

第二层,你可以决定“触发后做什么”。

最常见的是跑一个 shell command,但官方也支持 HTTP hook、prompt hook、agent hook。

放到 Claude Code 里看,Hooks 就是在这些时机上挂动作。


先配这 4 类就够了

Claude Code 的 hook 事件现在已经很多了。官方 reference 里能看到二十多个事件,包含 PreToolUsePostToolUseNotificationStopSessionStartPermissionRequestCwdChangedFileChanged 等等。

真正常用的,先从这四类开始就够了。

1. Notification:让自己不用老盯着终端

Claude Code 在需要你确认权限、等你输入,或者有其他通知时,可以直接走系统通知。官方 guide 里给了 macOS、Linux、Windows 的示例,macOS 用 osascript 就能配。

这类 hook 很实用,因为你终于不用老盯着 terminal 了。

一个最简单的配置大概长这样:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

如果你平时会切出去写文档、回消息、看 PR,这个 hook 用起来会很顺。

2. PostToolUse:把格式化、轻量检查自动补上

官方 guide 给的例子就是:Claude 改完文件后,自动跑 Prettier。触发点放在 PostToolUse,matcher 设成 Edit|Write,也就是只在 Claude 用编辑工具改完文件之后触发。

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

这类 hook 最适合放“轻量、确定、不会拖慢太多”的动作,比如:

  • prettier --write
  • ruff format
  • biome format
  • 针对单文件的 lint 或 fix

社区里这类用法很多,思路也很直接:让 Claude 负责写,最后那一下机械整理交给 hook。

3. PreToolUse:先把危险动作拦一下

官方文档里有个例子很实用:Claude 准备编辑文件前,先检查目标路径是不是命中了受保护文件,比如 .envpackage-lock.json.git/。命中就直接 block。

这类思路很适合放在团队项目里,因为有些文件本来就不该让 agent 随便碰。

Anthropic 自己在 repo 里还给了一个 bash_command_validator_example.py。这个例子也很实用:它拦截 Claude 准备执行的 Bash 命令,如果发现用了 grepfind -name 这种你不想要的写法,就直接阻止,并给出更推荐的替代方式。

PreToolUse 不光能拦文件,也能拿来拦命令习惯。

4. Stop:在 Claude 准备停下时再推它一把

Stop 会在 Claude 完成一轮回复、准备停下时触发。社区里不少人拿它做“最后一道检查”,比如提醒 Claude:

  • 再检查一下有没有测试没跑
  • 再看一眼改动范围是不是过大
  • 再确认一遍有没有漏掉 review comment

修 bug、做重构、处理 PR 时,这种 hook 会比较有用,因为 Claude 常常在“看起来差不多”时就停下来。

不过 Stop 也是最容易配出问题的一类。官方 guide 里专门写了一个坑:Stop hook runs forever。如果你的 Stop hook 逻辑没有处理好,就可能反复触发自己,卡成循环。

所以这一类要晚一点上,而且脚本里最好带状态位判断。


怎么配

最基本的配置方式,就是在 settings 里加一个 hooks 字段。

官方 guide 的第一步写得很明确,直接改:

~/.claude/settings.json

项目级的配置也可以放在:

.claude/settings.json

写完之后,可以在 Claude Code 里输入:

/hooks

去看当前有哪些事件、哪些 hook 已经生效。

hooks 的基本结构长这样:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "your-command-here"
          }
        ]
      }
    ]
  }
}

这里最关键的几个点:

  • PostToolUse 是事件名
  • matcher 用来过滤触发条件
  • type 决定 hook 怎么运行,最常见是 command
  • command 就是实际执行的命令

Claude Code 会把事件数据用 JSON 传到 hook 的 stdin 里。你的脚本可以读这个 JSON,再决定做什么。


从浅到深,Hooks 大概可以分三层

第一层:提醒和善后

这是最容易落地的。

典型就是:

  • Notification 做系统通知
  • PostToolUse 做格式化
  • SessionStart 注入一点上下文

这一层基本不会改 Claude 的决策,只是在补动作。

第二层:约束和保护

到了这一层,hook 已经会直接影响 Claude 的行为了。

典型是:

  • PreToolUse 拦受保护文件
  • PreToolUse 拦危险 Bash 命令
  • PermissionRequest 自动放行某些很窄的权限请求

官方 guide 里给的 PermissionRequest 例子,是自动批准 ExitPlanMode。不过官方也专门提醒了一句:matcher 要尽量窄,别写成空或者 .*,否则等于把所有权限都自动放开了。

第三层:把 Hooks 用进工作流里

再往上走,Hooks 就不只是补几个小动作了,会开始接进工作流本身。

比如:

  • CwdChanged / FileChangeddirenv,自动刷新环境变量
  • ConfigChange 记录设置或 skills 变更
  • Stop 做最终检查
  • SubagentStart / SubagentStop 做日志、通知或审计

还有一个新方向是 async hooks。社区里已经有人把 voice hooks 改成 async,在后台播放提示音,不阻塞 Claude 正常继续工作。这类场景很适合放通知、日志、埋点一类动作。


我的几条经验

1. 先上轻的,再上重的

最容易失败的做法,就是一上来把 hooks 配成“每改一次文件就跑全量检查”。

这样当然也能工作,但体感会很差。Claude 每走一步都被你拖住,最后你会先把 hooks 关掉。

更稳的做法是:

  • PostToolUse 先放格式化、单文件检查
  • 全量 lint / test 放到 Stop,或者干脆放到你自己确认前再跑

2. 复杂逻辑别硬塞一行 shell

很多人会把命令直接写在 JSON 里。但真到了项目里,只要逻辑稍微复杂一点,最好单独放到脚本文件。

比如:

.claude/hooks/protect-files.sh

或者 Python 脚本。

Anthropic 自己给的 Bash validator 示例就是 Python 文件,没有把逻辑硬塞进一大串 shell。这样可读、可测试,也更好维护。

3. matcher 一定要尽量窄

尤其是 PermissionRequest 这种 hook,matcher 写宽了,后果会比普通通知 hook 大很多。

能写到具体工具名,就别写空。
能写到具体事件值,就别写全匹配。

4. Hook 里要控制输出

Claude Code 是通过 stdin / stdout / stderr / exit code 和 hook 通信的。

这意味着脚本输出不能太随便。

有一个很常见的坑:你的 shell profile 里如果有无条件 echo,hook 输出的 JSON 前面就会混进一段无关文本,最后直接 JSON 解析失败。

这类问题平时不太容易想到,排查时要记得回头看看这里。

5. Stop hook 要防循环

这一条值得单独记一下。

很多人会很自然地想把更多检查塞到 Stop 里,但 Stop 处理不好最容易出现“停不下来”的循环触发。官方 guide 已经把这个列成单独的 troubleshooting 条目了。

所以 Stop 更适合做一个明确、可终止的收尾检查,不要塞太长太重的一串流程。

6. 先拿 Hooks 做“确定性动作”,别拿它替代判断

Hooks 最适合的是确定性规则:

  • 改完就格式化
  • 动某类文件就拦
  • 某种通知就提醒
  • 目录变化就刷新环境

而那种需要强判断、强上下文理解的事,还是交给 Claude 本身、prompt hook、agent hook,或者干脆让人来决定,会更稳一点。

Hooks 一旦承担了太多“判断”,后面通常会越来越难维护。


我会怎么开始配

如果从零开始配 Claude Code Hooks,我会按这个顺序来:

  1. 先上 Notification
  2. 再上 PostToolUse 的 formatter
  3. 然后加一个 PreToolUse 的保护文件脚本
  4. 用一段时间之后,再考虑 StopPermissionRequest

这样上手比较快,也不容易一开始就把体验配坏。


最后

如果你现在刚开始用 Claude Code,其实不用急着把 Hooks 一次配很满。

先上通知,再上格式化,再补一个保护文件的 PreToolUse,已经能解决不少实际问题。

等你真的开始长时间用它写代码、改 PR、跑调试,再慢慢加 StopPermissionRequest、环境刷新这一类 hook,会更顺一点。


参考链接