Claude Code Hooks 实战:在动作前后插自动化
Claude Code Hooks 是一组在 AI 执行特定动作前后自动触发你自定义脚本的钩子机制——它让你在”工具调用”这个时间点切进去,做拦截、检查、自动修复或通知。一句话:它把”AI 写完代码我得手动跑一遍 lint”这种重复动作,变成系统替你自动完成。
本文面向已经在用 Claude Code 写真实项目的人,讲清 Hooks 的触发机制、怎么在 settings.json 里配、两个最常用的场景(PreToolUse 拦危险命令、PostToolUse 自动 format/lint),以及配不通时怎么排查。读完你能把”保存即自动跑 lint”这条流水线搭起来。
为什么要用 Hooks
不用 Hooks,你只能靠提示词请求 AI:“改完记得跑一下格式化”。问题是提示词是软约束——模型可能忘、可能跑错、可能顺手执行一条 rm -rf 你没拦住。
Hooks 是硬约束。它不依赖模型”愿不愿意”,而是由 Claude Code 这个程序在确定的时间点强制执行你的脚本。两类刚需场景:
- 安全兜底:在 AI 执行 shell 命令前,先用脚本扫一遍,碰到危险命令直接拦下。
- 工程自动化:在 AI 改完文件后,自动跑 Prettier / ESLint / gofmt,保证落盘的代码永远是规范的。
把它和 斜杠命令(规划中) 对照着理解:斜杠命令是你主动触发的快捷动作,Hooks 是事件被动触发的自动动作。两者互补。
原理:Hooks 在什么时候触发
机制是长青的,记住这个就够了:Hooks 监听 Claude Code 的生命周期事件,在事件发生时执行你配置的命令行脚本。
最核心的两个事件:
| 事件 | 触发时机 | 典型用途 |
|---|---|---|
| PreToolUse | AI 调用某个工具(如执行 Bash、写文件)之前 | 拦截危险命令、做合规校验、阻止越权操作 |
| PostToolUse | 工具成功执行之后 | 自动格式化、跑 lint、跑测试、发通知 |
关键点:PreToolUse 的脚本有”否决权”。脚本通过退出码或输出告诉 Claude Code”这个动作不许做”,AI 的这次工具调用就会被阻断。PostToolUse 则发生在动作已经完成之后,主要用来做善后。
除了这两个,Claude Code 还提供了若干生命周期事件(如会话相关、提交相关的钩子点)。具体有哪些事件名、各自的输入字段和退出码语义,以官方文档为准——事件机制稳定,但字段细节各版本可能微调,别背死。
怎么配:settings.json 三步走
Hooks 配置写在 Claude Code 的 settings.json 里(项目级或用户级均可)。整体结构是:按事件名分组 → 用 matcher 圈定要匹配哪些工具 → 给出要执行的命令。
第一步:找到 settings.json
- 用户级(对所有项目生效):在你的用户配置目录下的
settings.json。 - 项目级(只对当前仓库生效,可提交进 git 让团队共享):项目根目录的
.claude/settings.json。
团队协作推荐用项目级,这样”保存自动 lint”这条规则跟着仓库走,新人 clone 下来就生效。
第二步:写一个 hooks 配置块
配置的骨架长这样(字段名以官方文档为准,这里给的是通用结构示意):
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_FILE_PATH\""
}
]
}
]
}
}
读法:当 PostToolUse 事件触发、且工具是 Edit 或 Write(即 AI 改了文件)时,对刚改的文件跑一次 Prettier 格式化。matcher 用来按工具名筛选,避免对所有动作都触发脚本。
第三步:让脚本拿到上下文
Hooks 脚本通过环境变量或标准输入的 JSON 拿到这次动作的上下文(比如改了哪个文件、要执行什么命令)。变量名/字段名以官方文档为准——不同版本暴露的字段不完全一样,写脚本前先打印一次看实际内容,比猜更靠谱。
两个最实用的实战配方
配方一:PostToolUse 自动跑 lint / format
这是最高频的用法。目标:AI 每次改完代码,自动格式化 + 跑 lint,发现问题让 AI 自己看到并修。
思路:
- matcher 圈定
Edit|Write(文件被修改的动作)。 - command 里跑你项目的格式化和 lint 命令,例如
npx eslint --fix配合prettier --write。 - 让 lint 的输出回到 Claude Code——这样如果还有改不掉的报错,AI 能读到错误信息并继续修。
效果:你不用再每轮手动喊”跑一下 lint”,落盘的代码默认就是过 lint 的。
配方二:PreToolUse 拦截危险命令
目标:在 AI 执行 Bash 命令前,扫一遍命令文本,命中黑名单就拦下。
思路:
- matcher 圈定
Bash(执行 shell 的动作)。 - command 指向你的一个小脚本,脚本从输入里取出待执行的命令字符串。
- 脚本里匹配危险模式——如
rm -rf /、git push --force到主分支、DROP TABLE、往生产环境写的命令等。 - 命中就以”阻断”的退出码/输出返回,这次工具调用被否决;没命中就放行。
这相当于给 AI 的双手加了一道”确认闸门”,尤其适合让 Claude Code 跑在有真实权限的环境里时。
怎么验证配好了
别假设配置生效了,跑一遍确认:
- 故意触发:让 Claude Code 改一个文件(触发 PostToolUse)或执行一条命令(触发 PreToolUse)。
- 看动作有没有发生:format 配方看文件是否被自动格式化;拦截配方就让它试一条黑名单命令,看是否被挡。
- 在脚本里加日志:临时往脚本里加一行写日志(如把上下文 echo 到一个临时文件),确认脚本真的被调用、且拿到的字段符合预期。
跑通一次、看到预期行为,才算配好。
常见坑与排查
| 现象 | 可能原因 | 解法 |
|---|---|---|
| Hook 完全没触发 | matcher 没匹配上工具名,或事件名拼错 | 核对工具名大小写、事件名拼写,以官方文档的准确名称为准 |
| 脚本报”命令找不到” | Hook 执行环境的 PATH 和你终端不一样 | 在 command 里用命令的绝对路径,或先 source 环境 |
| 拦截不生效,危险命令照跑 | PreToolUse 没用正确的退出码/输出表达”否决” | 确认阻断的返回约定,以官方文档为准 |
| 改一个文件触发好几次 | matcher 太宽,或脚本自身又改了文件形成回环 | 收窄 matcher;让格式化脚本不要触发新的 Hook 事件 |
| 脚本里取不到文件路径 | 拿上下文的变量名/字段记错了 | 先打印一次实际输入,按真实字段名改 |
排查总原则:先确认脚本到底有没有被调用(加日志),再确认它拿到的输入对不对,最后才看业务逻辑。顺序反了会浪费很多时间。
常见问题
Q:Hooks 和直接在提示词里要求 AI 跑 lint,有什么区别? A:提示词是软约束,模型可能忘或跑错;Hooks 是程序级硬约束,到了事件点一定执行,不依赖模型自觉。要稳定可靠,用 Hooks。
Q:PreToolUse 真能拦住 AI 执行命令吗? A:能。PreToolUse 的脚本有否决权,用约定的退出码/输出返回”阻断”,这次工具调用就不会执行。这是给高权限环境加安全闸的标准做法。
Q:配置写在哪里,能让团队共用吗?
A:写在项目根目录的 .claude/settings.json 里并提交进 git,全团队 clone 下来即生效。只想自己用就写用户级 settings.json。
Q:Hook 脚本支持什么语言? A:Hook 执行的是命令行命令,所以你的脚本用什么写都行——Bash、Python、Node 皆可,只要能被命令行调起、能读输入、能用退出码/输出表达结果即可。
Q:具体的事件名、字段名我记不准怎么办? A:不用背。事件机制是稳定的,但字段细节各版本可能微调,写之前以官方文档为准,并在脚本里打印一次实际输入对照——这比凭记忆更可靠。
👉 看看我们的 AI 编程实战体系课,或逛 AI 编程教程大全 把基本功打扎实。