Claude Code教程 -04- Hooks 自动化触发
Claude Code教程 -04- Hooks自动化触发Hooks 的价值就在于把这些动作从“ 你记得做” 变成 “系统自动做” 并且可以做到“该阻断就阻断、该后台跑就后台跑、该提醒就提醒”。Hooks是什么Hooks 是 Claude Code 的事件驱动自动化 系统在特定事件触发时自动执行一组 Hook。它主要有两种形态命令型 Hookscommand运行 Shell 命令/脚本适合自动化与校验格式化、检查、通知、跑测试……。提示型 Hooksprompt在Stop/SubagentStop事件里让 Claude 再做一次“停下前的质量检查”根据结果决定继续还是停止你可以把它理解为SessionStart/End会话生命周期钩子PreToolUse/PostToolUse工具执行前/后钩子Write/Edit/Bash/Read…Stop/SubagentStop模型准备结束前的“收尾门禁”Notification系统通知的自定义处理Hooks配置用户级 vs 项目级项目级推荐团队共享:文件{project}/.claude/settings.json适合团队统一的格式化、测试、危险命令拦截、质量门禁等配套建议把脚本也放进仓库例如{project}/.claude/hooks/*.sh让团队开箱即用项目级本地仅自己用不提交:文件{project}/.claude/settings.local.json适合个人路径相关脚本、个人通知方式、私有工具等配套建议加入.gitignore用户级跨项目生效文件~/.claude/settings.json适合你个人的通用 Hooks比如任何项目都启用的通知/危险命令拦截注意团队规范建议 “项目级为主、用户级为辅”项目级让仓库可复制用户级放个人偏好。八种事件类型汇总事件触发时机Hook 类型常见用途SessionStart会话开始命令型初始化环境、检查依赖、打印提示SessionEnd会话结束命令型清理临时文件、记录统计UserPromptSubmit用户提交输入前命令型输入验证、关键词拦截、注入上下文PreToolUse工具执行前命令型危险命令拦截、参数校验、审计PostToolUse工具执行后命令型自动格式化、自动测试、生成变更摘要StopClaude 决定停止前提示型质量门禁lint/test/git statusSubagentStop子代理停止前提示型子任务验收比如“测试是否补齐”NotificationClaude 发送通知时命令型转发通知系统通知/Slack/飞书等命令型Hooks脚本自动化基本语法最常用{hooks:{PostToolUse:[{matcher:Write|Edit,hooks:[{type:command,command:./.claude/hooks/format.sh,blocking:false}]}]}}关键点matcher用于筛选工具通常写正则/或模式串比如Write|Edit、Bash。blockingtrueHook 失败会阻断当前动作适合安全校验、门禁falseHook 失败不阻断适合格式化、统计、通知Hook 能拿到什么上下文命令型 Hooks 通常可以通过环境变量拿到上下文不同事件变量略有差异常见如变量名说明TOOL_NAME工具名称如Write/Edit/BashTOOL_INPUT工具输入JSON 字符串TOOL_INPUT_FILE_PATH文件路径Write/Edit 常用TOOL_OUTPUT工具输出PostToolUse 常用USER_MESSAGE用户输入UserPromptSubmitCLAUDE_WORKING_DIR当前工作目录NOTIFICATION_MESSAGE通知内容NotificationJSON更稳妥的写法脚本里同时支持读取stdinJSON与环境变量避免某些环境差异导致取不到值退出码怎么控制“阻断/不阻断”常见约定建议你按这个写团队最好统一exit0# 成功继续exit2# 阻断错误blockingtrue 时会拦下本次操作exit1# 非阻断错误记录/提示但不中断主流程提示型Hooks让 Claude 在“停下前”做质量门禁提示型 Hooks 仅用于Stop/SubagentStop当 Claude 觉得“差不多可以结束了”会先跑一次这个提示让它自检并输出 JSON 决策基础语法{hooks:{Stop:[{hooks:[{type:prompt,prompt:检查是否满足停止条件。返回 JSON: {\decision\:\stop\|\continue\,\feedback\:\说明\}}]}]}}核心格式核心{decision:continue,feedback:请先运行测试并修复 lint。}你可以用它来实现 “Claude 自己给自己验收” 的体验不满足条件就继续把事情做完满足才停。案例模版下面的模板尽量遵循一个原则高风险校验 blockingtrue耗时工作 blockingfalse。依赖提示后面的脚本示例会用到jq来解析 JSONmacOS 可用brew install jq。案例SessionStart 做环境体检不阻断session-init.sh检查 Node/Python/Git 状态、依赖是否安装等按你的项目改。.claude/settings.json配置如下{hooks:{SessionStart:[{hooks:[{type:command,command:./.claude/hooks/session-init.sh,blocking:false}]}]}}session-init.sh示例代码如下#!/usr/bin/env bashset-euopipefailecho Claude Code SessionStartechocwd:${CLAUDE_WORKING_DIR:-$(pwd)}ifcommand-vgit/dev/null21gitrev-parse --git-dir/dev/null21;thenechogit branch:$(gitbranch --show-current)changes$(gitstatus--porcelain|wc-l|tr-d )if[[${changes}!0]];thenecho⚠️ uncommitted changes:${changes}fifiif[[-fpackage.json]];thencommand-vnode/dev/null21echonode:$(node--version)[[-dnode_modules]]||echo tip: run npm installfiif[[-frequirements.txt]];thencommand-vpython/dev/null21echopython:$(python--version21|head-n1)[[-n${VIRTUAL_ENV:-}]]||echo tip: activate venvfi案例PreToolUse 拦截危险 Bash阻断guard-bash.sh思路从TOOL_INPUT或 stdin JSON里取出command匹配rm -rf、git push --force等高危模式命中就exit 2并在 stderr 输出原因。{hooks:{PreToolUse:[{matcher:Bash,hooks:[{type:command,command:./.claude/hooks/guard-bash.sh,blocking:true}]}]}}guard-bash.sh示例代码如下#!/usr/bin/env bashset-euopipefailinput_json${TOOL_INPUT:-}if[[-z${input_json}]];theninput_json$(cat||true)ficmd$(echo${input_json}|jq-r.command // empty2/dev/null||true)if[[-z${cmd}]];thenexit0fiif[[${cmd}*[claude-allow-danger]*]];thenexit0fideny_patterns((^|[[:space:]])rm[[:space:]]-rf[[:space:]](/|~|\\$HOME)([[:space:]]|$)(^|[[:space:]])git[[:space:]]push([[:space:]].*)?--force(^|[[:space:]])git[[:space:]]reset([[:space:]].*)?--hard(^|[[:space:]])chmod[[:space:]]-R[[:space:]]777)forpatternin${deny_patterns[]};doifecho${cmd}|grep-Eq${pattern};thenecho❌ 已阻断高危命令PreToolUse2echo${cmd}2echo如确认要执行请在命令末尾加 [claude-allow-danger] 作为显式确认。2exit2fidoneexit07.3 案例PreToolUse 做敏感信息检测阻断适用场景Write/Edit 写入内容前扫描是否疑似 Key/密码/Token命中直接拦截。{hooks:{PreToolUse:[{matcher:Write|Edit,hooks:[{type:command,command:./.claude/hooks/check-secrets.sh,blocking:true}]}]}}check-secrets.sh示例代码如下#!/usr/bin/env bashset-euopipefailinput_json${TOOL_INPUT:-}if[[-z${input_json}]];theninput_json$(cat||true)ficontent$(echo${input_json}|jq-r.new_string // .content // 2/dev/null||true)patterns(AKIA[0-9A-Z]{16}# AWS Access Keyghp_[a-zA-Z0-9]{36}# GitHub PATsk-[a-zA-Z0-9]{32,}# API Key示例(?i)password\\s*[:]\\s*[\x27][^\x27][\x27](?i)api[_-]?key\\s*[:]\\s*[\x27][^\x27][\x27])forpatternin${patterns[]};doifecho${content}|grep-Pq${pattern}2/dev/null;thenecho❌ 检测到潜在敏感信息已阻断写入PreToolUse2echopattern:${pattern}2echo建议改用环境变量/密钥管理服务不要把 Key 写进仓库。2exit2fidoneexit07.4 案例PostToolUse 自动格式化 自动跑测试不阻断{hooks:{PostToolUse:[{matcher:Write|Edit,hooks:[{type:command,command:./.claude/hooks/format.sh,blocking:false},{type:command,command:./.claude/hooks/auto-test.sh,blocking:false}]}]}}format.sh示例代码如下#!/usr/bin/env bashset-euopipefailfile${TOOL_INPUT_FILE_PATH:-}[[-n${file}-f${file}]]||exit0case${file}in*.ts|*.tsx|*.js|*.jsx|*.json)command-vprettier/dev/null21prettier--write${file}/dev/null21||truecommand-veslint/dev/null21eslint--fix${file}/dev/null21||true;;*.py)command-vblack/dev/null21black${file}/dev/null21||truecommand-visort/dev/null21isort${file}/dev/null21||true;;*.go)command-vgofmt/dev/null21gofmt-w${file}/dev/null21||true;;*.rs)command-vrustfmt/dev/null21rustfmt${file}/dev/null21||true;;esacexit0auto-test.sh示例代码如下#!/usr/bin/env bashset-euopipefailfile${TOOL_INPUT_FILE_PATH:-}[[-n${file}-f${file}]]||exit0ifecho${file}|grep-Eq(\\.test\\.|\\.spec\\.|/tests?/|__tests__);thenexit0fiif[[-fpackage.json]]command-vnpm/dev/null21;thenifecho${file}|grep-Eq\\.(ts|tsx|js|jsx)$;thenecho running minimal js/ts tests (best effort)npmtest--silent2/dev/null||truefifiif[[-fpyproject.toml||-frequirements.txt]]command-vpytest/dev/null21;thenifecho${file}|grep-Eq\\.py$;thenecho running minimal python tests (best effort)pytest-q2/dev/null||truefifiexit0建议格式化脚本按后缀选择prettier/black/gofmt/rustfmt测试脚本“只跑与当前文件相关的最小集合”避免每次都全量测试拖慢体验7.5 案例Notification 做系统通知不阻断notify.shmacOS可以用osascript或terminal-notifier从NOTIFICATION_MESSAGE里取title/message发系统通知。{hooks:{Notification:[{hooks:[{type:command,command:./.claude/hooks/notify.sh,blocking:false}]}]}}notify.sh示例脚本如下#!/usr/bin/env bashset-euopipefailmsg_json${NOTIFICATION_MESSAGE:-}if[[-z${msg_json}]];thenmsg_json$(cat||true)fititle$(echo${msg_json}|jq-r.title // Claude Code2/dev/null||echoClaude Code)message$(echo${msg_json}|jq-r.message // empty2/dev/null||true)[[-n${message}]]||exit0ifcommand-vterminal-notifier/dev/null21;thenterminal-notifier-title${title}-message${message}-groupclaude-code/dev/null21||trueexit0fiosascript-edisplay notification\${message}\with title\${title}\/dev/null21||trueexit0常见问题问题一Hook 不执行可以通过如下方式排查#开调试claude--debughooks#检查脚本可执行权限chmodx ./.claude/hooks/*.sh#脚本自测用 stdin 模拟 JSONecho{sessionId:test}|./.claude/hooks/session-init.sh问题二无故被阻断blockingtrue的 Hook 一定要做到只在命中明确条件时才exit 2stderr 输出清晰原因让 Claude 能把“为什么被拦”解释给你问题 三变慢很明显把耗时任务全量测试、扫描整个仓库改成blockingfalse或改成“增量/抽样”在脚本里加超时/短路条件比如只对特定目录、特定后缀执行总结Hooks是一套事件驱动的自动化与质量门禁机制。通过在 Session、Tool 执行前后以及 Stop 阶段合理配置命令型与提示型 Hooks可以将格式化、测试、危险命令拦截、敏感信息校验等“重复但关键”的操作交由系统自动完成把人为自觉升级为流程约束