Claude Code Hooks 实战指南
规则靠自觉,Hooks 靠机制。
我之前在 CLAUDE.md 里写了一堆"请确保代码通过 lint"、"不要在根目录创建临时文件",结果 AI 偶尔还是会忘。后来发现 Hooks 能解决这个问题------它是强制执行的,不是写在那等 AI 自觉遵守的。
这篇文章分享我实际在用的 Hooks 配置,以及官方支持的事件和配置方法。
一、Hooks 是什么
Hooks 就是在 Claude Code 工作流程的关键节点,自动执行你预设的脚本。不需要 AI 配合,不靠提示词,系统层面硬性执行。
SessionStart ──▶ UserPromptSubmit ──▶ PreToolUse ──▶ [工具执行] ──▶ PostToolUse
│
PreCompact ◀── 上下文压缩 │
SessionEnd ◀── 会话结束 │
Stop ◀── AI 停止输出
核心区别:
| 方式 | 执行保证 | 适用场景 |
|---|---|---|
| CLAUDE.md 规则 | AI 可能忘 | 项目约定、编码风格 |
| Hooks | 强制执行 | 安全防护、自动格式化、上下文注入 |
二、配置位置和优先级
Hooks 写在 JSON 配置文件里,有四个层级:
| 位置 | 作用范围 | 能提交到仓库吗 |
|---|---|---|
~/.claude/settings.json |
所有项目 | 不能,本地专属 |
.claude/settings.json |
单个项目 | 能 |
.claude/settings.local.json |
单个项目 | 不能(gitignored) |
插件 hooks/hooks.json |
插件启用时 | 跟插件走 |
我的做法:通用安全防护放全局 ,项目特定规则放项目级。
三、配置结构
一个 Hook 由三层嵌套组成:
json
{
"hooks": {
"事件名": [
{
"matcher": "工具匹配器",
"hooks": [
{
"type": "command",
"command": "你的脚本"
}
]
}
]
}
}
matcher 匹配规则
| 写法 | 匹配方式 | 示例 |
|---|---|---|
"*" 或 "" 或不写 |
匹配所有 | 所有事件都触发 |
| 纯字母+数字+` | ` | 精确匹配 |
| 包含其他字符 | 正则匹配 | "^Notebook" 或 "mcp__memory__.*" |
退出码含义
| 退出码 | 含义 | 效果 |
|---|---|---|
0 |
成功 | 正常继续,stdout 会被解析 |
2 |
阻断 | 阻止操作执行,stderr 反馈给 AI |
| 其他 | 非阻断错误 | 记录日志,继续执行 |
脚本能收到的环境变量
| 变量 | 说明 |
|---|---|
$CLAUDE_FILE_PATH |
当前操作的文件路径(PostToolUse 时) |
$CLAUDE_PROJECT_DIR |
项目根目录 |
$CLAUDE_SESSION_ID |
当前会话 ID |
脚本通过 stdin 接收 JSON 输入,格式取决于事件类型。
四、我实际在用的 Hooks
1. 危险命令拦截(PreToolUse + Bash)
问题 :AI 偶尔会生成 git push --force、rm -rf / 这种危险命令。
配置:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash /path/to/ocean-dock/hooks/guard_bash.sh"
}
]
}
]
}
}
脚本做的事:
- 从 stdin 读取 JSON,提取
tool_input.command - 先检查安全命令白名单(
ls、pwd、git status等直接放行) - 匹配危险模式:
rm -rf /etc、git push --force、DROP TABLE、mkfs、shutdown等 - 命中则
exit 2阻断执行
关键代码片段:
bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('command',''))")
# 匹配 force push
echo "$CMD" | grep -qiE 'git\s+push\s+.*(-(-force|-f)\b)' && {
echo "[GUARD] 危险: 检测到 git push --force 操作" >&2
exit 2
}
效果 :AI 要执行 git push --force 时,命令直接被拦截,AI 收到错误反馈,会自动换一种方式处理。
2. 垃圾文件拦截(PreToolUse + Write/Edit)
问题 :AI 偶尔会在根目录创建 test.py、debug.py,或者写 .env 文件。
配置:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash /path/to/ocean-dock/hooks/guard_write.sh"
}
]
}
]
}
}
拦截规则:
__pycache__/、.pyc、.pyo→ 编译缓存.DS_Store→ 系统垃圾.git/内部文件 → 破坏仓库.egg-info/→ 打包缓存- 根目录的
test.py、tmp.py、debug.py→ 必须放tests/
3. 自动格式化(PostToolUse + Write/Edit)
问题:AI 写完代码,格式可能不一致。
两个 Hook 叠加使用:
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "bash /path/to/ocean-dock/hooks/auto_check.sh"
},
{
"type": "command",
"command": "[ \"${CLAUDE_FILE_PATH##*.}\" = \"py\" ] && ruff check --fix \"$CLAUDE_FILE_PATH\" 2>/dev/null && ruff format \"$CLAUDE_FILE_PATH\" 2>/dev/null || true"
}
]
}
]
}
}
第一个脚本 auto_check.sh 做多语言检查:
- Python → ruff check
- JS/TS → eslint
- Go → go vet
- Rust → cargo check
- 所有文件 → 行数超过 1000 行警告
第二个内联命令专门处理 Python 文件的 ruff 格式化。
4. 会话启动注入上下文(SessionStart)
问题:每次新开 session,AI 不知道之前的对话历史。
配置:
json
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "bash /path/to/ocean-dock/hooks/session_start.sh"
}
]
}
]
}
}
脚本做的事:
- 查找当前项目的历史 session 列表
- 展示最近 8 个 session(ID、时间、消息数、首条消息预览)
- 通过
systemMessage注入给 Claude - 检测项目结构是否变化,如果变了提醒 AI 更新架构概览
效果:AI 开场会说"我看到这些历史 session,需要恢复哪个的上下文吗?"
5. 会话结束自动保存摘要(Stop)
问题:session 结束后,下次无法接续。
配置:
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "bash /path/to/ocean-dock/hooks/stop.sh"
}
]
}
]
}
}
脚本做的事:
- 调用 ocean-dock 生成 session 摘要
- 保存到
.claude/LAST_HANDOFF.md - 对比项目目录结构指纹,如果变化了标记"架构记忆需要更新"
6. 上下文压缩保护(PreCompact)
问题:对话太长时 Claude Code 会自动压缩上下文,但压缩可能丢失关键信息。
配置:
json
{
"hooks": {
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "bash /path/to/ocean-dock/hooks/pre_compact.sh"
}
]
}
]
}
}
脚本做的事:
- 在压缩前调用 ocean-dock 生成当前 session 摘要
- 通过
systemMessage注入回 Claude,让压缩后的对话仍然保留关键信息
7. 桌面通知(Notification)
问题:等 AI 执行长任务时,不知道它什么时候需要权限确认或已经完成。
配置:
json
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "bash /path/to/ocean-dock/hooks/notify.sh"
}
]
},
{
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "bash /path/to/ocean-dock/hooks/notify.sh"
}
]
}
]
}
}
AI 需要权限确认或任务完成空闲时,桌面弹通知。
五、项目级 Hooks 示例
上面是全局配置。对于特定项目,还可以在 .claude/hooks/ 下放项目专用的 hook 文件。
比如 ai-governance-example 项目,我配了文档同步提醒:
PostToolUse 自动化矩阵:
| 文件模式 | 触发动作 |
|---|---|
**/*.py |
ruff format + ruff check --fix |
**/*.{ts,tsx} |
prettier --write |
backend/app/api/**/*.py |
提醒更新 API 文档 |
backend/app/models/**/*.py |
提醒更新数据模型文档 |
backend/app/services/**/*.py |
提醒更新验收标准 |
**/Dockerfile 等 |
提醒更新部署文档 |
| 所有文件 | 检查 TODO/FIXME 残留 |
docs/**/*.md |
文档格式验证 |
提醒类 Hook 的写法示例:
json
{
"event": "PostToolUse",
"tool": "Write|Edit",
"glob": "backend/app/api/**/*.py",
"action": "API 修改提醒更新文档",
"message": "你修改了 API 文件,文档可能不同步。检查以下文档是否需要更新:\n1. docs/reference/api-spec.yaml\n2. docs/design/ 对应功能的状态标记"
}
六、所有可用事件速查
| 事件 | 触发时机 | 能阻断吗 |
|---|---|---|
SessionStart |
会话启动/恢复 | 不能 |
UserPromptSubmit |
用户提交 prompt 前 | 能 |
PreToolUse |
工具执行前 | 能(核心拦截点) |
PermissionRequest |
权限对话框弹出时 | 能 |
PostToolUse |
工具执行后 | 不能(已执行) |
PostToolBatch |
一批工具都完成后 | 能(停止循环) |
Notification |
发送通知时 | 不能 |
SubagentStart |
子 Agent 启动 | 不能 |
SubagentStop |
子 Agent 完成 | 能 |
Stop |
AI 停止输出 | 能(让它继续干活) |
PreCompact |
上下文压缩前 | 能 |
PostCompact |
上下文压缩后 | 不能 |
SessionEnd |
会话结束 | 不能 |
七、Hook 脚本编写要点
输入处理
所有脚本通过 stdin 接收 JSON。Python 解析最方便:
bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('tool_input',{}).get('command',''))")
PreToolUse 的输入示例:
json
{
"session_id": "abc123",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}
PostToolUse 的输入多了 tool_response 和 duration_ms。
输出控制
简单拦截 :exit 2 + stderr 反馈
bash
echo "危险操作被拦截" >&2
exit 2
JSON 精细控制:exit 0 + stdout JSON
bash
# PreToolUse 允许执行并注入上下文
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","additionalContext":"当前环境: production"}}'
# Stop 阻止 AI 停下来,让它继续
echo '{"decision":"block","reason":"还有测试没跑完"}'
# SessionStart 注入上下文
echo '{"systemMessage":"最近变更: feat/auth-refactor 分支有未提交代码"}'
关键注意点
- 安全命令白名单 :拦截脚本里记得放行只读命令(
ls、git status、echo),不然 AI 连查看目录都被拦 - 脚本要幂等:同一个 Hook 可能被多次触发,脚本要能安全重复执行
- 超时控制 :默认超时 600 秒,可以通过
timeout字段调整 - stdout 只输出有效内容:shell 启动时的欢迎文本会干扰 JSON 解析
八、调试技巧
/hooks命令 :在 Claude Code 里输入/hooks,能看到所有已配置的 Hook 及其来源claude --debug:debug 日志会记录每个 Hook 的匹配、执行、退出码claude --debug-file <path>:指定 debug 日志输出位置CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose:更详细的匹配过程日志
九、进阶玩法
异步 Hook
后台执行,不阻塞 AI:
json
{
"type": "command",
"command": "/path/to/run-tests.sh",
"async": true,
"timeout": 300
}
适合跑测试套件这种耗时操作,AI 继续工作,测试完了把结果反馈回来。
HTTP Hook
把事件发到远程服务:
json
{
"type": "http",
"url": "http://localhost:8080/hooks/pre-tool-use",
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}
Prompt Hook
让 AI 判断该不该放行:
json
{
"type": "prompt",
"prompt": "评估 Claude 是否该停止: $ARGUMENTS。检查所有任务是否完成。",
"timeout": 30
}
底层调一个快模型返回 {ok: true/false} 判断。
Agent Hook
比 Prompt Hook 更强------能启动一个子 Agent 读文件、跑命令后再判断:
json
{
"type": "agent",
"prompt": "验证所有单元测试是否通过。运行测试套件并检查结果。$ARGUMENTS",
"timeout": 120
}
十、总结
我用 Hooks 解决的核心问题:
| 问题 | Hook 方案 | 效果 |
|---|---|---|
| AI 忘记 lint | PostToolUse 自动 ruff format | 写完就格式化 |
| 危险命令 | PreToolUse 拦截 | force push 等直接拦掉 |
| 临时文件污染 | PreToolUse 拦截 | 根目录不让建 test.py |
| 文档过时 | PostToolUse 提醒 | 改 API 文件时弹提醒 |
| 上下文丢失 | PreCompact 注入摘要 | 压缩后关键信息还在 |
| 会话不连续 | Stop 保存 + SessionStart 恢复 | 开场就能接上 |
配置层级建议 :通用防护放全局 ~/.claude/settings.json,项目特定的放 .claude/settings.json,团队共享的提交到仓库。
调试口诀 :先用 /hooks 确认配置生效,再用 --debug 看执行日志。
十一、补充:.githooks(Git Hooks)
Claude Code Hooks 管 AI 的行为,Git Hooks 管人的行为(包括 AI 帮你 commit/push 时)。
两者配合形成完整防线:
┌─────────────────────────────────────────────────────────────────┐
│ 双层防护体系 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Claude Code Hooks Git Hooks (.githooks) │
│ ┌────────────────┐ ┌────────────────┐ │
│ │ AI 写代码时 │ │ git commit 时 │ │
│ │ • 拦截危险命令 │ │ • 垃圾文件检测 │ │
│ │ • 自动格式化 │ ──▶ │ • 敏感信息扫描 │ │
│ │ • 上下文保护 │ │ • commit 格式 │ │
│ │ • 文档同步提醒 │ │ • 测试运行 │ │
│ └────────────────┘ └────────────────┘ │
│ 实时拦截 提交门禁 │
│ │
└─────────────────────────────────────────────────────────────────┘
为什么用 .githooks 而不是 .git/hooks
.git/hooks/ 不会被提交到仓库,团队成员拿不到。.githooks/ 放在项目根目录,通过 git config core.hooksPath .githooks 激活,团队共享。
激活方式
bash
# 项目根目录执行一次即可
git config core.hooksPath .githooks
也可以在 AGENTS.md 或 CLAUDE.md 里写上这句,让 AI 在新项目初始化时自动执行。
我用的四个 Git Hook
1. pre-commit --- 提交前检查
三道关卡:
bash
# ① 垃圾文件检测
# 扫描暂存区,发现 __pycache__/、.pyc、.DS_Store、*.tmp、*.bak 直接报错
# ② 敏感信息扫描
# 正则匹配 password=、api_key=、secret=、token=、sk-xxx 模式
# ③ ruff 代码检查(如果可用)
# 只检查暂存的 Python 文件
效果示例:
[pre-commit] 运行检查...
[1/3] 检查垃圾文件...
[OK] 未检测到垃圾文件
[2/3] 检查敏感信息...
[WARNING] config.py 可能包含敏感信息 (pattern: api_key\s*=\s*["']...)
[3/3] ruff 检查...
[OK] ruff check 通过
[pre-commit] 所有检查通过
不通过时 commit 被阻止,必须修复后重试。用 git commit --no-verify 可以跳过,但显然不推荐。
2. commit-msg --- 提交格式校验
检查 commit message 是否符合 Conventional Commits 格式:
feat(api): 添加用户认证接口
fix(parser): 修复空输入崩溃问题
docs: 更新 API 文档
这个 hook 只警告不阻断(exit 0),因为有时候确实需要写非标准格式的 commit message。另外检查标题长度是否超过 72 字符。
3. pre-push --- 推送前跑测试
bash
# 支持 SKIP_PRE_PUSH_TESTS=1 git push 跳过
# 如果没有 tests/ 目录或 pytest 没装也自动跳过
# pytest 退出码 5(无测试收集)视为通过
确保推到远程的代码至少本地测试是通过的。
4. post-checkout --- 切换分支后清理缓存
切换分支时自动清理 __pycache__/、.pyc、.pyo,避免残留缓存文件造成干扰。
.githooks 目录结构
.githooks/
├── pre-commit # 提交前:垃圾文件 + 敏感信息 + lint
├── commit-msg # 提交信息格式校验
├── pre-push # 推送前:跑测试
└── post-checkout # 切分支后:清理缓存
和 Claude Code Hooks 的分工
| 检查项 | Claude Code Hook | Git Hook | 理由 |
|---|---|---|---|
| 危险命令拦截 | PreToolUse (Bash) | --- | AI 专属场景 |
| 自动格式化 | PostToolUse (Write/Edit) | --- | 实时修正 |
| 上下文保护 | PreCompact / Stop | --- | AI 会话专属 |
| 垃圾文件 | PreToolUse 拦截写入 | pre-commit 拦截提交 | 双保险 |
| 敏感信息 | --- | pre-commit 扫描 | 提交是最后防线 |
| commit 格式 | --- | commit-msg | Git 专属 |
| 测试 | --- | pre-push | 推送是最后防线 |
核心原则:本地 Hook 是开发反馈,CI 是合并门------两层独立,本地没启用 Hook 不会绕过 CI 检查。
参考链接
- Hooks 官方参考文档 --- 事件、配置、JSON 格式完整说明
- Hooks 入门指南 --- 官方手把手教程
- Claude Code Hooks 生产级实践 --- 三种 Hook 类型深度对比
- 12 个生产级 Hook 配置 --- 实用配置合集