Hooks 是 Claude Code 最适合做"确定性自动化"的机制。CLAUDE.md 能告诉 Claude "尽量这样做",Hooks 则能在关键生命周期点自动执行脚本、HTTP 请求或 Prompt,并且可以拦截危险操作。
Hooks 适合解决什么问题?
典型场景:
- 安全拦截:阻止
rm -rf、git push --force、读取.env等高风险操作 - 自动格式化:文件写入后自动运行
prettier、eslint --fix、go fmt - 审计记录:记录 Claude 执行过的 Bash 命令、修改过的文件
- 环境管理:进入不同目录时自动加载 direnv 或项目环境
- CI/脚本准备:用
Setup事件做一次性初始化 - 上下文治理:在
PreCompact/PostCompact前后保存关键状态
一句话:凡是"每次都必须发生"的动作,用 Hooks;凡是"希望 Claude 遵守"的规则,用 CLAUDE.md。
Hook 生命周期:三类触发节奏
Claude Code Hooks 按触发频率分三类:
- 会话级:
SessionStart、SessionEnd、Setup - 回合级:
UserPromptSubmit、Stop、StopFailure - 工具级:
PreToolUse、PostToolUse、PostToolUseFailure、PostToolBatch
此外还有配置、文件、工作树和 MCP 相关事件。
28 个 Hook 事件速查
| 事件 | 触发时机 | 常见用途 |
|---|---|---|
SessionStart | 会话开始或恢复 | 打印项目状态、加载环境摘要 |
Setup | --init-only 或 CI 初始化 | 一次性准备依赖、缓存 |
UserPromptSubmit | 用户 Prompt 提交前 | Prompt 审计、注入过滤 |
UserPromptExpansion | Slash/Skill 展开前 | 阻止危险命令展开 |
PreToolUse | 工具调用前 | 安全拦截、权限决策 |
PermissionRequest | 权限弹窗出现 | 自动审批/记录 |
PermissionDenied | auto mode 拒绝工具调用 | 允许模型重试某些安全替代方案 |
PostToolUse | 工具成功后 | 格式化、测试、日志 |
PostToolUseFailure | 工具失败后 | 收集错误、重试建议 |
PostToolBatch | 并行工具批次完成后 | 批量汇总、统一检查 |
Notification | Claude 发送通知 | 推送到 Slack/Discord |
SubagentStart | Subagent 启动 | 记录并行任务 |
SubagentStop | Subagent 结束 | 汇总子任务结果 |
TaskCreated | TaskCreate 创建任务 | 任务审计 |
TaskCompleted | 任务标记完成 | 任务统计 |
Stop | Claude 完成回复 | 自动摘要、保存结果 |
StopFailure | API 错误结束 | 错误上报 |
TeammateIdle | Agent team 队友即将 idle | 自动分配后续任务 |
InstructionsLoaded | CLAUDE.md 或 rules 加载 | 检查规则是否生效 |
ConfigChange | 配置文件改变 | 重新验证配置 |
CwdChanged | 工作目录改变 | 切换环境变量 |
FileChanged | 监听文件变化 | 外部编辑同步 |
WorktreeCreate | 创建 Worktree | 替换默认 Git 行为 |
WorktreeRemove | 删除 Worktree | 清理临时资源 |
PreCompact | 上下文压缩前 | 保存计划和关键状态 |
PostCompact | 压缩完成后 | 恢复摘要和上下文 |
Elicitation | MCP 请求用户输入 | 审计外部工具交互 |
ElicitationResult | 用户回应 MCP 请求后 | 过滤敏感输入 |
配置结构:matcher + if + handler
Hooks 通常写在 .claude/settings.json:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
}
]
}
]
}
}三个核心概念:
matcher:先按工具名或事件范围匹配,例如Bash、Edit|Write、*if:进一步按子命令过滤,例如Bash(rm *)hooks:真正执行的 handler,可以是 command、HTTP endpoint 或 prompt
if 很重要:它能避免每个 Bash 命令都启动脚本,降低开销。
实战:阻止危险 rm 命令
.claude/hooks/block-rm.sh:
bash
#!/bin/bash
CMD_TO_CHECK=`jq -r '.tool_input.command'`
if echo "${CMD_TO_CHECK}" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Destructive command blocked by hook"
}
}'
else
exit 0
fi工作流:
- Claude 想执行
Bash "rm -rf /tmp/build" PreToolUse事件触发matcher: Bash命中if: Bash(rm *)命中- 脚本读取 stdin JSON,返回
permissionDecision: deny - Claude Code 阻止工具调用,并把原因反馈给模型
PostToolUse:文件修改后自动检查
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "npm run lint -- --fix"
}
]
}
]
}
}适合:
- TypeScript 项目:
npm run lint -- --fix - Go 项目:
gofmt -w - Python 项目:
ruff check --fix && ruff format - Rust 项目:
cargo fmt
注意:这类 hook 应该保持快速,避免每次编辑都跑完整测试。全量测试更适合 Stop 或手动 Skill。
推荐团队基线配置
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm -rf *)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/deny-dangerous.sh"
},
{
"type": "command",
"if": "Bash(git push --force*)",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/deny-force-push.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/format-changed-files.sh"
}
]
}
],
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/save-session-state.sh"
}
]
}
]
}
}Hooks vs CLAUDE.md vs Skills
| 需求 | 用什么 | 原因 |
|---|---|---|
| 每次修改后自动格式化 | Hooks | 确定性执行 |
| 写 API 时遵循响应格式 | CLAUDE.md / rules | 持续性规则 |
| 发布到 staging 的完整流程 | Skills | 步骤化任务 |
| 阻止危险命令 | Hooks | 必须强制执行 |
| 代码审查清单 | Skills | 按需加载,避免常驻上下文 |
最佳实践
- 安全类 hook 用 deny,不要只打印 warning
- 格式化 hook 要快,慢检查放到
Stop或 CI - 脚本读取 stdin JSON,不要依赖脆弱的环境变量
- 配置进
.claude/settings.json,团队共享;个人实验放.claude/settings.local.json - Hook 脚本放
.claude/hooks/,并提交到 Git - 避免重复:Hook 已经自动 lint,就不要在 CLAUDE.md 里写"每次都运行 lint"
来源:Claude Code 官方文档 - Hooks reference | 整理:ClaudeEagle