Appearance
Hooks 使用指南:自动化你的开发工作流
Hooks 是用户自定义的 shell 命令,在 Claude Code 生命周期的特定时刻自动执行。它能让某些操作必然发生,而不是依赖 LLM 去"决定"要不要做——这就是 Hooks 的核心价值:确定性控制。
用 Hooks 可以做什么?格式化代码、拦截危险命令、发桌面通知、注入上下文、记录审计日志……这些"每次都要做"的事情,Hooks 一劳永逸。
本页讲常见用法。完整事件 schema、JSON 格式、异步 hooks 等进阶内容,参见 Hooks 参考文档。
5 分钟上手:创建第一个 Hook
下面以"Claude 需要你输入时发桌面通知"为例,一步步创建你的第一个 Hook。
第一步:添加配置
打开 ~/.claude/settings.json,添加 Notification hook(macOS 示例):
json
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code 需要你的输入\" with title \"Claude Code\"'"
}
]
}
]
}
}Linux 用 notify-send 'Claude Code' 'Claude Code 需要你的输入',Windows PowerShell 用 MessageBox API。
第二步:验证配置
在 CLI 中输入 /hooks,会看到所有已配置的 hook 事件,Notification 旁边应该有计数。
第三步:测试效果
让 Claude 做一些需要权限的操作,然后切换到其他窗口。收到通知了吗?
/hooks菜单只读,修改 Hook 需要直接编辑 settings.json,或者告诉 Claude 帮你改。
常用自动化场景
场景一:代码编辑后自动格式化
每次 Claude 编辑文件后,自动用 Prettier 格式化,保持代码风格一致:
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}jq 从事件 JSON 里提取文件路径,传给 Prettier。macOS 用 brew install jq 安装,Debian/Ubuntu 用 apt-get install jq。
场景二:保护敏感文件不被修改
防止 Claude 改动 .env、package-lock.json、.git/ 等文件:
第一步:创建保护脚本 .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 "已拦截:$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"
}
]
}
]
}
}场景三:上下文压缩后重新注入关键信息
上下文窗口满了会触发压缩(compaction),重要细节可能丢失。用 SessionStart hook 在每次压缩后重新注入关键信息:
json
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo '提醒:使用 Bun 而不是 npm。提交前运行 bun test。当前迭代:认证重构。'"
}
]
}
]
}
}echo 输出的任何文本都会加入 Claude 的上下文。可以替换成动态命令,比如 git log --oneline -5 显示最近提交记录。
如果要在每次会话开始时注入上下文(不只是压缩后),用 CLAUDE.md 更合适。
场景四:记录配置变更审计日志
追踪 settings 或 skills 文件的改动,方便合规审计:
json
{
"hooks": {
"ConfigChange": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "jq -c '{timestamp: now | todate, source: .source, file: .file_path}' >> ~/claude-config-audit.log"
}
]
}
]
}
}场景五:自动批准特定权限请求
跳过总是允许的操作的确认对话框。比如自动批准 ExitPlanMode(Claude 展示计划后的继续请求):
json
{
"hooks": {
"PermissionRequest": [
{
"matcher": "ExitPlanMode",
"hooks": [
{
"type": "command",
"command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
}
]
}
]
}
}尽量缩小 matcher 范围。用
.*或空 matcher 会自动批准所有权限请求,包括文件写入和 shell 命令,非常危险。
Hooks 的工作原理
所有 Hook 事件一览
| 事件 | 触发时机 |
|---|---|
SessionStart | 会话开始或恢复时 |
UserPromptSubmit | 你提交提示词后,Claude 处理前 |
PreToolUse | 工具调用执行前(可以拦截) |
PermissionRequest | 权限确认对话框出现时 |
PostToolUse | 工具调用成功后 |
PostToolUseFailure | 工具调用失败后 |
Notification | Claude Code 发送通知时 |
SubagentStart / SubagentStop | 子代理启动/结束时 |
Stop | Claude 完成回复时 |
StopFailure | API 错误导致轮次结束时 |
TeammateIdle | Agent Team 中成员即将空闲时 |
TaskCompleted | 任务被标记为完成时 |
InstructionsLoaded | CLAUDE.md 或 rules 文件加载时 |
ConfigChange | 配置文件在会话中被修改时 |
WorktreeCreate / WorktreeRemove | worktree 创建/删除时 |
PreCompact / PostCompact | 上下文压缩前/后 |
Elicitation / ElicitationResult | MCP 服务器请求用户输入时 |
SessionEnd | 会话结束时 |
Hook 类型
"type": "command"— 执行 shell 命令(最常用)"type": "http"— POST 事件数据到 URL"type": "prompt"— 调用 Claude 模型判断(适合需要"判断力"的场景)"type": "agent"— 生成可访问文件/工具的子代理(适合需要验证代码库状态的场景)
通过退出码控制行为
| 退出码 | 效果 |
|---|---|
exit 0 | 允许操作继续 |
exit 2 | 拦截操作,stderr 内容作为反馈给 Claude |
| 其他 | 操作继续,stderr 记录到日志 |
拦截示例:
bash
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q "drop table"; then
echo "已拦截:不允许删除数据库表" >&2
exit 2
fi
exit 0用 Matcher 过滤触发范围
不加 matcher,hook 对每次事件都触发。加 matcher 可以缩小范围,值是正则表达式:
json
{
"matcher": "Edit|Write"
}| 事件类型 | Matcher 匹配的字段 | 示例值 |
|---|---|---|
PreToolUse, PostToolUse, PermissionRequest | 工具名 | Bash, Edit|Write, mcp__.* |
SessionStart | 会话启动原因 | startup, resume, clear, compact |
SessionEnd | 会话结束原因 | clear, resume, logout |
ConfigChange | 配置来源 | user_settings, project_settings |
PreCompact, PostCompact | 压缩触发方式 | manual, auto |
配置文件位置与作用域
| 位置 | 作用范围 | 是否可共享 |
|---|---|---|
~/.claude/settings.json | 所有项目 | 否(仅本机) |
.claude/settings.json | 当前项目 | 是(可提交到仓库) |
.claude/settings.local.json | 当前项目 | 否(gitignore) |
进阶:Prompt-based Hooks
当判断需要"智能"而非规则时,用 type: "prompt" hooks。Claude 会调用一个小模型(默认 Haiku)来做判断,返回 {"ok": true} 或 {"ok": false, "reason": "..."}。
用例:在 Claude 准备停止时检查任务是否真的完成了:
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "检查所有请求的任务是否完成。若未完成,回复 {\"ok\": false, \"reason\": \"还剩什么未完成\"}。"
}
]
}
]
}
}进阶:Agent-based Hooks
需要检查文件或运行命令来验证条件时,用 type: "agent" hooks。子代理可以读文件、搜索代码,再返回决策。
用例:在 Claude 停止前验证测试是否通过:
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "验证所有单元测试通过。运行测试套件并检查结果。$ARGUMENTS",
"timeout": 120
}
]
}
]
}
}常见问题排查
Hook 没有触发?
- 用
/hooks确认 hook 出现在正确事件下 - 检查 matcher 是否大小写匹配(区分大小写)
- 非交互模式(
-p)下PermissionRequesthook 不触发,改用PreToolUse
报错 "command not found"? 用绝对路径或 $CLAUDE_PROJECT_DIR 引用脚本;确保脚本有执行权限(chmod +x)。
Stop hook 死循环? 在 hook 脚本里检查 stop_hook_active 字段:
bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0
fiJSON 解析失败? Shell 配置文件(~/.zshrc)里的 echo 语句会污染 hook 输出。用 if [[ $- == *i* ]]; then 包裹,只在交互 shell 里执行。
开启详细模式(Ctrl+O)或用 claude --debug 查看 hook 执行详情。
相关文档
- Hooks 完整参考 — 事件 schema、JSON 输出格式、HTTP hooks
- 权限配置 — 管理 Claude Code 的操作权限