Claude Code 的 hooks 我现在每个项目都配这 5 个
一次差点被 fire 的 commit
2025 年 11 月,给一家做营销 SaaS 的甲方做后端重构。Claude Code 接了他们一个 14 万行的 Node 仓库,我那周手速也快,Opus 跑着跑着就把一个 shared util 文件给改了。表面看 tsc 过了、vitest 过了,PR 也被 review 通过。
然后 merge 到 main 的 37 分钟后,Grafana 告警拉响——一个正则没转义,所有 webhook 签名校验失败。用户投诉 1247 条涌进来。
回滚花了 8 分钟。我复盘那天晚上的 transcript,发现 Claude 其实跑完 Edit 之后没跑 lint,而项目里有个自定义 ESLint 规则会抓这个模式。它只是不知道要跑。
从那之后我给每个新项目进 Claude Code 之前,一定先配好 5 个 hook。不是洁癖,是真被吓到了。
先说清楚 Claude Code 的 hook 到底有几个入口
官方目前稳定的 hook 点我常用这几个:PreToolUse、PostToolUse、UserPromptSubmit、Stop、SessionStart。配置走 .claude/settings.json 或者 settings.local.json,格式大致是:
1 | { |
matcher 支持正则,所以 Edit|Write|MultiEdit 这种合并写法很省事。每个 hook 拿到的 payload 从 stdin 进来,是个 JSON,想拦截的话 exit code 非 0 就行。
下面这 5 个就是我跑了大半年沉淀的固定配置。
Hook 1:PostToolUse 自动跑 lint/format
最基础但最救命的一个。Claude 每次 Edit/Write/MultiEdit 之后,自动触发项目自己的 lint 和 format。关键是要把错误回灌给 Claude,这样它下一步会自己修。
我的 Node 项目模板:
1 | { |
脚本里我干这几件事:读 stdin 拿到被改的文件路径,按扩展名路由(.ts/.tsx 走 eslint –fix + prettier,.py 走 ruff + black,.go 走 gofmt + golangci-lint),有 error 就 echo 到 stderr 并 exit 2。exit 2 是 Claude Code 专门的约定,stderr 内容会被塞回模型的下一轮上下文。
之前我图省事写 exit 1,Claude 完全看不到错误信息,一脸懵地继续往下跑。后来翻文档才知道必须 2。真要命,这种细节官方藏得挺深。
Python 项目的差异是 ruff 比较快但 mypy 慢,所以我 mypy 只在 Stop hook 里跑一次,不在 PostToolUse 里跑。不然每次编辑都卡 6-8 秒,Claude 的节奏会被打乱。
Hook 2:PreToolUse 拦截危险操作
这个就是我那次事故之后写的。规则很简单:所有 Bash 调用先过一层关键词筛子。
1 | { |
guard 脚本里的黑名单(从 stdin 读 tool_input.command 然后 grep):
rm -rf /以及任何作用在/、~、$HOME的递归删除git push --force到任何带main|master|prod|release的分支git reset --hard超过 10 个 commit 往前DROP TABLE、TRUNCATE在生产连接串上chmod 777任何目录
匹配上就 exit 2 并输出拒绝原因。Claude 收到后 84.3% 的情况会自己换一个更安全的写法,剩下 15.7% 会在下一轮请求用户 confirm——这个比例是我翻了自己最近 412 次触发记录统计出来的,还挺靠谱。
顺便说,这东西的权限模式我之前也被坑过,想展开聊的可以看我另一篇 Claude Code 权限模式(虽然那篇主要比三家工具,但权限部分讲得比较细)。
Hook 3:UserPromptSubmit 自动注入项目状态
这个是后期加的,但加了之后 Claude 的”上下文感”提升非常明显。用户每次提交 prompt 之前,我往上下文里塞一段项目快照:
- 当前 git 分支
- 最近 3 条 commit 的 hash 和 subject
- 未 commit 的文件数(
git status --short | wc -l) - 当前开着的 PR 号码(如果有,通过
gh pr status) - 今天是周几(我自己 debug 方便)
脚本输出的字符串会被拼到用户 prompt 前面。我控制在 280 字以内,不然每次都多烧 token。
好处是 Claude 不会再问 “你现在在哪个分支” 这种废话,也不会傻乎乎建议我 git checkout main 而忽略我脏了一堆文件没提交。一个同事说”感觉 Claude 变聪明了”——其实只是它终于有视力了。
为什么这个要放在 prompt 之前而不是 system?因为系统提示词会被缓存,你每次都变动会打破 cache。想深入理解这个可以翻 系统角色与位置放置 和 Prompt Caching 深度指南。
Hook 4:Stop 自动写会话摘要
Claude 每次结束对话(Stop 触发),我让它把这次会话摘要写到 .claude/logs/YYYY-MM-DD-HHMMSS.md。格式固定:
1 | ## 任务 |
实现方式是 Stop hook 调一个小脚本,脚本里再触发一次 Claude API 的 Haiku 4.5(便宜,$0.0037 一次摘要)读最近 transcript 生成。
这东西最大的用处是第二天继续干活的时候。我打开项目第一件事是 ls -t .claude/logs/ | head -5,看昨天卡在哪。之前没有这个习惯的时候,我经常花 15 分钟重新”找回状态”。现在平均 3 分钟。
想了解 agent 长期记忆的完整思路可以看 长期记忆与 Agent 状态,hook 写摘要本质上就是人工版的 episodic memory。
Hook 5:SessionStart 拉依赖 + 健康检查
每次我打开 Claude Code 会话,SessionStart 触发:
git fetch origin看远端有没有更新npm install或者uv sync(根据项目类型)静默跑- 跑一个 10 秒的 smoke test(我们项目里是
make health,其实就是 curl 几个 internal endpoint) - 把结果摘要塞进 session context
目的是让 Claude 一上来就知道”依赖是最新的、本地服务是活的”。之前没这个 hook 的时候,Claude 经常花一轮对话去诊断”为什么 import 失败”,最后发现是 node_modules 没装。
一个副作用是团队新人接手项目也轻松了。以前新人 clone 下来要翻 README 照抄一堆命令,现在 Claude Code 一跑 SessionStart 全搞定。一个实习生原话:”这个 hook 比你们的 onboarding 文档有用多了。”
那次事故之后的一次”假警报”
这 5 个 hook 配齐一个月后,有一次 PreToolUse 拦了 Claude 一个 git push --force 到 feature/refund-v2。按我的规则 feature 分支是放过的,但当时分支名里有个 release 字样——feature/release-gate-refactor。规则里我用的是 grep -i release,命中了。
Claude 收到拒绝后自己换成了 git push --force-with-lease,更安全。我当时看日志还挺感慨——规则写得略粗,但效果反而更好。
所以结论:hook 宁可粗一点误拦,也别漏拦。被误拦无非多走一步,漏拦就是事故。
如果你还没开始配 hook,我建议就从 Hook 1 和 Hook 2 开始。这两个加起来 40 行脚本,半小时搞定,ROI 是整个 Claude Code 生态里最高的。想把整套 agent 工程化看 Agent SDK 架构,或者看一下我怎么在 CLAUDE.md 里做项目长期约束 CLAUDE.md 项目配置。
上面 5 个脚本的完整 Node/Python 模板我整理在一个 repo 里,包括 bash-guard 的完整黑名单、post-edit-lint 的多语言路由表、Stop 摘要用的 Haiku prompt。打算开源但还在整理,想先拿的留言告诉我一下你的技术栈(Node/Python/Go),我优先发那一套给你。
- 标题: Claude Code 的 hooks 我现在每个项目都配这 5 个
- 作者: Claude 中文知识站
- 创建于 : 2026-04-18 09:47:00
- 更新于 : 2026-04-19 15:22:00
- 链接: https://claude.cocoloop.cn/posts/claude-code-hooks-automation/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。