Logo Vincent
返回文章列表

Claude Code settings.json 详解(三):hooks 钩子全解析

Claude
Claude Code settings.json 详解(三):hooks 钩子全解析

hooks 是什么

Claude 每次执行工具(读文件、写文件、运行命令……)都会经历几个固定的生命周期节点:执行前、执行后、会话结束时。hooks 让你在这些节点上挂载自己的脚本——在 Claude 动手之前检查一遍,或者在它写完代码后自动跑一次 lint。

这是 Claude Code 配置里自动化程度最高的部分,也是最能体现”按你的规矩办事”的地方。

配置结构

hookssettings.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 POSTWebhook、外部系统集成

日常使用最多的是 command,其他三种适合更高级的场景。

command 类型的完整字段:

{
  "type": "command",
  "command": "your-shell-command",
  "shell": "bash",
  "timeout": 30,
  "statusMessage": "正在检查...",
  "async": false,
  "asyncRewake": false,
  "once": false
}
  • timeout:超时秒数
  • statusMessage:执行时显示的 spinner 提示文字
  • async: true:后台异步执行,不阻塞 Claude
  • asyncRewake: 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_promptauth_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 code 2 可以在 Claude 动手之前做任意检查和阻断
  • PostToolUse 可以在工具执行后触发自动修复或验证
  • Stop 可以在 Claude 结束前插入额外逻辑,或触发通知
  • stdin/stdout JSON 协议让 hook 脚本可以精确控制 Claude 的下一步行为

下一篇将介绍 settings.json 中的 env 和其他杂项字段。

更多同类文章

© 2026 vincentqiao.com . 保留所有权利。