Claude Code Hooks 2026 完整实战指南:6 个生产可用的 Hook 场景,附完整脚本和配置

张开发
2026/4/17 15:35:28 15 分钟阅读

分享文章

Claude Code Hooks 2026 完整实战指南:6 个生产可用的 Hook 场景,附完整脚本和配置
keywords: [Claude Code, Hooks, AI编程, 自动化, Shell脚本, 开发工具]上周我让 Claude Code 帮我重构一个模块它很贴心地执行了rm -rf dist/来清理构建产物。问题是那个目录里还有一份我手动调试时放进去的配置文件——没有提交到 Git直接没了。这不是 Claude 的错。它按照正常的工程流程执行了清理操作。但作为人类我希望有一种机制能在危险操作执行之前拦截它就像 Git 的 pre-commit hook 一样——让自动化流程在关键节点停下来先检查再执行。Claude Code 的 Hooks 就是这个东西。它让你可以在 Agent 的生命周期中插入自定义的 Shell 脚本、HTTP 请求甚至 LLM 判断实现从「信任 Agent」到「信任但验证」的转变。这篇文章不讲概念直接给 6 个生产可用的 Hook 场景每个都附完整脚本和配置。看完直接能用。Hooks 到底是什么Agent 生命周期的切面如果你写过 Spring 的 AOP 或者用过 Git HooksClaude Code Hooks 的概念你一秒就懂在 Agent 执行特定操作的前后自动触发你定义的逻辑。整个生命周期长这样hook-lifecycle-events核心事件有这几个事件触发时机你能做什么SessionStart对话开始/恢复注入环境变量、加载上下文PreToolUse工具执行前拦截危险操作、修改参数PostToolUse工具执行后自动 lint、日志记录PermissionRequest权限弹窗时自动批准/拒绝特定操作StopAgent 结束响应阻止过早结束、触发总结UserPromptSubmit用户提交 prompt预处理、添加上下文其中PreToolUse是最常用的——90% 的「护栏」需求都在这里实现。配置文件放在哪三级作用域Hooks 的配置是一个 JSON 对象放在 Claude Code 的 settings 文件里。根据你的需求有三个位置可选~/.claude/settings.json # 全局所有项目生效 .claude/settings.json # 项目级随代码提交团队共享 .claude/settings.local.json # 本地不提交只对自己生效推荐做法是通用的安全策略放全局项目特定的放.claude/settings.json提交到仓库。配置的基本结构{ hooks: { PreToolUse: [ { matcher: Bash, hooks: [ { type: command, command: \$CLAUDE_PROJECT_DIR\/.claude/hooks/check.sh, timeout: 10 } ] } ] } }三层嵌套事件名 → 匹配器 → 处理器数组。matcher用正则匹配工具名比如Bash只拦截命令行操作Edit|Write拦截文件修改mcp__.*拦截所有 MCP 工具调用。场景一拦截危险 Shell 命令这是最高频的需求。创建一个脚本拦截rm -rf、DROP TABLE、git push --force等危险操作。#!/bin/bash # .claude/hooks/block-dangerous-commands.sh INPUT$(cat) COMMAND$(echo$INPUT | jq -r .tool_input.command // empty) # 定义危险模式正则匹配 DANGEROUS_PATTERNS( rm\s-rf\s/ # rm -rf 根目录或绝对路径 git\spush\s.*--force# force push DROP\sTABLE # 删表 DROP\sDATABASE # 删库 git\sreset\s--hard # 丢弃未提交修改 \s*/dev/sd # 写入磁盘设备 ) for pattern in${DANGEROUS_PATTERNS[]}; do ifecho$COMMAND | grep -iEq $pattern; then # 退出码 2 阻塞性错误Claude 会收到拒绝通知 echoBLOCKED: 命令匹配危险模式 [$pattern] 2 echo原始命令: $COMMAND 2 exit 2 fi done # 通过检查允许执行 exit 0配置{ hooks: { PreToolUse: [ { matcher: Bash, hooks: [ { type: command, command: \$CLAUDE_PROJECT_DIR\/.claude/hooks/block-dangerous-commands.sh, timeout: 5 } ] } ] } }“踩坑记录退出码的含义和你想象的不一样。退出码1是非阻塞错误Claude 会忽略并继续执行退出码2才是阻塞错误Claude 收到拒绝停止操作。我第一次写的时候用了exit 1结果发现 Claude 完全无视了我的拦截逻辑。场景二敏感文件保护防止 Claude 修改.env、密钥文件、锁文件等你不想被动的文件。#!/bin/bash # .claude/hooks/protect-sensitive-files.sh INPUT$(cat) FILE_PATH$(echo$INPUT | jq -r .tool_input.file_path // empty) # 如果没有文件路径比如 Bash 命令直接放行 [ -z $FILE_PATH ] exit 0 # 敏感文件模式 PROTECTED_PATTERNS( \.env$ \.env\. credentials secret \.pem$ \.key$ package-lock\.json$ pnpm-lock\.yaml$ yarn\.lock$ go\.sum$ ) for pattern in${PROTECTED_PATTERNS[]}; do ifecho$FILE_PATH | grep -iEq $pattern; then echoBLOCKED: 不允许修改敏感文件 $FILE_PATH 2 exit 2 fi done exit 0配置注意 matcher 匹配 Edit 和 Write 两个工具{ hooks: { PreToolUse: [ { matcher: Edit|Write, hooks: [ { type: command, command: \$CLAUDE_PROJECT_DIR\/.claude/hooks/protect-sensitive-files.sh, timeout: 5 } ] } ] } }pretooluse-decision-flow场景三代码修改后自动 Lint每次 Claude 编辑完文件自动跑一遍 linter把结果反馈给它。这样 Claude 可以在同一轮对话里自动修复格式问题。#!/bin/bash # .claude/hooks/auto-lint.sh INPUT$(cat) FILE_PATH$(echo$INPUT | jq -r .tool_input.file_path // empty) [ -z $FILE_PATH ] exit 0 # 根据文件类型选择 linter case$FILE_PATHin *.js|*.ts|*.jsx|*.tsx) RESULT$(npx eslint --fix $FILE_PATH 21) || true ;; *.py) RESULT$(python -m ruff check --fix $FILE_PATH 21) || true ;; *.go) RESULT$(gofmt -w $FILE_PATH 21) || true ;; *.java) # 只检查不修复把问题反馈给 Claude RESULT$(mvn checkstyle:check -pl $(dirname $FILE_PATH) 21 | tail -5) || true ;; *) exit 0 ;; esac # 如果有 lint 问题作为 context 反馈给 Claude if [ -n $RESULT ]; then jq -n --arg result $RESULT --arg file $FILE_PATH{ hookSpecificOutput: { hookEventName: PostToolUse, additionalContext: Lint 结果 [\($file)]:\n\($result)\n如果有问题请修复。 } } fi exit 0配置注意这是PostToolUse在编辑完成之后触发{ hooks: { PostToolUse: [ { matcher: Edit|Write, hooks: [ { type: command, command: \$CLAUDE_PROJECT_DIR\/.claude/hooks/auto-lint.sh, timeout: 30 } ] } ] } }场景四SessionStart 自动注入项目上下文每次对话启动时自动加载 Git 状态、最近 issue、当前分支等信息让 Claude 一进来就有上下文。#!/bin/bash # .claude/hooks/inject-context.sh # 收集项目状态 BRANCH$(git rev-parse --abbrev-ref HEAD 2/dev/null || echounknown) RECENT_COMMITS$(git log --oneline -5 2/dev/null || echono commits) DIRTY_FILES$(git diff --name-only 2/dev/null | head -10) ISSUES$(gh issue list -L 3 --json title,number --jq .[] | #\(.number) \(.title) 2/dev/null || echoGitHub CLI 不可用) # 构建上下文 CONTEXT 当前分支: $BRANCH 最近 5 次提交: $RECENT_COMMITS 未提交的修改: ${DIRTY_FILES:-无} 最近的 Issues: ${ISSUES:-无} jq -n --arg ctx $CONTEXT{ hookSpecificOutput: { hookEventName: SessionStart, additionalContext: $ctx } } exit 0配置{ hooks: { SessionStart: [ { matcher: startup, hooks: [ { type: command, command: \$CLAUDE_PROJECT_DIR\/.claude/hooks/inject-context.sh, timeout: 15 } ] } ] } }这个 Hook 有个细节SessionStart 的 matcher 可以区分startup新对话、resume恢复对话和compactcontext 压缩后。只在startup时加载完整上下文避免 resume 时重复注入。hook-types-comparison场景五异步审计日志记录 Claude 执行的所有操作不阻塞正常流程。关键是async: true——异步执行不影响 Agent 速度。{ hooks: { PostToolUse: [ { hooks: [ { type: command, async: true, command: echo \$(date %Y-%m-%dT%H:%M:%S) | $(jq -r .tool_name) | $(jq -r .tool_input | tostring | head -c 200)\ \$CLAUDE_PROJECT_DIR\/.claude/audit.log } ] } ] } }没有 matcher 意味着所有工具调用都会被记录。输出像这样2026-04-08T14:23:01 | Edit | {file_path:/src/main/java/Service.java,old_string:... 2026-04-08T14:23:05 | Bash | {command:mvn compile -pl dlm-framework/dlm-rule} 2026-04-08T14:23:12 | Read | {file_path:/src/test/java/ServiceTest.java}在出问题的时候这个日志能帮你回溯 Claude 的每一步操作。场景六HTTP Hook 对接外部合规系统如果你的团队有合规审核系统比如安全扫描服务可以用 HTTP Hook 在执行前请求外部 API{ hooks: { PreToolUse: [ { matcher: Bash, hooks: [ { type: http, url: http://localhost:8080/api/validate-command, headers: { Authorization: Bearer $COMPLIANCE_TOKEN }, allowedEnvVars: [COMPLIANCE_TOKEN], timeout: 10 } ] } ] } }HTTP Hook 会把工具调用的完整 JSON 作为 POST body 发送给你的服务。你的服务返回{decision: block, reason: ...}就能拦截操作。这在企业环境里特别有用——安全团队可以维护一个中心化的策略服务所有开发者的 Claude Code 实例都通过 HTTP Hook 对接。进阶四种 Hook 类型怎么选Claude Code 支持四种 Hook 类型适用场景不同类型执行方式适用场景延迟command本地 Shell 脚本大多数场景文件检查、lint、日志极低httpHTTP POST 请求对接外部系统、合规审核取决于网络prompt发送给 LLM 评估需要语义理解的判断较高agent启动 Sub-agent需要多步推理的复杂验证最高90% 的场景用command就够了。prompt和agent类型虽然强大但每次触发都会消耗额外 token不建议放在高频事件如 PostToolUse上。pretooluse-decision-flow常见问题QHook 脚本的 stdin 里具体传了什么每种工具的输入结构不同。Bash 工具传{tool_input: {command: ..., description: ..., timeout: ...}}Edit 工具传{tool_input: {file_path: ..., old_string: ..., new_string: ...}}。所有 Hook 还会收到session_id、cwd、permission_mode等公共字段。用jq -r .tool_input.command提取你需要的字段。QHook 执行失败会怎样取决于退出码。退出码 0 成功退出码 2 阻塞Claude 停止操作其他任何退出码包括 1 非阻塞错误仅记录到 debug 日志Claude 继续执行。这是新手最容易踩的坑——别用exit 1来拦截。Q怎么调试 Hook在 Claude Code 里输入/hooks可以查看所有已加载的 Hook 配置。脚本的 stderr 输出会出现在 debug 日志里。建议开发时先在终端单独测试脚本echo {tool_input:{command:rm -rf /}} | bash .claude/hooks/block-dangerous-commands.sh检查退出码和输出。QHook 会拖慢 Claude 的执行速度吗同步 Hook 会。所以要注意两点1设置合理的timeout默认 600 秒太长了大多数场景 5-10 秒足够2对于不需要阻塞的操作如审计日志使用async: true异步执行。Q能在 Hook 里修改 Claude 的操作参数吗可以。PreToolUse 的 Hook 可以在 JSON 输出里返回updatedInput字段来修改工具参数。比如你可以把rm -rf dist/改成rm -rf dist/ --interactive或者给 Bash 命令自动加上set -e。但谨慎使用——静默修改用户意图可能造成困惑。我的建议Hooks 本质上是给 AI Agent 加 middleware。和 Web 开发里的中间件一样最好的 Hook 是你写完就忘了它存在——它在背后默默工作只在真正危险的时候跳出来拦你一下。我个人的最小化配置是三个 HookPreToolUse/Bash拦截rm -rf、--force、DROP等危险模式PreToolUse/Edit|Write保护.env和锁文件PostToolUse/Edit|Writeasync自动 lint 审计日志这三个覆盖了 95% 的「AI 编程事故」场景。剩下的 5% 靠 Git。关注公众号「码哥跳动」解锁更多 AI 技能......往期推荐为什么我的 Claude Code 老是不按我说的做如何写出让 Agent 准确执行的「共识协议」Claude Code 源码8大新功能/26个隐藏指令/6级安全架构全被扒光了Claude Code 最值得安装的 10 个开源 Skills 最佳实战搞懂 Claude Code 的 Agent 编排原理我再也不一个个对话了Claude Code Skills 完全指南从零打造你的生产级别 AI 编程助理工作流

更多文章