【Claude】Claude Code Hooks 配置全攻略:6大事件钩子让AI编程拥有“自动护栏“
【Claude】Claude Code Hooks 配置全攻略6大事件钩子让AI编程拥有自动护栏前言很多开发者刚接触 Claude Code 时遇到的最大痛点不是 AI 写错代码而是管不住它。Claude 会在你不知情的情况下执行rm -rf会在代码提交前忘记跑测试会在写完代码后没有自动格式化……这些问题让团队不得不额外花时间做人工复查。Claude Code Hooks钩子系统正是为解决这类问题而生。它允许你在 AI 执行每一步操作前后自动触发自定义脚本——像安全门一样帮你过滤危险指令像流水线一样帮你自动执行标准化流程。本文基于 Claude Code v2.1 版本结合官方文档与社区实战案例带你彻底掌握 Hooks 配置打造一套AI 自动护栏。一、问题现象为什么你需要 Hooks1.1 典型痛点场景场景 A危险命令被意外执行Claude 正在清理临时文件执行了 rm -rf ./dist/../src/ # 路径拼接出错误删源码没有拦截机制时这类操作一旦触发就无法撤销。场景 B代码提交前缺少质量把关Claude 完成功能开发后直接 git commit 但没有运行 eslint 检查导致 CI/CD 管道失败场景 C文件修改后忘记格式化Python 文件被修改后Claude 没有运行 black 格式化 导致代码风格不一致PR review 收到大量格式问题评论场景 D敏感目录被意外访问Claude 在读取配置文件时顺带读取了 .env 文件 包含数据库密码、API 密钥等敏感信息这些问题靠人工监督成本极高而 Hooks 可以系统性地解决它们。1.2 Hooks 能做什么Hooks 本质上是事件驱动的脚本触发器能力描述拦截危险操作在工具执行前检查exit 1 可阻止执行自动化后处理工具执行后自动触发格式化、测试、通知审计日志记录 Claude 的所有操作到日志文件自定义通知任务完成时发 Slack 消息、桌面通知合规强制团队层面强制执行编码规范二、核心概念Hooks 的工作原理2.1 六个触发事件Claude Code Hooks 支持以下六个事件钩子事件名触发时机是否可拦截典型用途PreToolUseAI 执行工具之前✅ exit 1 可拦截危险命令检查、权限验证PostToolUse工具执行之后❌ 仅做后续处理自动格式化、运行测试StopAI 完成整轮回答后❌发通知、写日志NotificationAI 发出通知时❌桌面通知、消息推送SubagentStop子代理完成后❌子任务完成汇报PreCompact上下文压缩前❌保存重要信息2.2 Hook 执行流程用户输入 → Claude 决策 → PreToolUse Hook ↓ [exit 0] → 执行工具 [exit 1] → 拦截输出错误信息到 Claude ↓执行完毕 PostToolUse Hook异步不阻塞 ↓ Claude 继续对话 ↓ Stop Hook本轮结束时触发2.3 Hook 的 JSON 输入格式每个 Hook 脚本被调用时Claude 会通过stdin传入一个 JSON 对象PreToolUse 示例输入{ session_id: abc123, tool_name: Bash, tool_input: { command: rm -rf ./dist, description: 清理构建产物 } }PostToolUse 示例输入{ session_id: abc123, tool_name: Write, tool_input: { file_path: /src/utils/helper.py, content: ... }, tool_output: { success: true } }脚本可以通过读取 stdin 解析这些信息决定如何响应。三、环境准备与配置结构3.1 版本确认claude --version # 需要 v2.0 版本支持 Hooks # 如版本不足升级 npm install -g anthropic-ai/claude-codelatest3.2 三级配置作用域Hooks 配置写在 Claude Code 的 settings.json 里支持三个作用域~/.claude/settings.json # 全局所有项目生效 .claude/settings.json # 项目级随代码提交团队共享 .claude/settings.local.json # 本地不提交 git只对自己生效优先级项目级 全局local 覆盖非 local推荐做法团队通用的安全规则 → 项目级settings.json提交 git个人偏好通知方式等→settings.local.json加入 .gitignore3.3 配置文件基本结构{ hooks: { PreToolUse: [ { matcher: Bash, hooks: [ { type: command, command: bash .claude/hooks/pre-bash-check.sh } ] } ], PostToolUse: [ { matcher: Write, hooks: [ { type: command, command: bash .claude/hooks/post-write-format.sh } ] } ], Stop: [ { hooks: [ { type: command, command: bash .claude/hooks/on-stop-notify.sh } ] } ] } }matcher 字段说明Bash— 匹配 Bash 工具Write— 匹配文件写入工具Read— 匹配文件读取工具或不填 — 匹配所有工具支持正则Bash|Write匹配两种工具四、6大实战场景详解场景一拦截危险 Bash 命令PreToolUse目标阻止 Claude 执行高危命令如rm -rf、chmod 777、格式化磁盘等脚本.claude/hooks/pre-bash-check.sh#!/bin/bash # 从 stdin 读取 JSON 输入 INPUT$(cat) # 提取命令内容 COMMAND$(echo $INPUT | python3 -c import json, sys data json.load(sys.stdin) print(data.get(tool_input, {}).get(command, )) 2/dev/null) # 如果提取失败则放行 if [ -z $COMMAND ]; then exit 0 fi # 危险命令黑名单 DANGEROUS_PATTERNS( rm -rf / rm -rf \* dd if mkfs /dev/sda chmod -R 777 / :(){ :|: };: # fork bomb curl.*| bash wget.*| sh ) for pattern in ${DANGEROUS_PATTERNS[]}; do if echo $COMMAND | grep -qF $pattern 2/dev/null; then echo ❌ [安全拦截] 检测到高危命令模式: $pattern 2 echo 被拦截的命令: $COMMAND 2 echo 如确需执行请手动在终端运行 2 exit 1 # 非零退出码 拦截 fi done # 检查是否试图删除 src/ 目录 if echo $COMMAND | grep -qE rm.*src/ 2/dev/null; then echo ❌ [安全拦截] 检测到删除 src/ 目录的操作 2 echo 如需清理请只删除 dist/ 或 build/ 目录 2 exit 1 fi # 通过检查 exit 0配置写法{ hooks: { PreToolUse: [ { matcher: Bash, hooks: [ { type: command, command: bash .claude/hooks/pre-bash-check.sh } ] } ] } }测试验证# 在 Claude Code 中执行 rm -rf /tmp/../src/ # 预期输出 # ❌ [安全拦截] 检测到删除 src/ 目录的操作 # 如需清理请只删除 dist/ 或 build/ 目录场景二文件修改后自动格式化PostToolUse目标Python 文件被修改后自动运行 black isortJS/TS 文件修改后自动运行 prettier脚本.claude/hooks/post-write-format.sh#!/bin/bash INPUT$(cat) # 提取被写入的文件路径 FILE_PATH$(echo $INPUT | python3 -c import json, sys data json.load(sys.stdin) print(data.get(tool_input, {}).get(file_path, )) 2/dev/null) if [ -z $FILE_PATH ]; then exit 0 fi # 根据文件类型执行格式化 case $FILE_PATH in *.py) echo [自动格式化] Python 文件: $FILE_PATH # 检查 black 是否安装 if command -v black /dev/null; then black $FILE_PATH --quiet 2/dev/null echo ✅ black 格式化完成 fi # 检查 isort 是否安装 if command -v isort /dev/null; then isort $FILE_PATH --quiet 2/dev/null echo ✅ isort 排序完成 fi ;; *.js|*.jsx|*.ts|*.tsx) echo [自动格式化] JS/TS 文件: $FILE_PATH if command -v prettier /dev/null; then prettier --write $FILE_PATH 2/dev/null echo ✅ prettier 格式化完成 fi ;; *.go) echo [自动格式化] Go 文件: $FILE_PATH if command -v gofmt /dev/null; then gofmt -w $FILE_PATH 2/dev/null echo ✅ gofmt 格式化完成 fi ;; esac exit 0配置写法{ hooks: { PostToolUse: [ { matcher: Write, hooks: [ { type: command, command: bash .claude/hooks/post-write-format.sh } ] } ] } }场景三禁止访问敏感目录PreToolUse 读写均拦截目标禁止 Claude 读取.env、secrets/、credentials/等敏感路径脚本.claude/hooks/pre-sensitive-guard.sh#!/bin/bash INPUT$(cat) TOOL_NAME$(echo $INPUT | python3 -c import json, sys data json.load(sys.stdin) print(data.get(tool_name, )) 2/dev/null) # 提取文件路径Read 工具用 file_pathWrite 工具也用 file_path FILE_PATH$(echo $INPUT | python3 -c import json, sys data json.load(sys.stdin) ti data.get(tool_input, {}) print(ti.get(file_path, ti.get(path, ))) 2/dev/null) if [ -z $FILE_PATH ]; then exit 0 fi # 敏感路径模式 SENSITIVE_PATTERNS( .env .env.local .env.production secrets/ credentials/ private/ *.pem *.key *.pfx id_rsa id_ed25519 ) for pattern in ${SENSITIVE_PATTERNS[]}; do if [[ $FILE_PATH *$pattern* ]]; then echo [安全拦截] 禁止访问敏感路径: $FILE_PATH 2 echo 检测到敏感路径模式: $pattern 2 echo 如确需操作敏感文件请在终端手动执行 2 exit 1 fi done exit 0配置同时拦截 Read 和 Write{ hooks: { PreToolUse: [ { matcher: Read|Write, hooks: [ { type: command, command: bash .claude/hooks/pre-sensitive-guard.sh } ] } ] } }场景四任务完成后发送桌面通知Stop目标Claude 完成一轮任务后发送系统桌面通知适合跑长任务时去做其他事脚本.claude/hooks/on-stop-notify.sh#!/bin/bash INPUT$(cat) # 提取本轮消息摘要取前100字符 SUMMARY$(echo $INPUT | python3 -c import json, sys data json.load(sys.stdin) # Stop 事件包含最后一条消息 msg data.get(message, {}) content if isinstance(msg.get(content), list): for block in msg[content]: if isinstance(block, dict) and block.get(type) text: content block.get(text, )[:100] break elif isinstance(msg.get(content), str): content msg[content][:100] print(content if content else Claude 已完成任务) 2/dev/null || echo Claude 已完成任务) # macOS 桌面通知 if [[ $OSTYPE darwin* ]]; then osascript -e display notification \$SUMMARY\ with title \Claude Code\ sound name \Glass\ fi # Linux 桌面通知需要 notify-send if [[ $OSTYPE linux-gnu* ]]; then if command -v notify-send /dev/null; then notify-send Claude Code $SUMMARY --icondialog-information fi fi # 可选写入日志 LOG_FILE${HOME}/.claude/task_log.txt echo [$(date %Y-%m-%d %H:%M:%S)] $SUMMARY $LOG_FILE exit 0场景五代码提交前自动运行测试PreToolUse 拦截 git commit目标每次 Claude 执行git commit前先运行测试测试失败则拦截提交脚本.claude/hooks/pre-commit-test.sh#!/bin/bash INPUT$(cat) COMMAND$(echo $INPUT | python3 -c import json, sys data json.load(sys.stdin) print(data.get(tool_input, {}).get(command, )) 2/dev/null) # 只对 git commit 命令触发 if ! echo $COMMAND | grep -q git commit; then exit 0 fi echo [Pre-commit] 检测到 git commit先运行测试... # 根据项目类型选择测试命令 if [ -f package.json ]; then if npm test -- --passWithNoTests 21 | tail -5; then echo ✅ 测试通过允许提交 exit 0 else echo ❌ 测试失败拦截提交 2 echo 请修复测试后再提交或使用 git commit --no-verify 跳过不推荐 2 exit 1 fi elif [ -f requirements.txt ] || [ -f pyproject.toml ]; then if python -m pytest --tbshort -q 21 | tail -5; then echo ✅ 测试通过允许提交 exit 0 else echo ❌ 测试失败拦截提交 2 exit 1 fi fi # 没找到已知测试框架直接放行 exit 0场景六全量操作审计日志所有工具目标记录 Claude 的每一步操作便于事后审计和排障脚本.claude/hooks/audit-log.sh#!/bin/bash INPUT$(cat) LOG_DIR${HOME}/.claude/audit mkdir -p $LOG_DIR LOG_FILE$LOG_DIR/$(date %Y-%m-%d).log TOOL_NAME$(echo $INPUT | python3 -c import json, sys data json.load(sys.stdin) print(data.get(tool_name, Unknown)) 2/dev/null || echo Unknown) TIMESTAMP$(date %Y-%m-%d %H:%M:%S) SESSION$(echo $INPUT | python3 -c import json, sys data json.load(sys.stdin) print(data.get(session_id, N/A)[:8]) 2/dev/null || echo N/A) # 写入审计日志不含完整内容避免日志过大 echo [$TIMESTAMP] [$SESSION] Tool$TOOL_NAME $LOG_FILE # 对于 Bash 工具额外记录命令摘要 if [ $TOOL_NAME Bash ]; then CMD$(echo $INPUT | python3 -c import json, sys data json.load(sys.stdin) cmd data.get(tool_input, {}).get(command, ) print(cmd[:200]) # 只记录前200字符 2/dev/null) echo CMD: $CMD $LOG_FILE fi exit 0配置捕获所有工具{ hooks: { PreToolUse: [ { matcher: , hooks: [ { type: command, command: bash .claude/hooks/audit-log.sh } ] } ] } }五、完整 settings.json 配置示例将多个 Hook 整合到一个配置文件中{ hooks: { PreToolUse: [ { matcher: Bash, hooks: [ { type: command, command: bash .claude/hooks/pre-bash-check.sh }, { type: command, command: bash .claude/hooks/pre-commit-test.sh } ] }, { matcher: Read|Write, hooks: [ { type: command, command: bash .claude/hooks/pre-sensitive-guard.sh } ] }, { matcher: , hooks: [ { type: command, command: bash .claude/hooks/audit-log.sh } ] } ], PostToolUse: [ { matcher: Write, hooks: [ { type: command, command: bash .claude/hooks/post-write-format.sh } ] } ], Stop: [ { hooks: [ { type: command, command: bash .claude/hooks/on-stop-notify.sh } ] } ] } }注意同一事件下的多个 Hook 会顺序执行任意一个 exit 1 都会拦截操作。六、Hook 调试技巧6.1 手动模拟 Hook 触发在开发 Hook 脚本时不需要启动 Claude Code可以直接手动测试# 构造测试 JSON 并传入脚本 echo { session_id: test123, tool_name: Bash, tool_input: { command: rm -rf /src, description: 测试 } } | bash .claude/hooks/pre-bash-check.sh echo 退出码: $?6.2 查看 Hook 输出Hook 脚本的stderr输出会显示在 Claude Code 的交互界面中stdout不显示用于内部处理。调试时可以这样区分echo 这条信息会传给 Claude 看到 # stdout echo 这条信息显示在 Claude 回复里 2 # stderr6.3 常见问题排查问题可能原因解决方案Hook 未触发配置路径错误检查 settings.json 语法和文件位置脚本权限错误脚本没有执行权限chmod x .claude/hooks/*.shpython3 命令不存在系统没有 Python 3改用jq解析 JSONHook 总是拦截脚本有 bug 退出码非 0添加set -e调试或检查逻辑性能慢Hook 脚本耗时过长PreToolUse 需要快速响应避免网络请求6.4 用 jq 替代 Python 解析 JSON如果不想依赖 Python可以用jq#!/bin/bash INPUT$(cat) COMMAND$(echo $INPUT | jq -r .tool_input.command // ) if echo $COMMAND | grep -q rm -rf /; then echo 危险命令被拦截 2 exit 1 fi exit 0安装 jqbrew install jqmacOS或apt install jqUbuntu七、团队协作最佳实践7.1 目录结构规范your-project/ ├── .claude/ │ ├── settings.json # 团队共享的 Hook 配置提交 git │ ├── settings.local.json # 个人配置加入 .gitignore │ └── hooks/ │ ├── pre-bash-check.sh # 危险命令检查 │ ├── pre-sensitive-guard.sh # 敏感文件保护 │ ├── pre-commit-test.sh # 提交前测试 │ ├── post-write-format.sh # 写后格式化 │ ├── on-stop-notify.sh # 完成通知 │ └── audit-log.sh # 审计日志 └── ...7.2 .gitignore 配置# Claude Code 本地配置不提交 .claude/settings.local.json # 审计日志可选按需决定是否提交 .claude/audit/7.3 Hook 脚本编写规范快速失败原则PreToolUse Hook 应当在 100ms 内完成避免拖慢 Claude 响应幂等原则PostToolUse Hook 可能在重试场景下被多次触发脚本需要幂等错误不阻塞非安全类 Hook如通知、日志出错时应exit 0不影响主流程信息清晰拦截时的 stderr 消息应明确告诉 Claude为什么被拦截以及怎么绕过八、进阶动态 Hook 与条件逻辑8.1 根据项目类型动态选择格式化工具#!/bin/bash INPUT$(cat) FILE_PATH$(echo $INPUT | python3 -c import json, sys print(json.load(sys.stdin).get(tool_input, {}).get(file_path, )) 2/dev/null) # 读取项目配置动态选择格式化器 if [ -f .prettierrc ]; then FORMATTERprettier elif [ -f .editorconfig ]; then FORMATTEReditorconfig fi # 根据 formatter 执行不同逻辑 case $FORMATTER in prettier) prettier --write $FILE_PATH 2/dev/null ;; esac exit 08.2 根据 git 分支控制拦截严格度#!/bin/bash BRANCH$(git rev-parse --abbrev-ref HEAD 2/dev/null || echo unknown) # 主分支严格模式额外检查 if [[ $BRANCH main || $BRANCH master ]]; then echo ⚠️ 当前在主分支启用严格安全检查 2 # ...严格检查逻辑 fi exit 0九、避坑清单不要在 PreToolUse 中做耗时操作网络请求、数据库查询等会让 Claude 的每一步操作都变慢测试脚本权限chmod x每个 Hook 脚本否则会报 Permission deniedJSON 解析要有 fallbackPython/jq 命令不存在时脚本不应崩溃应exit 0放行避免循环触发PostToolUse 的格式化操作如果也会触发 Write 事件可能造成无限循环——建议格式化工具用--no-save模式或在脚本中检测重入审计日志做轮转日志文件不轮转会无限增长建议加logrotate或定期清理总结Claude Code Hooks 是让 AI 编程工具真正可信赖的关键机制。通过合理配置PreToolUse钩子建立安全门拦截危险操作PostToolUse钩子建立自动流水线确保代码质量Stop钩子建立任务感知让你从键盘前解放出来建议所有在生产环境使用 Claude Code 的团队都将 Hooks 纳入标准配置和.eslintrc、Dockerfile一样作为项目必备文件提交到代码仓库。AI 不会犯困但也不会主动遵守你的规范——Hooks 就是让规范自动执行的最优解。