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 的实际效果
相关推荐
苏三说技术1 小时前
Claude Code从失控到起飞,只用了这些技巧
后端
长栎2 小时前
写 for 循环写了十年,你却从没用过迭代器模式最狠的那一面
后端
LiaCode2 小时前
Redis 在生产项目的使用
前端·后端
用户559822481222 小时前
Docker Compose Down 导致容器数据误删——ext4 日志恢复全记录
后端
LiaCode2 小时前
一天学完 redis 的爽翻版核心知识总结
前端·后端
大刚测试开发实战2 小时前
如何内网穿透访问本地私有化部署的TestHub
前端·后端·github
xiaodaoluanzha2 小时前
迄今為止,最簡單的編程語言 Nolang
前端·后端
Csvn2 小时前
Docker 容器管理入门 — 从镜像到容器编排
后端
用户762352425913 小时前
ShardingJDBC
后端
行者全栈架构师3 小时前
IDEA 中 Maven 项目的 15 个红色报错快速解决方法
java·后端