Files
claude-code/docs/extensibility/hooks.mdx
claude-code-best c5edee431f docs: 文档检查/check 20260419 (#296)
* docs: 修复文档巡检发现的 4 处错误

- daemon.md: 反映实际实现状态(supervisor/worker 已实现而非 stub)
- bridge-mode.md: API 操作数量从 7 修正为 9
- web-search-tool.md: 文件路径从 src/tools/ 修正为 packages/builtin-tools/src/tools/
- remote-control-self-hosting.md: 补充缺失的 RCS_WS_IDLE_TIMEOUT 和 RCS_WS_KEEPALIVE_INTERVAL 配置项

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 Safety 和 Context 文档中的代码引用和类型错误

- permission-model: 修正规则来源从"五层"到八层,优先级顺序对齐代码
- permission-model: PermissionUpdate 类型改为实际的 addRules/replaceRules 等
- permission-model: 补充 acceptEdits 和 dontAsk 两种权限模式
- permission-model: DENIAL_LIMITS 字段名对齐实际代码
- plan-mode: 工具路径从 src/tools/ 改为 packages/builtin-tools/src/tools/
- compaction: 修正 COMPACTABLE_TOOLS 和 POST_COMPACT_* 的行号
- project-memory: 修正 ENTRYPOINT_NAME 常量的行号
- system-prompt: 修正 SystemPrompt 类型定义文件路径和多个行号引用

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修复 introduction 文档中的错误路径和行号引用

- why-this-whitepaper.mdx: BashTool 路径从 src/tools/ 修正为 packages/builtin-tools/src/tools/
- what-is-claude-code.mdx: 移除不存在的 Azure provider,改为实际的 7 种 provider
- architecture-overview.mdx: State 类型行号从 204 修正为 207

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修复 conversation/features 文档中的错误

- streaming.mdx: queryStreamRaw → queryModelWithStreaming 函数名修正
- streaming.mdx: Azure 提供商不存在,替换为实际 7 个提供商
- debug-mode.mdx: --inspect-wait 描述错误,实际使用 BUN_INSPECT 环境变量
- buddy.mdx: 补充缺失的 companionReact.ts、CompanionCard.tsx、index.ts

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修复文档巡检中的源码引用错误

- feature-flags.mdx: 修正 feature() 兜底描述,实际从 bun:bundle 导入而非 cli.tsx:3 内联
- feature-flags.mdx: 修正工具 require 路径为 @claude-code-best/builtin-tools 包路径
- ant-only-world.mdx: 修正 tools.ts 中 require 路径为包路径
- ant-only-world.mdx: 修正 INTERNAL_ONLY_COMMANDS 行号 (267-295) 和数量 (24+)
- skills.mdx: 修正 COMMANDS memoize 行号 258 → 299
- mcp-protocol.mdx: 修正 fetchToolsForClient LRU 缓存上限 20 → 100
- streaming.mdx: 修正流式事件引用
- file-operations.mdx: 修正工具路径引用
- search-and-navigation.mdx: 修正搜索工具引用
- shell-execution.mdx: 修正 shell 工具引用
- buddy.mdx: 补充缺失的 frontmatter 字段
- debug-mode.mdx: 修正调试模式描述

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 tools/agent 文档中的文件路径和行号引用

- 修正 TodoWriteTool、AgentTool、ToolSearchTool 等工具路径
  src/tools/ → packages/builtin-tools/src/tools/
- 更新 Tool.ts、tools.ts、BashTool.tsx 中过时的行号引用
- 修正 WebSearchTool/WebFetchTool/EnterWorktreeTool/ExitWorktreeTool 路径
- 修正 AgentTool.tsx 中多行行号引用

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 feature 文档中的文件路径和行号引用

- ultraplan.md: 更新文件行数(525/349/127)
- fork-subagent.md: 路径迁移 src/tools/ → packages/builtin-tools/
- mcp-skills.md: 修正 getMcpSkillCommands 行号 547→604,client.ts 行号 117→129
- kairos.md: 修正 getBriefSection/getProactiveSection 行号
- proactive.md: 修正 getProactiveSection 行号 860→864

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正顶层文档中的路径迁移和行号引用

- auto-updater.md: config.ts 行号 1735→1737,标注未接入启动流程的函数
- external-dependencies.md: WebSearchTool/WebFetchTool 路径迁移到 builtin-tools 包,Vertex 行号修正
- lsp-integration.md: LSPTool 路径从 src/tools/ 迁移到 packages/builtin-tools/
- stub-recovery-design-1-4.md: 修正 Windows 绝对路径链接为标准代码引用

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 task 文档中的文件扩展名和路径引用

- task-004: AssistantSessionChooser.ts → .tsx, assistant.ts → .tsx
- task-003: cli.tsx 行号 249→272, markdownConfigLoader.ts 行号 29→35
- lan-pipes: SendMessageTool 路径迁移到 packages/builtin-tools/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 补充 computer-use-tools-reference 缺失的 Windows 工具

添加遗漏的 open_terminal 和 activate_window 两个 Windows 专属工具,
修正工具总数 37→39,Windows 工具数 10→12。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 audit/bash-classifier/token-budget/tree-sitter 文档

- feature-flags-audit: ScheduleCronTool 路径迁移、DAEMON 状态更新为 COMPLETE、assistant 文件标记已补全、UDS 标记已实现
- bash-classifier: BashPermissionRequest 文件路径修正、withRetry 行号移除
- token-budget: attachments.ts 行号范围修正
- tree-sitter-bash: bashPermissions.ts 路径迁移到 packages/builtin-tools

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 langfuse-monitoring AgentTool 路径迁移

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 bridgeApi 行号和 Tool.ts 行号引用

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 Safety/Extensibility 文档中的工具路径迁移和行号引用

- sandbox.mdx: shouldUseSandbox.ts 和 bashPermissions.ts 路径迁移至 packages/builtin-tools
- why-safety-matters.mdx: bashPermissions.ts 路径迁移(3 处)
- plan-mode.mdx: EnterPlanModeTool/prompt.ts 路径迁移
- auto-mode.mdx: Auto mode 指令行号 3464→3481
- hooks.mdx: AgentTool/runAgent.ts 路径迁移
- skills.mdx: SkillTool.ts 路径迁移
- custom-agents.mdx: Agent built-in 目录和 exploreAgent.ts 路径迁移

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 internals 文档引用计数和路径

- ant-only-world: USER_TYPE 引用计数 465→410+,工具路径迁移到 builtin-tools
- growthbook-ab-testing: growthbook.ts 行数 1156→1258
- hidden-features: 语音模式状态更新(audio-napi 已恢复)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正工具文档中的行号引用

- sub-agents: AgentTool.call 入口行号 340→387
- shell-execution: ShellCommand onTimeout 行号 129→144

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 feature 文档中的状态、路径和计数

- all-features-guide: 修正 feature flag 启用范围(dev only vs dev+build)
- tier3-stubs: 大量状态修正(stub→已实现),缩减过时条目
- workflow-scripts: 路径迁移到 builtin-tools,状态更新
- web-browser-tool: 工具状态缺失→已实现,路径迁移
- context-collapse: CtxInspectTool 状态缺失→已实现
- computer-use: 行号引用更新,平台分发描述修正
- computer-use-tools-reference: 工具数 39→38
- voice-mode: voiceModeEnabled 行数 55→54

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 更新 the-loop 查询循环行号引用

query.ts 代码变更后终止原因行号整体偏移约 40 行

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 补充 feature-flags-audit 完整 build 默认 feature 列表

添加 ULTRATHINK/LODESTONE/ACP/DAEMON 等 19 个缺失的 build 默认 feature,
修正 dev-only 特征标注(UDS_INBOX/LAN_PIPES/BG_SESSIONS/TEMPLATES)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 feature-flags-audit ConfigTool 路径迁移

ConfigTool 路径从 src/tools/ 迁移到 packages/builtin-tools/src/tools/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 feature-flags-audit BashTool 路径迁移

BashTool 路径从 src/tools/ 迁移到 packages/builtin-tools/src/tools/

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 修正 feature-flags-audit SkillTool 路径迁移

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: 更新 feature-flags-audit WorkflowTool 状态为已实现

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-19 09:30:00 +08:00

254 lines
11 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: "Hooks 生命周期钩子 - 执行引擎与拦截协议"
description: "从源码角度解析 Claude Code Hooks 系统27 种 Hook 事件、6 种 Hook 类型、同步/异步执行协议、JSON 输出 schema、if 条件匹配、以及 Hook 如何注入上下文和拦截工具调用。"
keywords: ["Hooks", "生命周期钩子", "拦截器", "PreToolUse", "Hook 协议"]
---
{/* 本章目标:从源码角度揭示 Hook 的执行引擎、匹配机制、返回值协议和生命周期管理 */}
## 27 种 Hook 事件
Claude Code 定义了 27 种 Hook 事件(`HOOK_EVENTS` 数组,`src/entrypoints/sdk/coreTypes.ts`),覆盖完整的 Agent 生命周期:
| 阶段 | 事件 | 触发时机 | 匹配字段 |
|------|------|---------|---------|
| **会话** | `SessionStart` | 会话启动 | `source` |
| | `SessionEnd` | 会话结束 | `reason` |
| | `Setup` | 初始化完成 | `trigger` |
| **用户交互** | `UserPromptSubmit` | 用户提交消息 | — |
| | `Stop` | Agent 停止响应 | — |
| | `StopFailure` | Agent 停止失败 | `error` |
| **工具执行** | `PreToolUse` | 工具调用前 | `tool_name` |
| | `PostToolUse` | 工具调用后(成功) | `tool_name` |
| | `PostToolUseFailure` | 工具调用后(失败) | `tool_name` |
| **权限** | `PermissionRequest` | 权限请求 | `tool_name` |
| | `PermissionDenied` | 权限被拒 | `tool_name` |
| **子 Agent** | `SubagentStart` | 子 Agent 启动 | `agent_type` |
| | `SubagentStop` | 子 Agent 停止 | `agent_type` |
| **压缩** | `PreCompact` | 上下文压缩前 | `trigger` |
| | `PostCompact` | 上下文压缩后 | `trigger` |
| **协作** | `TeammateIdle` | Teammate 空闲 | — |
| | `TaskCreated` | 任务创建 | — |
| | `TaskCompleted` | 任务完成 | — |
| **MCP** | `Elicitation` | MCP 服务器请求用户输入 | `mcp_server_name` |
| | `ElicitationResult` | Elicitation 结果返回 | `mcp_server_name` |
| **通知** | `Notification` | 系统通知事件 | `notification_type` |
| **环境** | `ConfigChange` | 配置变更 | `source` |
| | `CwdChanged` | 工作目录变更 | — |
| | `FileChanged` | 文件变更 | `file_path` |
| | `InstructionsLoaded` | 指令加载 | `load_reason` |
| | `WorktreeCreate` / `WorktreeRemove` | Worktree 操作 | — |
## 6 种 Hook 类型
Hooks 配置支持 6 种执行方式,类型定义分布在 3 个文件中:
- **可持久化类型**`command`、`prompt`、`agent`、`http`)— Zod schema 定义在 `src/schemas/hooks.ts`,通过 `z.discriminatedUnion('type', [...])` 声明
- **callback 类型** — TypeScript 接口定义在 `src/types/hooks.ts`,用于 SDK 注册的内部 JS 函数
- **function 类型** — 定义在 `src/utils/hooks/sessionHooks.ts`,用于运行时动态注册的函数 Hook
| 类型 | 执行方式 | 适用场景 |
|------|---------|---------|
| `command` | Shell 命令bash/PowerShell | 通用脚本、CI 检查 |
| `prompt` | 注入到 AI 上下文 | 代码规范提醒 |
| `agent` | 启动子 Agent 执行 | 复杂分析任务 |
| `http` | HTTP 请求 | 远程服务、Webhook |
| `callback` | 内部 JS 函数 | 系统内置 Hook |
| `function` | 运行时注册的函数 Hook | Agent/Skill 内部使用 |
## 执行引擎execCommandHook
`execCommandHook()``src/utils/hooks.ts``execCommandHook` 函数)是命令型 Hook 的执行核心:
```
execCommandHook(hook, hookEvent, hookName, jsonInput, signal)
├── Shell 选择: hook.shell ?? DEFAULT_HOOK_SHELL
│ ├── bash: spawn(cmd, [], { shell: gitBashPath | true })
│ └── powershell: spawn(pwsh, ['-NoProfile', '-NonInteractive', '-Command', cmd])
├── 变量替换
│ ├── ${CLAUDE_PLUGIN_ROOT} → pluginRoot 路径
│ ├── ${CLAUDE_PLUGIN_DATA} → plugin 数据目录
│ └── ${user_config.X} → 用户配置值
├── 环境变量注入
│ ├── CLAUDE_PROJECT_DIR
│ ├── CLAUDE_ENV_FILESessionStart/Setup/CwdChanged/FileChanged
│ └── CLAUDE_PLUGIN_OPTION_*plugin options
├── stdin 写入: jsonInput + '\n'
├── 超时: hook.timeout * 1000 ?? 600000ms10分钟
└── 异步检测: 检查 stdout 首行是否为 {"async":true}
```
### 异步 Hook 的检测协议
Hook 进程的 stdout 第一行如果是 `{"async":true}`,系统将其转为后台任务(`isAsyncHookJSONOutput` 检测 + `executeInBackground` 调用):
```typescript
const firstLine = firstLineOf(stdout).trim()
if (isAsyncHookJSONOutput(parsed)) {
executeInBackground({
processId: `async_hook_${child.pid}`,
asyncResponse: parsed,
...
})
}
```
后台 Hook 通过 `registerPendingAsyncHook()` 注册到 `AsyncHookRegistry`,完成后通过 `enqueuePendingNotification()` 通知主线程。
### asyncRewakeHook 唤醒模型
`asyncRewake` 模式的 Hook 绕过 `AsyncHookRegistry`。当 Hook 退出码为 2 时,通过 `enqueuePendingNotification()` 以 `task-notification` 模式注入消息,唤醒空闲的模型(通过 `useQueueProcessor`)或在忙碌时注入 `queued_command` 附件。
## Hook 输出的 JSON Schema
同步 Hook 的输出遵循严格的 Zod schema`syncHookResponseSchema`,定义在 `src/types/hooks.ts``hookJSONOutputSchema` 定义在 `src/schemas/hooks.ts`
```json
{
"continue": false, // 是否继续执行
"suppressOutput": true, // 隐藏 stdout
"stopReason": "安全检查失败", // continue=false 时的原因
"decision": "approve" | "block", // 全局决策
"reason": "原因说明", // 决策原因
"systemMessage": "警告内容", // 注入到上下文的系统消息
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow" | "deny" | "ask",
"permissionDecisionReason": "匹配了安全规则",
"updatedInput": { ... }, // 修改后的工具输入
"additionalContext": "额外上下文" // 注入到对话
}
}
```
### 各事件的 hookSpecificOutput
| 事件 | 专有字段 | 作用 |
|------|---------|------|
| `PreToolUse` | `permissionDecision`, `permissionDecisionReason`, `updatedInput`, `additionalContext` | 拦截/修改工具输入 |
| `PostToolUse` | `additionalContext`, `updatedMCPToolOutput` | 修改 MCP 工具输出 |
| `PostToolUseFailure` | `additionalContext` | 失败后注入上下文 |
| `UserPromptSubmit` | `additionalContext` | 注入额外上下文 |
| `SessionStart` | `additionalContext`, `initialUserMessage`, `watchPaths` | 设置初始消息和文件监控 |
| `PermissionRequest` | `decision`(含 `allow`/`deny` 子字段) | 权限请求的 Hook 决策 |
| `PermissionDenied` | `retry` | 指示是否重试 |
| `SubagentStart` | `additionalContext` | 子 Agent 启动时注入上下文 |
| `Elicitation` | `action`, `content` | 控制用户输入对话框 |
| `ElicitationResult` | `action`, `content` | Elicitation 结果处理 |
| `Notification` | `additionalContext` | 通知事件注入上下文 |
| `Setup` | `additionalContext` | 初始化时注入上下文 |
| `CwdChanged` | `watchPaths` | 目录变更后更新监控路径 |
| `FileChanged` | `watchPaths` | 文件变更后更新监控路径 |
| `WorktreeCreate` | `worktreePath` | Worktree 创建通知 |
## Hook 匹配机制getMatchingHooks
`getMatchingHooks()``src/utils/hooks.ts``getMatchingHooks` 函数)负责从所有来源中查找匹配的 Hook
### 多来源合并
```
getHooksConfig()
├── getHooksConfigFromSnapshot() ← settings.json 中的 Hookuser/project/local
├── getRegisteredHooks() ← SDK 注册的 callback Hook
├── getSessionHooks() ← Agent/Skill 前置注册的 session Hook
└── getSessionFunctionHooks() ← 运行时 function Hook
```
### 匹配规则
`matcher` 字段支持三种模式(`matchesPattern()` 函数,`src/utils/hooks.ts`
```
"Write" → 精确匹配
"Write|Edit" → 管道分隔的多值匹配
"^Bash(git.*)" → 正则匹配
"*" 或 "" → 通配(匹配所有)
```
### if 条件过滤
Hook 可以指定 `if` 条件,只在特定输入时触发。`prepareIfConditionMatcher()``src/utils/hooks.ts``prepareIfConditionMatcher` 函数)预编译匹配器:
```json
{
"hooks": [{
"command": "check-git-branch.sh",
"if": "Bash(git push*)"
}]
}
```
`if` 条件使用 `permissionRuleValueFromString` 解析,支持与权限规则相同的语法(工具名 + 参数模式。Bash 工具还会使用 tree-sitter 进行 AST 级别的命令解析。
### Hook 去重
同一个 Hook 命令在不同配置层级user/project/local可能重复。系统按四部分复合键做 Map 去重:`${pluginRoot}\0${shell}\0${command}\0${ifCondition}`(由 `hookDedupKey()` 函数构建),保留**最后合并的层级**。
## 工作区信任检查
**所有 Hook 都要求工作区信任**`shouldSkipHookDueToTrust()` 函数,`src/utils/hooks.ts`)。这是纵深防御措施——防止恶意仓库的 `.claude/settings.json` 在未信任的情况下执行任意命令。
```typescript
// 交互模式下,所有 Hook 要求信任
const hasTrust = checkHasTrustDialogAccepted()
return !hasTrust
```
SDK 非交互模式下信任是隐式的(`getIsNonInteractiveSession()` 为 true 时跳过检查)。
## 四种 Hook 能力的源码映射
### 1. 拦截操作PreToolUse
```json
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny"
}
}
```
`processHookJSONOutput()` 将 `permissionDecision` 映射为 `result.permissionBehavior = 'deny'`,并设置 `blockingError`,阻止工具执行。
### 2. 修改行为updatedInput / updatedMCPToolOutput
```json
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"updatedInput": { "command": "npm test -- --bail" }
}
}
```
`updatedInput` 替换原始工具输入;`updatedMCPToolOutput`PostToolUse 事件)替换 MCP 工具的返回值——可用于过滤敏感数据。
### 3. 注入上下文additionalContext / systemMessage
- `additionalContext` → 通过 `createAttachmentMessage({ type: 'hook_additional_context' })` 注入为用户消息
- `systemMessage` → 注入为系统警告,直接显示给用户
### 4. 控制流程continue / stopReason
```json
{ "continue": false, "stopReason": "构建失败,停止执行" }
```
`continue: false` 设置 `preventContinuation = true`,阻止 Agent 继续执行后续操作。
## Session Hook 的生命周期
Agent 和 Skill 的前置 Hook 通过 `registerFrontmatterHooks()` 注册(调用位置:`packages/builtin-tools/src/tools/AgentTool/runAgent.ts`;定义位置:`src/utils/hooks/registerFrontmatterHooks.ts`),绑定到 agent 的 session ID。Agent 结束时通过 `clearSessionHooks()`(定义位置:`src/utils/hooks/sessionHooks.ts`)清理。
```typescript
// runAgent.ts — 注册 agent 的前置 Hook
registerFrontmatterHooks(rootSetAppState, agentId, agentDefinition.hooks, ...)
// runAgent.ts — finally 块清理
clearSessionHooks(rootSetAppState, agentId)
```
这确保 Agent A 的 Hook 不会泄漏到 Agent B 的执行中。