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 — 替换为完整输入 JSONmodel — 默认为小型快速模型timeout — 默认:30 秒agent — 多轮验证
{
"type": "agent",
"prompt": "Verify tests still pass: $ARGUMENTS",
"timeout": 60,
"model": "claude-sonnet-4-6"
} 可访问工具,最多 50 轮
model — 默认为 Haikutimeout — 默认: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_name、source、agent_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 hooksdisableAllHooks — 在 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 限制为仅策略控制的来源。