Claude Code 的 Hooks 系统

Hooks 是用户自定义的 shell 命令、HTTP 端点、LLM 提示词或 Agent 验证器,在特定生命周期事件触发时执行。它们让你无需修改核心代码,即可实现自动化、审计、流程拦截和功能扩展。


概述

核心源文件:


Hook 配置

Hooks 在 settings.jsonhooks 键下配置:

json 复制代码
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Bash 即将运行' >> ~/.claude/audit.log"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$TOOL_INPUT_PATH\""
          }
        ]
      }
    ]
  }
}

配置文件位置与作用域:

  • ~/.claude/settings.json --- 全局(适用于所有项目)
  • .claude/settings.json --- 项目级(团队共享)
  • .claude/settings.local.json --- 本地个人级(已加入 .gitignore)

Hooks 也可以在 Skill 或 Agent 的 frontmatter 中配置。


所有 Hook 事件

会话生命周期

事件 触发时机 关键输入字段
Setup CLI 首次安装或维护运行 `trigger: "init"
SessionStart 会话开始(启动、恢复、清空、压缩后) source, model, agent_type
SessionEnd 会话结束 exit_reason
Stop 模型完成响应 stop_hook_active, last_assistant_message
StopFailure 模型响应以错误结束 error, error_details, last_assistant_message

Stop Hook 特殊能力: 返回 { "continue": true } 可重新激活模型------它会将 Hook 的输出作为新消息来处理。这让 Hook 可以驱动自主循环。

工具生命周期

事件 触发时机 关键输入字段
PreToolUse 任意工具执行前 tool_name, tool_input, tool_use_id
PostToolUse 工具执行成功后 tool_name, tool_input, tool_response, tool_use_id
PostToolUseFailure 工具执行失败后 tool_name, tool_input, error, is_interrupt

PreToolUse Hook 特殊能力:

  • decision: "block" → 阻止工具运行
  • updatedInput: {...} → 在执行前静默改写工具输入
  • additionalContext: "..." → 向模型注入额外上下文

权限系统

事件 触发时机 关键输入字段
PermissionRequest 工具需要用户授权 tool_name, tool_input, permission_suggestions
PermissionDenied 工具被拒绝执行 tool_name, tool_input, reason

PermissionRequest Hook 特殊能力: 可在不询问用户的情况下自动批准或拒绝:

json 复制代码
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": { "behavior": "allow" }
  }
}

用户交互

事件 触发时机 关键输入字段
UserPromptSubmit 用户发送消息 prompt
Notification Claude 发送桌面通知 message, title, notification_type
Elicitation MCP 服务器请求用户输入 mcp_server_name, message, mode, requested_schema
ElicitationResult 用户响应 MCP 请求 mcp_server_name, action, content

UserPromptSubmit Hook 能力: 可向模型注入 additionalContext,或提供 initialUserMessage 覆盖。

压缩(Compaction)

事件 触发时机 关键输入字段
PreCompact 上下文压缩前 `trigger: "manual"
PostCompact 压缩完成后 trigger, compact_summary

文件系统与配置

事件 触发时机 关键输入字段
CwdChanged 工作目录变更 old_cwd, new_cwd
FileChanged 监听的文件在磁盘上发生变化 file_path, `event: "change"
InstructionsLoaded CLAUDE.md 文件被加载 file_path, memory_type, load_reason
ConfigChange settings.json 发生变更 source, file_path

FileChanged 配置方法:SessionStartCwdChanged Hook 的输出中注册监听路径:

json 复制代码
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "watchPaths": ["/path/to/watch", "/another/path"]
  }
}

多 Agent 与 Worktree

事件 触发时机 关键输入字段
SubagentStart Agent 子进程启动 agent_id, agent_type
SubagentStop Agent 子进程结束 agent_id, agent_type, agent_transcript_path, last_assistant_message
TeammateIdle 协作 Agent 进入空闲 teammate_name, team_name
TaskCreated 后台任务被创建 task_id, task_subject, teammate_name
TaskCompleted 后台任务完成 task_id, task_subject
WorktreeCreate Git worktree 被创建 name
WorktreeRemove Git worktree 被移除 worktree_path

Hook 类型

1. 命令 Hook(type: "command"

运行一个 shell 命令。Hook 输入 JSON 以环境变量和 $STDIN 的形式提供。

json 复制代码
{
  "type": "command",
  "command": "python3 ~/.claude/hooks/audit.py",
  "shell": "bash",
  "timeout": 30,
  "statusMessage": "审计中...",
  "async": false,
  "asyncRewake": false,
  "once": false,
  "if": "Bash(git *)"
}

关键字段说明:

  • shell --- "bash"(默认)或 "powershell"
  • timeout --- Hook 被强制终止前等待的秒数
  • statusMessage --- 运行时 UI 加载动画显示的文字
  • async: true --- 在后台运行,不阻塞模型
  • asyncRewake: true --- 异步运行 + 退出码为 2 时唤醒模型
  • once: true --- 运行一次后自动从 Hook 列表删除
  • if --- 权限规则过滤器(见下文)

命令 Hook 中可用的环境变量:

变量名 内容
CLAUDE_TOOL_NAME 被调用的工具名(PreToolUse)
CLAUDE_TOOL_USE_ID 唯一工具调用 ID
CLAUDE_SESSION_ID 当前会话 ID
CLAUDE_CWD 当前工作目录
CLAUDE_TRANSCRIPT_PATH 会话记录文件路径
TOOL_INPUT_* 工具输入字段(大写,嵌套字段用 _ 分隔)

所有工具输入也以 JSON 格式通过 stdin 传入。

退出码含义:

退出码 含义
0 成功------正常继续
1 非阻塞错误------记录日志但继续
2 阻塞错误------停止 Claude,向用户显示错误

2. 提示词 Hook(type: "prompt"

使用轻量 Claude 模型评估一段自然语言提示。提示词通过 $ARGUMENTS 接收 Hook 输入 JSON。

json 复制代码
{
  "type": "prompt",
  "prompt": "检查这个 bash 命令是否安全:$ARGUMENTS。以 JSON 格式回复:{\"decision\": \"allow\" 或 \"block\", \"reason\": \"...\"}",
  "model": "claude-haiku-4-5-20251001",
  "timeout": 60,
  "statusMessage": "检查安全性...",
  "if": "Bash"
}

提示词 Hook 的输出被解析为 JSON,并作为同步 Hook 响应处理。适用于正则表达式无法处理的语义/上下文感知决策。

3. Agent Hook(type: "agent"

运行完整的 Claude Agent 来验证或处理 Hook 上下文。比提示词 Hook 更重量级,但支持多步推理和工具调用。

json 复制代码
{
  "type": "agent",
  "prompt": "验证变更文件的单元测试是否已运行并通过:$ARGUMENTS",
  "model": "claude-sonnet-4-6",
  "timeout": 120,
  "statusMessage": "验证测试中..."
}

默认模型为 Haiku,以控制成本。

4. HTTP Hook(type: "http"

将 Hook 输入 JSON 以 POST 请求发送到外部端点。

json 复制代码
{
  "type": "http",
  "url": "https://my-audit-service.internal/claude-hooks",
  "headers": {
    "Authorization": "Bearer $MY_API_TOKEN"
  },
  "allowedEnvVars": ["MY_API_TOKEN"],
  "timeout": 10,
  "statusMessage": "通知审计服务..."
}

安全说明: allowedEnvVars 控制哪些环境变量可以在 headers 中被插值。未列出的变量将被替换为空字符串。


Hook 匹配器

每个事件的 Hook 列表包含匹配器(matchers)------用于决定何时触发的过滤器:

json 复制代码
{
  "PreToolUse": [
    {
      "matcher": "Bash",        ← 只在 Bash 工具调用时触发
      "hooks": [...]
    },
    {
      "matcher": "Write(src/)", ← 只在写入 src/ 目录时触发
      "hooks": [...]
    },
    {
      "matcher": "",            ← 所有工具都触发
      "hooks": [...]
    }
  ]
}

匹配器语法与权限规则语法一致:

  • "Bash" --- 精确匹配工具名
  • "Bash(git *)" --- 工具名 + 命令前缀通配符
  • "Write(src/)" --- 工具名 + 路径前缀
  • "" 或省略 --- 匹配所有

if 字段(单个 Hook 过滤器)

匹配器内的单个 Hook 可以添加 if 条件进行更精细的控制:

json 复制代码
{
  "matcher": "Bash",
  "hooks": [
    {
      "type": "command",
      "command": "log-dangerous.sh",
      "if": "Bash(rm *)"   ← 只在 rm 命令时运行
    }
  ]
}

Hook 响应协议

Hooks 通过 stdout JSON 与 Claude Code 通信。

同步响应

json 复制代码
{
  "continue": true,
  "suppressOutput": false,
  "stopReason": "",
  "decision": "approve",
  "reason": "操作安全",
  "systemMessage": "警告:检测到破坏性操作",
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "updatedInput": { "command": "safe-version-of-command" },
    "additionalContext": "注入模型的额外上下文"
  }
}

异步响应

用于不需要阻塞的长时间运行 Hook:

json 复制代码
{
  "async": true,
  "asyncTimeout": 300
}

Hook 进程继续运行。若以退出码 2 退出,模型会被唤醒并收到错误信息。

各事件专属 hookSpecificOutput

每个 Hook 事件都有自己的输出 Schema。关键示例:

PreToolUse --- 改写输入、注入上下文或强制批准:

json 复制代码
{
  "hookEventName": "PreToolUse",
  "permissionDecision": "allow",
  "updatedInput": { "command": "更安全的命令" },
  "additionalContext": "已根据安全策略修改"
}

SessionStart --- 注册文件监听路径:

json 复制代码
{
  "hookEventName": "SessionStart",
  "additionalContext": "项目上下文已加载",
  "watchPaths": ["/project/src", "/project/tests"],
  "initialUserMessage": "开始运行:npm test"
}

PermissionRequest --- 自动批准或拒绝:

json 复制代码
{
  "hookEventName": "PermissionRequest",
  "decision": {
    "behavior": "allow",
    "updatedInput": { "command": "已审计的命令" }
  }
}

PermissionDenied --- 重试其他方案:

json 复制代码
{
  "hookEventName": "PermissionDenied",
  "retry": true
}

Hook 执行顺序

对于有多个匹配器的特定事件:

  1. 收集所有匹配的匹配器下的 Hooks
  2. 每个匹配器内的 Hooks 顺序执行
  3. 多个匹配器的 Hooks 可能并行运行(实现细节)
  4. 第一个阻塞结果(exit 2continue: false)停止后续执行
  5. 所有 Hooks 的 additionalContext 被拼接后注入

回调 Hooks(内部机制)

除了基于文件的 Hooks,Claude Code 内部还使用回调 Hooks(Callback Hooks) 。这些是通过 registerPostSamplingHook() 等 API 注册的 TypeScript 函数,遵循相同的 HookInput → HookJSONOutput 契约,但在进程内运行,无子进程开销。

typescript 复制代码
type HookCallback = {
  type: 'callback'
  callback: (input: HookInput, toolUseID: string | null, abort: AbortSignal) => Promise<HookJSONOutput>
  timeout?: number
  internal?: boolean   // 排除在分析指标之外
}

实战示例

文件写入后自动格式化

json 复制代码
{
  "PostToolUse": [{
    "matcher": "Write",
    "hooks": [{
      "type": "command",
      "command": "npx prettier --write \"$TOOL_INPUT_FILE_PATH\" 2>/dev/null || true"
    }]
  }]
}

拦截危险 shell 命令

json 复制代码
{
  "PreToolUse": [{
    "matcher": "Bash",
    "hooks": [{
      "type": "command",
      "command": "bash -c 'if echo \"$TOOL_INPUT_COMMAND\" | grep -qE \"rm -rf|DROP TABLE\"; then echo \"{\\\"decision\\\": \\\"block\\\", \\\"reason\\\": \\\"危险命令已被拦截\\\"}\"; exit 2; fi'"
    }]
  }]
}

会话结束时发送通知

json 复制代码
{
  "Stop": [{
    "matcher": "",
    "hooks": [{
      "type": "command",
      "command": "osascript -e 'display notification \"Claude 已完成\" with title \"Claude Code\"'",
      "async": true
    }]
  }]
}

代码编辑后运行测试(Agent Hook)

json 复制代码
{
  "PostToolUse": [{
    "matcher": "Edit",
    "hooks": [{
      "type": "agent",
      "prompt": "检查被编辑的文件是否有测试。如果有,运行测试并报告通过/失败状态。输入:$ARGUMENTS",
      "timeout": 120
    }]
  }]
}

会话启动时注入环境上下文

json 复制代码
{
  "SessionStart": [{
    "matcher": "",
    "hooks": [{
      "type": "command",
      "command": "echo \"{\\\"hookSpecificOutput\\\": {\\\"hookEventName\\\": \\\"SessionStart\\\", \\\"additionalContext\\\": \\\"分支:$(git branch --show-current),PR:$(gh pr view --json number -q .number 2>/dev/null || echo 无)\\\"}}\"",
      "statusMessage": "加载 git 上下文..."
    }]
  }]
}

Hook 信任与安全

  • 定义在 ~/.claude/settings.json 中的 Hooks 首次执行前需要一次性信任确认对话
  • .claude/settings.json(项目配置)中的 Hooks 受项目信任机制约束
  • CLAUDE_CODE_DISABLE_HOOKS=true 全局禁用所有 Hooks
  • Hook 输出注入的 systemMessage 会作为警告显示给用户,而非静默执行
  • 响应字段 suppressOutput: true 会从会话记录中隐藏 stdout,但不会抑制 Hook 的实际效果
相关推荐
Pkmer2 小时前
古法编程: 我要的是状态模式,策略模式不要误我大计
后端·设计模式
tenggouwa2 小时前
16GB Mac 同时开 3 个 Cursor 拯救我的mac
前端·后端
ltl2 小时前
激活函数:让网络「弯下来」的非线性魔法
后端
二哈赛车手3 小时前
新人笔记---浅扒一下Spring AI的chatClient的装配流程源码
后端
犹豫的果冻布丁3 小时前
OpenSpec 完全中文教程:AI 规范驱动开发入门与实战
前端·后端
哈里谢顿3 小时前
redis的分布式设计
后端·面试
IT_陈寒3 小时前
Java的HashMap竟然不是线程安全的?刚在生产环境踩了坑
前端·人工智能·后端
字节高级特工3 小时前
MySQL数据库基础与实战指南
数据库·c++·人工智能·后端·mysql·adb