一文讲清 Claude Code Hooks:从提示词到确定执行
Claude Code 改完几个文件,代码逻辑没问题,格式却乱了。你补了一句:“按项目的 Prettier 配置格式化一下。”,Claude Code开始格式化代码。
再比如,你的项目里有 .env、package-lock.json,还有 .git/ 目录下的文件。这些文件通常不应被 AI 在一次普通需求里顺手改掉。
为避免反复补充,格式化、敏感文件保护等项目约定,可以写进 CLAUDE.md、Rules 或 Skill。但这些只是说明,不是强制执行。Claude 通常会遵守,却不能保证每次修改后都格式化,也不能保证在修改敏感文件前都拦截。
有些场景要求某件事必须在指定时点执行,或者某个操作必须在执行前拦住。
Hooks 用来处理这类要求。
1 · Hooks 是什么
Hooks 是由用户定义的处理程序,会在 Claude Code 生命周期的特定节点自动执行。最常见的处理方式是 Shell 命令。它们能够以确定性的方式控制 Claude Code 的行为,确保某些操作总会发生,而不是依赖大模型自行选择是否运行。
可以把 Hook 理解为:把一个命令绑定到某个事件上;事件一发生,这个命令就执行。
Hooks 可用于强制执行项目规则、自动化重复任务,或把 Claude Code 接入已有工具链。
它和 Git Hook、CI 检查的思路相似:把靠人记住的动作放进固定流程。不同的是,Claude Code 的 Hook 挂在 Agent 生命周期里,可以在工具调用前后介入。
2 · 配置 Hook 及其如何被命中
Hook 配置放在哪里
配置位置决定 Hook 影响谁。
| 位置 | 适合放什么 | 管理与分发方式 |
|---|---|---|
~/.claude/settings.json | 跨项目的个人保护规则,如拦截 rm -rf 等危险命令 | 不提交 |
.claude/settings.json | 团队共享的项目规则,如代码格式化、密钥保护或敏感信息检查 | 提交 |
.claude/settings.local.json | 只在当前项目使用的个人偏好,如本地开发环境检查 | 不提交 |
| Managed policy settings | 组织统一的安全或合规策略 | 由管理员管理 |
Plugin 的 hooks/hooks.json | 可复用的插件能力 | 随插件分发 |
| Skill 或 Agent 的 frontmatter | 只在某个 Skill 或 Agent 运行时生效的行为 | 随组件维护 |
选好配置位置后,在对应的 JSON 文件中添加 hooks 字段;文件不存在就创建它。
最小配置与执行顺序
一个最小配置如下:
{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "./.claude/hooks/after-edit.sh" } ] } ]}}
最外层的 hooks 是配置入口。下面三层分别对应 JSON 中的不同位置:
| JSON 中的位置 | 名称 | 作用 |
|---|---|---|
"PostToolUse" | Event(事件) | 决定何时触发 |
包含 matcher 和内层 hooks 的对象 | Matcher Group(匹配组) | 决定哪些工具调用会进入处理 |
内层 hooks 数组中的对象 | Handler(处理程序) | 决定实际运行什么 |
这份配置的执行顺序是:PostToolUse 发生 → matcher: "Edit|Write" 筛选出编辑或新建文件 → 运行 command 指定的脚本。这里的脚本路径只是结构示例,后面的实战会给出可直接使用的配置。
用 matcher 和 if 筛选
if 是 Handler 中可选的进一步筛选条件,和 type、command 写在同一层:
{ "type": "command", "if": "Bash(rm *)", "command": "./.claude/hooks/block-rm.sh"}
它只在工具调用相关事件中有效。上例表示:只有 Bash 命令以 rm 开头时,才运行这个脚本。
下图展示了这些层级的关系:

matcher 先筛选工具,if 再筛选工具的参数。这样不必为每条 Bash 命令都启动检查脚本。
这套筛选并不适用于所有事件。Stop、UserPromptSubmit 这类事件不支持 matcher,写了也会被忽略;if 只用于工具调用相关事件,放在其他事件的 Handler 中,该 Handler 不会执行。
matcher 有几种常见写法:
Edit:精确匹配Edit工具。Edit|Write:精确匹配Edit或Write工具。Bash:只匹配 Bash 工具。*、空字符串或省略:匹配该事件的全部触发。mcp__github__.*:用正则匹配某个 MCP Server 暴露的全部工具。
工具名区分大小写,edit 不会匹配 Edit。
多个 Handler 如何执行
同一个事件中,所有命中的 Handler 都会并行执行。同一匹配组内的 hooks 数组可以放多个处理程序:
"hooks": [ { "type": "command", "command": "./.claude/hooks/format.sh" }, { "type": "command", "command": "./.claude/hooks/check.sh" }]
上面的格式化和检查脚本会同时运行。如果两步有先后依赖,例如必须先格式化再检查,应把它们放进同一个脚本,按顺序执行。
对 PreToolUse 而言,即使其中一个 Handler 拒绝本次操作,同组中用于记录日志的 Handler 仍会照常运行。
3 · Claude Code 的 Hook 生命周期
Hook 会在会话、对话和工具调用的不同节点触发。
从触发频率看,SessionStart、SessionEnd 是每个会话一次;UserPromptSubmit、Stop 是每轮对话一次;PreToolUse、PostToolUse 则跟随每次工具调用触发。
一次普通任务通常经过这些节点:

本文后面的两个实战分别使用 PreToolUse 和 PostToolUse。
主线图回答“何时触发”。接下来按 Hook 对当前流程的影响来归类:
| 介入方式 | 关键事件 | 能做什么 |
|---|---|---|
| 操作前拦截 | UserPromptSubmit 、PreToolUse、Stop | 阻止提示词处理、工具调用或任务结束 |
| 权限决策 | PermissionRequest | 对权限请求作出允许、拒绝或进一步询问的决定 |
| 后续处理与通知 | PostToolUse 、PostToolUseFailure、Notification | 格式化、记录、检查、补充后续上下文 |
| 会话与环境 | SessionStart 、SessionEnd、ConfigChange | 初始化、清理、响应环境变化 |
工具已经执行完成后,Hook 不能撤销这次操作,但可以影响 Claude 接下来的处理。
MCP 交互、子 Agent、任务协作、上下文压缩和 worktree 也有对应事件;遇到这些场景时,再按事件名查配置即可。
4 · Hook 的五种处理程序
Hook 的处理程序共有五种:
能由规则明确处理,就用 command;需要模型判断,用 prompt;还要检查项目实际状态,再用 agent。调用远程服务用 http,已有 MCP 工具用 mcp_tool。
command:运行本地命令或脚本
格式化、敏感文件保护、代码检查和记录日志通常用 command。它从 stdin 接收事件 JSON,通过退出码和 stdout 返回结果;第 5 章会展开说明。
http:把 Hook 接到服务
HTTP Hook 会把事件 JSON 以 POST 发送给服务,适合调用内部审计、通知或策略服务。服务返回非 2xx、连接失败或超时,只会记为非阻塞错误,本次流程仍会继续。要在支持拦截的事件中阻止操作,服务必须返回 2xx 响应,并在 JSON 中给出相应的拒绝决定。
mcp_tool:调用已连接的 MCP 工具
mcp_tool 会调用已经连接的 MCP Server 中的工具。它适合把 Hook 接到 GitHub、工单或内部系统。
prompt:把事件交给模型判断
prompt 不直接执行命令。Claude Code 会把你写的规则和本次事件信息交给模型;模型返回允许,或阻止并说明原因。
它不会自行读取文件或运行命令;如果判断依赖项目里的实际代码、测试结果或文件状态,应使用 agent。
下面以“Claude 准备修改 .env”为例:

图中的 $ARGUMENTS 是 Claude Code 自动传入的本次工具调用信息。模型返回 ok: false 后,Claude Code 会取消这次 Edit,并把 reason 交给主 Claude。
不同事件对“阻止”的处理不同。PreToolUse 会拒绝本次工具调用;Stop 则会把原因交给 Claude,让它继续处理而不是结束任务。
agent:先调查,再判断
agent 返回的也是允许或阻止,但它会先启动子 Agent 去读取文件、搜索代码或运行检查。
例如,Claude 准备结束任务时,Agent 可以检查单元测试是否通过;失败时返回:
{ "ok": false, "reason": "单元测试失败:UserServiceTest 有 2 个用例未通过。"}
agent 仍是实验性能力。能用脚本完成的规则,优先使用 command。
5 · 命令型 Hook 的输入、输出与异步执行
命令型(command) Hook 从 stdin 读取事件 JSON。处理完成后,它可以通过退出码、stderr 和 stdout 把结果交回 Claude Code。
本章说的“输出”,不是 Hook 修改后的文件,也不是 Claude 的回答,而是 Hook 脚本结束时交回 Claude Code 的结果。
例如,Claude 决定调用 Bash 工具执行 npm test 后,在真正执行前会触发 PreToolUse。Claude Code 同时构造一份描述本次工具调用的 JSON,并传给 Hook:
{ "session_id": "abc123", "cwd": "/Users/sarah/myproject", "hook_event_name": "PreToolUse", "tool_name": "Bash", "tool_input": { "command": "npm test" }}
脚本可以用 jq 取出 tool_input.command 或 tool_input.file_path,再按自己的规则处理。
最简单的返回方式是退出码:
exit 0:Hook 正常结束。如果没有输出 JSON,Claude Code 按默认流程继续;对PreToolUse而言,这不等于自动批准,后续仍按权限流程处理;exit 2:在可阻止的事件中中止相应流程,并把stderr的内容反馈给 Claude;- 其他非零值:通常表示 Hook 自己出错,Claude Code 会继续默认流程。
保护敏感文件的脚本会把拦截原因写到 stderr,然后 exit 2。
需要更细的控制时,脚本向 stdout 打印结构化 JSON,并以 exit 0 结束。Claude Code 只会在 exit 0 时解析 JSON;stderr 用于错误信息,不放 JSON,JSON 不能与 exit 2 混用。
如果要停止 Claude Code 的整个后续流程,返回顶层字段:
printf '%s\n' '{"continue": false, "stopReason": "Build failed"}'exit 0
continue: false 会停止后续处理,stopReason 则把原因显示给用户。
如果只想控制本次事件,返回 hookSpecificOutput。例如,PreToolUse 可以拒绝一次工具调用:
printf '%s\n' '{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Destructive command blocked by hook" }}'exit 0
hookEventName 指明这是一份 PreToolUse 的结果;permissionDecision: "deny" 拒绝本次工具调用,permissionDecisionReason 是 Claude 收到的原因。
除了作出决定,JSON 也能通过 additionalContext 向 Claude 补充运行时信息。例如,PostToolUse 发现刚被修改的是生成文件时,同样把下面这段 JSON 打印到 stdout 并以 exit 0 结束:
{ "hookSpecificOutput": { "hookEventName": "PostToolUse", "additionalContext": "这个文件由代码生成。请修改 src/schema.ts,然后运行 bun generate。" }}
Claude 会在下一次模型调用时看到这条信息,并据此决定后续操作。
异步 Hook:让 Claude Code 不必等待
默认情况下,Claude Code 会等 Hook 执行完再继续。设置 "async": true 后,命令型 Hook 会在后台运行,Claude Code 不必等待。它适合日志、通知和后台测试这类不需要立即决定结果的任务。
例如,文件修改后在后台运行测试:
{ "type": "command", "command": "npm test", "async": true, "timeout": 300}
Claude Code 会继续工作;脚本可通过 additionalContext 在后续对话把结果交给 Claude。
如果后台任务失败后需要 Claude 立刻处理,设置 asyncRewake: true;脚本以 exit 2 结束时,Claude 会收到错误信息。
异步只支持 command,并且不能阻止当前工具调用。
无论同步还是异步,都应设置合适的 timeout。格式化通常只需几十秒;全量测试或网络请求才需要更长时间。不要把一个可能卡住的任务放在工具调用前。
6 · Hook 的安全、性能与调试
不同处理程序的风险点不同。配置时,至少检查下面五点。
- 安全。添加前先审查并手动测试 Hook 脚本;项目级的配置和脚本同样应进入代码审查。
- 输入与路径。不要信任输入 JSON 里的路径和字符串:引用 Shell 变量时用双引号;拒绝包含
..的路径,避免脚本访问项目目录外的文件;用"${CLAUDE_PROJECT_DIR}"指向项目内的脚本。 - 权限控制。
if用于减少无关 Hook 的执行,不是硬性安全边界。强制允许或拒绝某类操作,应使用 Claude Code 的权限规则。 - 性能。同步 Hook 会阻塞 Claude Code 后续执行。缩小事件、
matcher和if的范围;不要在每次编辑前跑全仓库测试或慢网络请求。耗时但不影响当前操作的任务,使用异步 Hook。 - 调试。
/hooks可查看 Hook 是否注册、来自哪个配置文件和具体配置。claude --debug会把 Hook 的匹配、退出码以及stdout、stderr写入调试日志;它不会直接打印到终端。也可用claude --debug-file <路径>指定日志位置。
Hook 不触发或报错,怎么查
按这个顺序排查:
- 运行
/hooks,确认 Hook 已注册,并查看它实际来自哪个配置文件。 - 检查事件名、
matcher和if是否匹配。工具名区分大小写,edit不会匹配Edit。 - 在终端单独运行脚本,确认脚本路径、执行权限、
jq等依赖和退出码都正常。 - 仍找不到原因时,用
claude --debug启动 Claude Code,再查看~/.claude/debug/<session-id>.txt;或用claude --debug-file /tmp/claude-debug.txt把日志写到指定位置。
7 · 两个实战:自动格式化与敏感文件保护
实战一:编辑后自动格式化
Claude 修改或新建文件后,自动用 Prettier 格式化这个文件。
这里使用 PostToolUse,因为它会在编辑工具执行成功后触发。Edit|Write 表示只关注修改文件和新建文件,不会在 Claude 运行其他工具时触发。
把下面配置加到项目根目录的 .claude/settings.json:
{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs -I {} npx prettier --write \"{}\"" } ] } ]}}
如果 .claude/settings.json 已有 hooks,只添加 PostToolUse 这一项,不要覆盖原来的其他 Hook。
这条命令做了两件事:
jq -r '.tool_input.file_path'从 Hook 输入里取出刚被修改的文件路径;xargs -I {} npx prettier --write "{}"把完整路径交给 Prettier,让它按项目里的 Prettier 规则格式化。
这条配置依赖 jq 和项目中的 Prettier。
验证:触发 Hook
用 /hooks 确认配置已注册,再让 Claude 修改一个受 Prettier 支持的文件,检查格式是否变化。
实战二:敏感文件保护
格式化在文件写入后执行;文件保护必须在写入前判断。
把规则写入单独的脚本。先创建目录:
mkdir -p .claude/hooks
再创建 .claude/hooks/protect-files.sh:
#!/bin/bashFILE_PATH=$(jq -r '.tool_input.file_path // empty' < /dev/stdin)if [[ -z "$FILE_PATH" ]]; thenecho "Blocked: unable to determine target file" >&2exit 2fiPROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")for pattern in "${PROTECTED_PATTERNS[@]}"; doif [[ "$FILE_PATH" == *"$pattern"* ]]; then echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2 exit 2fidoneexit 0
在 macOS 或 Linux 上,再给脚本执行权限:
chmod +x .claude/hooks/protect-files.sh
再在 .claude/settings.json 注册它:
{ "hooks": { "PreToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-files.sh", "args": [] } ] } ]}}
如果 .claude/settings.json 已有 hooks,只添加 PreToolUse 这一项,不要覆盖原来的其他 Hook。
PreToolUse 在 Edit 或 Write 执行前触发。脚本读取目标路径;命中 .env、package-lock.json 或 .git/ 后,把原因写入 stderr 并以 exit 2 结束。Claude Code 会取消这次工具调用。
${CLAUDE_PROJECT_DIR} 指向项目根目录,避免 Claude 在项目子目录工作时找不到脚本。
args: [] 表示直接启动这个脚本。需要使用管道等 Shell 语法的命令,不应写 args。
模式应保持狭窄。config、src 会匹配大量正常文件,容易误拦截;先保护少量高风险文件。
验证:触发 Hook
用 /hooks 确认配置已注册,再尝试修改一个受保护文件,检查是否收到拦截原因。
8 · Claude Code 的常用机制怎么选
| 机制 | 解决什么问题 | 典型场景 |
|---|---|---|
Prompt | 当前一次任务的临时要求 | “这次只改登录页,不动接口” |
CLAUDE.md | 每个会话加载的核心项目说明 | 项目结构、构建命令、代码风格 |
Rules | 按主题或路径拆分项目规则 | 测试、安全、API 设计等独立规则;或只对特定目录生效的规则 |
Skills | 按需渐进式加载专项知识和工作流 | 发布、审查、迁移等重复任务 |
Subagent | 上下文隔离 | 代码库探索、专项审查 |
MCP | 以统一方式连接外部工具和数据源 | GitHub、数据库、飞书、Figma |
Hooks | 在生命周期事件触发时自动执行或拦截 | 格式化、敏感文件保护、审计 |
9 · 结语
Hooks 的价值,是把需要确定执行的项目规则绑定到固定事件上。
格式化、敏感文件保护这类需求,不必依赖 Claude 临场记住。
先从一个规则开始:编辑后自动格式化,或保护 .env。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋

📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~
这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】

更多推荐



所有评论(0)