Appearance
Hooks:自动化工作流
Hooks 是用户定义的 shell 命令,在 Claude Code 生命周期的特定节点自动执行。与 CLAUDE.md 指令不同,Hooks 是确定性的——它们保证某些动作一定会发生,而不是依赖 AI 决定要不要执行。
常见用途:
- 每次文件编辑后自动格式化代码
- Claude 需要你操作时发送桌面通知
- 阻止 Claude 修改受保护的文件
- 压缩上下文后重新注入关键信息
创建第一个 Hook
以桌面通知 Hook 为例:Claude 等待你输入时,你可以切换到其他任务,完成后收到通知。
第一步:添加 Hook 到设置
打开 ~/.claude/settings.json,添加 Notification hook:
macOS:
json
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
}
]
}
]
}
}Linux:
json
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
}
]
}
]
}
}Windows(PowerShell):
json
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')\""
}
]
}
]
}
}第二步:验证配置
在 Claude Code 中运行 /hooks,确认 Notification 事件下有你的 Hook。
常见 Hook 配方
编辑后自动格式化
每次 Claude 编辑文件后自动运行 Prettier:
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}阻止修改受保护文件
防止 Claude 修改 .env、package-lock.json 等敏感文件:
第一步:创建校验脚本 .claude/hooks/protect-files.sh:
bash
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH 匹配受保护模式 '$pattern'" >&2
exit 2
fi
done
exit 0第二步:赋予执行权限(macOS/Linux):
bash
chmod +x .claude/hooks/protect-files.sh第三步:注册 Hook:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}压缩后重新注入上下文
上下文压缩后,重新提醒 Claude 关键规则:
json
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo '提醒:使用 Bun 而不是 npm。提交前运行 bun test。当前 sprint:auth 重构。'"
}
]
}
]
}
}自动批准计划结束
不想每次计划结束都手动确认?用 PermissionRequest hook 自动批准:
json
{
"hooks": {
"PermissionRequest": [
{
"matcher": "ExitPlanMode",
"hooks": [
{
"type": "command",
"command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
}
]
}
]
}
}Hook 生命周期事件
| 事件 | 触发时机 | 常见用途 |
|---|---|---|
SessionStart | 会话开始或恢复 | 注入上下文、恢复环境 |
UserPromptSubmit | 提交提示词、Claude 处理前 | 预处理、添加上下文 |
PreToolUse | 工具调用执行前(可阻止) | 验证、阻止危险操作 |
PostToolUse | 工具调用成功后 | 格式化、日志、通知 |
PermissionRequest | 权限弹窗出现时 | 自动批准安全操作 |
Notification | Claude 等待输入时 | 桌面通知 |
Stop | Claude 完成响应时 | 验证任务是否完成 |
SessionEnd | 会话结束时 | 清理临时文件 |
Hook 如何工作
输入:Hook 从 stdin 接收 JSON 格式的事件数据:
json
{
"session_id": "abc123",
"cwd": "/Users/sarah/myproject",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}输出:通过退出码和 stdout/stderr 告诉 Claude Code 该做什么:
| 退出码 | 行为 |
|---|---|
0 | 操作继续。UserPromptSubmit 和 SessionStart 中 stdout 内容被添加到上下文 |
2 | 阻止操作。stderr 内容作为反馈发送给 Claude |
| 其他非零 | 操作继续,stderr 记录到日志(按 Ctrl+O 查看) |
阻止操作示例:
bash
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q "drop table"; then
echo "Blocked: 不允许执行删表操作" >&2
exit 2
fi
exit 0用 matcher 过滤 Hook
不加 matcher 时,Hook 在每次事件触发。用 matcher 缩小范围:
json
{
"PostToolUse": [
{
"matcher": "Edit|Write", // 只在编辑/写入后触发
"hooks": [...]
}
]
}不同事件的 matcher 过滤字段:
| 事件 | matcher 过滤字段 |
|---|---|
PreToolUse, PostToolUse | 工具名称 |
SessionStart | 会话来源(startup/resume/clear/compact) |
Notification | 通知类型(permission_prompt/idle_prompt/auth_success) |
ConfigChange | 配置来源 |
MCP 工具用 mcp__服务器名__工具名 格式,如 mcp__github__.* 匹配所有 GitHub MCP 工具。
Hook 配置位置
| 位置 | 作用域 | 是否可共享 |
|---|---|---|
~/.claude/settings.json | 所有项目 | 否(本地) |
.claude/settings.json | 单个项目 | 是(可提交 git) |
.claude/settings.local.json | 单个项目 | 否(gitignore) |
高级 Hook 类型
Prompt-based Hooks
需要判断而非固定规则时,用 type: "prompt" 让 Claude 模型(默认 Haiku)来做决定:
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "检查是否所有任务都已完成。如果没有,返回 {\"ok\": false, \"reason\": \"还需要做什么\"}。"
}
]
}
]
}
}返回 {"ok": false} 时,Claude 会根据 reason 继续工作。
Agent-based Hooks
需要读文件或运行命令来验证时,用 type: "agent" 创建能使用工具的验证代理:
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "验证所有单元测试通过。运行测试套件并检查结果。",
"timeout": 120
}
]
}
]
}
}HTTP Hooks
把事件数据 POST 到 HTTP 端点:
json
{
"hooks": {
"PostToolUse": [
{
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/tool-use",
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}
]
}
]
}
}常见问题
Hook 不触发
- 运行
/hooks确认 Hook 出现在正确事件下 - 检查 matcher 是否和工具名完全匹配(区分大小写)
PermissionRequest在非交互模式(-p)不触发,改用PreToolUse
Hook 报错
- 手动测试脚本:
echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh - 脚本未执行:检查是否有执行权限(
chmod +x) - 找不到命令:用绝对路径或
$CLAUDE_PROJECT_DIR
Stop hook 无限循环 解析 stop_hook_active 字段,已触发时直接退出:
bash
#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0
fi
# ... 其余 hook 逻辑相关资源
- Hooks 完整参考:完整的事件 schema 和高级功能
- 设置:在哪里配置 hooks