C Claude Code Internals
| EN | ES

Hooks

Hooks 是响应 Claude Code 事件执行的 Shell 命令、LLM 提示词、多轮 Agent 或 HTTP 调用。它们可以阻止工具调用、后处理结果、转换提示词、自动格式化文件,以及编程式控制权限。

27 hook 事件 4 hook 类型 7 hook 来源 全部并行运行
i Claude Code 的事件驱动自动化
每次 Claude 运行工具、启动会话、请求权限或完成一轮对话时,hooks 都会触发。 它们并行运行,通过 stdin 接收 JSON 负载,并通过 stdout 返回结构化 JSON。 退出码 2 会阻止操作并将 stderr 发送给模型。

Hook 来源(优先级顺序)

# 来源 位置 作用域
1 Policy(托管) 托管设置 企业范围,最高权限
2 用户设置 ~/.claude/settings.json 个人,所有项目
3 项目设置 .claude/settings.json 与团队共享
4 本地设置 .claude/settings.local.json 项目私有
5 Plugin hooks ~/.claude/plugins/*/hooks/hooks.json Plugin 作用域
6 Session hooks 内存中(来自 skills/agents) 临时,仅当前会话
7 Function hooks 内存中(SDK/plugins) 编程式验证

特定事件的所有匹配 hooks 并行运行。

全部 27 种 Hook 事件

工具生命周期

PreToolUse 工具运行前。可以阻止或修改输入。
PostToolUse 工具成功后。可以注入上下文。
PostToolUseFailure 工具失败或中断后。

会话

SessionStart startup / resume / clear / compact
SessionEnd clear / resume / logout / exit / other
UserPromptSubmit 用户提交提示词时。可以阻止。
Stop 轮次完成(无更多工具调用)。
StopFailure 轮次失败并出错。
Setup 一次性设置:init / maintenance。

权限

PermissionRequest 权限对话框显示前。可以自动批准。
PermissionDenied 用户拒绝后。可以重试。
Notification Claude 发送通知时。

子代理和团队

SubagentStart 子代理启动时。
SubagentStop 子代理完成时。
TeammateIdle 队友无工作。
TaskCreated 团队中创建任务。
TaskCompleted 任务完成。

上下文

PreCompact 压缩前。manual / auto。
PostCompact 压缩后。包含摘要。
InstructionsLoaded CLAUDE.md / memory 文件加载时。
ConfigChange 设置文件变更。

MCP 和文件系统

Elicitation MCP 服务器请求用户输入。
ElicitationResult MCP elicitation 响应后。
WorktreeCreate Git worktree 创建。
WorktreeRemove Git worktree 移除。
CwdChanged 工作目录变更。
FileChanged 监听的文件变更。

4 种 Hook 类型

command — 最常用

{
  "type": "command",
  "command": "prettier --write "$FILE"",
  "if": "Write|Edit",
  "shell": "bash",
  "timeout": 30,
  "statusMessage": "Formatting...",
  "once": false,
  "async": false,
  "asyncRewake": false
}
timeout — 秒(默认:600)
once — 首次运行后自动移除
async — 在后台运行而不阻塞
asyncRewake — 后台运行 + 退出码 2 时唤醒模型

prompt — LLM 评估

{
  "type": "prompt",
  "prompt": "Review for security issues: $ARGUMENTS",
  "if": "Write|Edit",
  "timeout": 30,
  "model": "claude-sonnet-4-6",
  "statusMessage": "Reviewing..."
}
$ARGUMENTS — 替换为完整输入 JSON
model — 默认为小型快速模型
timeout — 默认:30 秒

agent — 多轮验证

{
  "type": "agent",
  "prompt": "Verify tests still pass: $ARGUMENTS",
  "timeout": 60,
  "model": "claude-sonnet-4-6"
}
可访问工具,最多 50 轮
model — 默认为 Haiku
timeout — 默认:60 秒

http — webhook / 外部 API

{
  "type": "http",
  "url": "https://api.example.com/hooks/claude",
  "headers": {
    "Authorization": "Bearer $API_TOKEN"
  },
  "allowedEnvVars": ["API_TOKEN"],
  "timeout": 30
}
将 hook 输入 JSON 作为 body POST
allowedEnvVars — headers 中 $VAR 展开的白名单
SSRF 防护:阻止 localhost/内部 IP

配置格式

settings.json 结构

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "prettier --check "$FILE"",
            "timeout": 10
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "notify-done.sh"
          }
        ]
      }
    ]
  }
}

Skill frontmatter hooks

---
hooks:
  PostToolUse:
    - matcher: "Write|Edit"
      hooks:
        - type: command
          command: "prettier --write $FILE"
---

注册为内存中的 session hooks。仅在 skill 调用期间激活(或如果 once: false 则在整个会话期间)。

Agent frontmatter hooks

相同的 YAML 格式。Stop hooks 自动转换为 SubagentStop hooks,作用域限定为 agent 的 ID,并在 agent 完成时清理。

Hook 输入(stdin)和输出(stdout)

基础输入字段(所有事件)

{
  "session_id": "abc123",
  "transcript_path": "/path/to/transcript.jsonl",
  "cwd": "/path/to/project",
  "hook_event_name": "PreToolUse",
  "permission_mode": "default",
  "agent_id": "agent-xyz",
  "agent_type": "general-purpose"
}

各事件的关键附加字段

PreToolUse / PostToolUse
tool_name, tool_input, tool_use_id
// PostToolUse adds: tool_response
// PostToolUseFailure adds: error, is_interrupt
SessionStart
source, agent_type, model
Stop / SubagentStop
stop_hook_active, last_assistant_message
// SubagentStop adds: agent_id, agent_transcript_path
PermissionRequest
tool_name, tool_input, permission_suggestions
FileChanged / CwdChanged
file_path, event           // FileChanged
old_cwd, new_cwd            // CwdChanged

输出:纯文本或 JSON

如果 stdout 以 { 开头,则解析为 JSON。否则在转录中显示为纯文本。

{
  "continue": true,
  "suppressOutput": false,
  "stopReason": "string",
  "decision": "approve|block",
  "reason": "string",
  "systemMessage": "shown to user",
  "hookSpecificOutput": { }
}

各事件的 hookSpecificOutput

PreToolUse
permissionDecision: "allow|deny|ask"
permissionDecisionReason: "string"
updatedInput: { }     // modify the tool input
additionalContext: "" // injected as context
PostToolUse
additionalContext: ""
updatedMCPToolOutput: { }  // replace MCP output
SessionStart
additionalContext: ""
initialUserMessage: ""  // auto-inject message
watchPaths: ["/path/"]  // set up FileChanged watchers
PermissionRequest
decision: {
  behavior: "allow|deny",
  updatedInput: { },
  updatedPermissions: [
    { tool: "Bash(npm*)", behavior: "allow" }
  ]
}

退出码

含义 行为
0 成功 Stdout 在转录中显示或解析为 JSON
2 阻塞错误 阻止操作。Stderr 作为反馈发送给模型。
其他 非阻塞错误 仅显示给用户,不阻止操作
事件 退出码 2 行为
PreToolUse 阻止工具调用。Stderr 显示给模型。
PostToolUse 立即将 stderr 作为反馈显示给模型。
UserPromptSubmit 阻止处理,清空提示词,向用户显示 stderr。
Stop 将 stderr 显示给模型并继续对话。
SessionStart 阻塞错误被忽略 — 会话仍会启动。

匹配和 if 过滤器

matcher 字段(3 种模式)

Mode Example
Exact "Write"
Pipe-separated "Write|Edit"
Regex "^Write.*" or ".*"

无 matcher = 对该事件的所有实例触发。根据事件匹配 tool_namesourceagent_type 等。

if 条件 — 次级过滤器

使用权限规则语法。在生成 hook 进程之前评估。避免不必要的进程创建。

// Only git commands
"if": "Bash(git *)"

// Only npm publish
"if": "Bash(npm publish:*)"

// Only TypeScript writes
"if": "Write(*.ts)"

// Only edits in api/ directory
"if": "Edit(src/api/*)"

权限集成

PreToolUse 权限优先级

Hook says "allow"
  + deny rule exists  → DENY (rule wins)
  + ask rule exists   → FORCE prompt dialog
  + no rules          → ALLOW ✓

Hook says "deny"      → DENY immediately

Hook says "ask"       → Force permission dialog

Multiple hooks: deny > ask > allow

Hook 的 allow 不会绕过 settings.json 中的 deny/ask 规则。规则始终优先。

PermissionRequest — CI 中自动批准

在权限对话框显示前触发。Hooks 可以无需人工干预自动批准或拒绝。

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedPermissions": [
        { "tool": "Bash(npm test:*)",
          "behavior": "allow" }
      ]
    }
  }
}

异步 Hooks

配置式异步

{
  "type": "command",
  "command": "slow-task.sh",
  "async": true
}

Hook 在后台运行。Claude 继续执行而不等待。

运行时异步声明

Hook 通过其首行 stdout 在运行时声明自己为异步:

{"async": true, "asyncTimeout": 30}

asyncRewake

{
  "type": "command",
  "command": "check-ci.sh",
  "async": true,
  "asyncRewake": true
}

当此异步 hook 以退出码 2 退出时,通过 enqueuePendingNotification() 唤醒模型。启用后台监控模式。

环境变量和超时

可用于 command hooks

CLAUDE_PROJECT_DIR 根项目目录(稳定,非 worktree 路径)
CLAUDE_PLUGIN_ROOT Plugin/skill 根目录
CLAUDE_PLUGIN_DATA Plugin 数据目录
CLAUDE_PLUGIN_OPTION_* Plugin 选项值(大写)
CLAUDE_ENV_FILE BashTool 环境变量的 .sh 文件路径。仅限 SessionStart、Setup、CwdChanged、FileChanged。
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS 覆盖 SessionEnd 超时(默认:1500ms)

默认超时

常规(command / http) 10 分钟
SessionEnd hooks 1.5 秒
Prompt hooks 30 秒
Agent hooks 60 秒
异步 hooks(asyncTimeout) 15 秒
Function hooks 5 秒

Hook 配置中的 timeout 字段单位为

安全策略

allowManagedHooksOnly — 仅执行 policy hooks
disableAllHooks — 在 policySettings 中:禁用所有 hooks;在其他来源中:仅禁用非托管 hooks

交互式提示词获取

Command hooks 可以通过 stdout/stdin 协议请求用户输入 — 实现无需单独 UI 的交互式多步 hooks。

Hook 写入 stdout:

{
  "prompt": "request-id-123",
  "message": "Choose deployment target:",
  "options": [
    { "key": "staging",    "label": "Staging" },
    { "key": "production", "label": "Production" }
  ]
}

Claude Code 写回 hook 的 stdin:

{
  "prompt_response": "request-id-123",
  "selected": "staging"
}

实用示例

文件编辑后自动格式化

// PostToolUse, matcher: "Write|Edit"
{
  "type": "command",
  "command": "prettier --write "$(cat | jq -r '.tool_input.file_path')"",
  "timeout": 10,
  "statusMessage": "Formatting..."
}

阻止危险的 git 命令

// PreToolUse, matcher: "Bash", if: "Bash(git *)"
{
  "type": "command",
  "command": "CMD=$(cat|jq -r '.tool_input.command'); if echo "$CMD" | grep -qE 'force|reset --hard'; then echo 'Blocked' >&2; exit 2; fi"
}

CI 中自动批准权限

// PermissionRequest, matcher: "Bash"
{
  "type": "command",
  "command": "echo '{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"allow"}}}'"
}

后台 CI 监控(asyncRewake)

// Stop event
{
  "type": "command",
  "command": "check-ci-status.sh",
  "async": true,
  "asyncRewake": true
}
// CI 失败时,脚本退出码 2 → 唤醒模型
! Hooks 以完整用户权限运行
Command hooks 作为用户的 shell 进程执行,无沙箱隔离。项目 .claude/settings.json 中的恶意 hook 可能执行任意代码。 在接受工作区信任前务必验证项目级 hooks,并在企业环境中使用 allowManagedHooksOnly 将 hooks 限制为仅策略控制的来源。