Claude Code settings.json 详解(三):hooks 钩子全解析
目录
hooks 是什么
Claude 每次执行工具(读文件、写文件、运行命令……)都会经历几个固定的生命周期节点:执行前、执行后、会话结束时。hooks 让你在这些节点上挂载自己的脚本——在 Claude 动手之前检查一遍,或者在它写完代码后自动跑一次 lint。
这是 Claude Code 配置里自动化程度最高的部分,也是最能体现”按你的规矩办事”的地方。
配置结构
hooks 是 settings.json 的顶层字段,与 permissions 平级:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "echo '即将执行 Bash 命令' >> /tmp/claude.log"
}
]
}
],
"PostToolUse": [...],
"Stop": [...],
"Notification": [...]
}
}
结构是三层嵌套:
hooks
└── 事件名(如 PreToolUse)
└── [] HookMatcher 数组
├── matcher: 匹配规则(可选)
└── hooks: [] HookCommand 数组
每个事件下可以挂多个 matcher,每个 matcher 下可以挂多条命令。
四种 HookCommand 类型
| 类型 | 说明 | 适用场景 |
|---|---|---|
command | 执行 shell 命令 | 日志、lint、通知、阻断检查 |
prompt | 调用 LLM 做判断 | 智能审查、语义检查 |
agent | 启动子代理处理 | 复杂自动化流程 |
http | 向指定 URL 发送 HTTP POST | Webhook、外部系统集成 |
日常使用最多的是 command,其他三种适合更高级的场景。
command 类型的完整字段:
{
"type": "command",
"command": "your-shell-command",
"shell": "bash",
"timeout": 30,
"statusMessage": "正在检查...",
"async": false,
"asyncRewake": false,
"once": false
}
timeout:超时秒数statusMessage:执行时显示的 spinner 提示文字async: true:后台异步执行,不阻塞 ClaudeasyncRewake: true:后台执行,若 exit code 为 2 则唤醒模型once: true:只执行一次后自动移除
核心事件详解
Claude Code 共有 26 个以上的 hook 事件,以下是日常最实用的四个。
PreToolUse — 工具执行前
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": "/path/to/check.sh" }]
}
]
何时触发:Claude 即将调用某个工具,尚未执行时。
stdin 收到的 JSON:
{
"hook_event_name": "PreToolUse",
"session_id": "...",
"cwd": "/your/project",
"transcript_path": "/tmp/transcript.jsonl",
"tool_name": "Bash",
"tool_input": { "command": "rm -rf dist/" },
"tool_use_id": "..."
}
stdout 可输出的 JSON:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"additionalContext": "禁止删除 dist 目录"
}
}
permissionDecision 支持 "allow" / "deny" / "ask" 三个值。
exit code 语义:
| exit code | 效果 |
|---|---|
0 | 正常,不显示任何输出 |
2 | 阻断工具执行,将 stderr 内容告知模型 |
| 其他 | 仅将 stderr 显示给用户,工具继续执行 |
关键能力:exit code 2 配合 JSON output 的 permissionDecision: "deny" 可以完全阻断工具调用,比 permissions 规则更灵活,因为可以在脚本里写任意逻辑。
PostToolUse — 工具执行后
"PostToolUse": [
{
"matcher": "Write",
"hooks": [{ "type": "command", "command": "pnpm lint --fix" }]
}
]
何时触发:工具成功执行完毕之后。
stdin 收到的 JSON:
{
"hook_event_name": "PostToolUse",
"tool_name": "Write",
"inputs": { "file_path": "src/foo.ts", "content": "..." },
"response": { "type": "text", "text": "文件写入成功" },
"tool_use_id": "..."
}
stdout 可输出的 JSON:
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "lint 已自动修复"
}
}
exit code 语义:
| exit code | 效果 |
|---|---|
0 | 正常,输出显示在 transcript 模式中 |
2 | 将 stderr 立即告知模型 |
| 其他 | 仅将 stderr 显示给用户 |
Stop — 会话结束前
"Stop": [
{
"hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude 已完成\"'" }]
}
]
何时触发:Claude 即将结束本轮回复时。
stdin 收到的 JSON:
{
"hook_event_name": "Stop",
"stop_hook_active": false,
"last_assistant_message": "好的,我已经完成了修改。"
}
exit code 为 2 时:Claude 会读取 stderr 内容并继续对话,适合实现”Claude 停止前自动检查,发现问题就继续”的效果。
Stop 事件没有 matcher,对所有停止事件都生效。
Notification — 通知事件
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [{ "type": "command", "command": "afplay /System/Library/Sounds/Ping.aiff" }]
}
]
何时触发:Claude 发出权限请求、认证提示等通知时。
matcher 匹配 notification_type,常见值有 permission_prompt、auth_success 等。
这个事件是即发即忘的,不阻塞执行,适合做声音提醒、系统通知等副作用操作。
stdin/stdout 协议
每个 hook 脚本通过标准 IO 与 Claude Code 通信:
- stdin:收到一行 JSON,包含事件名、session_id、cwd、transcript_path 以及事件特有字段
- stdout:可以输出 JSON 来控制行为,也可以输出纯文本(会被记录到 transcript)
如果脚本第一行输出 {"async":true},则立即后台化,Claude 不等待结果继续执行。
可用的环境变量
hook 脚本执行时,可以访问以下环境变量:
| 变量 | 说明 |
|---|---|
CLAUDE_PROJECT_DIR | 稳定的项目根目录(非 worktree 路径) |
CLAUDE_ENV_FILE | 可写入 export VAR=val 的 .sh 文件(仅 bash) |
CLAUDE_PLUGIN_ROOT | 插件/skill 目录路径 |
matcher 格式
matcher 复用 permissions 里的规则语法,可以是:
"Write"— 精确匹配 Write 工具"Bash(git *)"— 匹配带 git 命令的 Bash 调用"Bash(npm:*)"— 旧式前缀匹配- 留空或省略 — 匹配所有调用
实用配置示例
写文件后自动 lint:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "cd $CLAUDE_PROJECT_DIR && pnpm lint --fix",
"timeout": 30
}
]
}
]
}
}
阻断危险 Bash 命令:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash /path/to/guard.sh"
}
]
}
]
}
}
guard.sh 里读取 stdin JSON,判断 tool_input.command 是否包含危险操作,危险则 exit 2 并向 stderr 输出原因。
Claude 完成时发送系统通知(macOS):
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude 已完成任务\" with title \"Claude Code\"'"
}
]
}
]
}
}
记录所有 Bash 命令到日志:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command' >> /tmp/claude-bash.log",
"async": true
}
]
}
]
}
}
async: true 让日志写入在后台进行,不影响 Claude 的执行速度。
注意事项
- 工作区信任:所有 hook 都需要工作区处于受信任状态才会执行,在不信任的工作区中 hook 会被跳过
- 来源优先级:
policySettings(企业策略)>projectSettings>userSettings>localSettings> 插件 hooks;各层的 hooks 会合并执行,而不是互相覆盖 - 超时控制:建议给每个 hook 设置合理的
timeout,避免因脚本卡住而阻塞 Claude
小结
hooks 是 Claude Code 配置体系里最强大的扩展点:
- 四种类型(
command/prompt/agent/http)覆盖从简单脚本到复杂自动化的需求 PreToolUse+ exit code2可以在 Claude 动手之前做任意检查和阻断PostToolUse可以在工具执行后触发自动修复或验证Stop可以在 Claude 结束前插入额外逻辑,或触发通知- stdin/stdout JSON 协议让 hook 脚本可以精确控制 Claude 的下一步行为
下一篇将介绍 settings.json 中的 env 和其他杂项字段。
相关推荐
Claude Code Agent Loop:拆解 AI 编程助手的心脏
Claude Code 是怎么一步步理解你的需求、调用工具、自我修复的?从源码角度拆解 Agent Loop 的核心架构——流式响应、并行工具执行、自动压缩、错误恢复,一次讲透。
Claude Code settings.json 详解(一):配置文件在哪里、谁说了算
全面介绍 Claude Code 的配置文件体系——五个配置来源的路径、优先级规则、数组合并与单值覆盖的区别、企业管理设置的多种下发方式。
Claude Code settings.json 详解(二):permissions 权限系统全解析
深入解析 Claude Code 的 permissions 配置——allow/deny/ask 三类规则、通配符语法、MCP 工具权限、defaultMode 各模式含义,以及 additionalDirectories 的作用。
Claude Code settings.json 详解(四):env、模型、认证与其他实用字段
全面介绍 Claude Code settings.json 中的 env 环境变量注入、模型配置、身份认证辅助、Git 提交署名、会话清理、语言与界面、思考深度、自动更新、记忆系统等实用字段。
Claude Code /agents 详解:自定义 AI 子代理,各司其职
详细介绍 Claude Code 的 /agents 命令——查看、管理和创建自定义 Agent,让不同任务由专门的 AI 角色来执行,从代码探索到架构规划各司其职。