Claude Code Hooks 是用户定义的 Shell 命令、HTTP 端点或 LLM Prompt,在 Claude Code 生命周期的特定时机自动执行。与 CLAUDE.md 中的建议性指令不同,Hooks 是确定性的——你说执行什么就执行什么。
核心概念:Hook 生命周期
每个 Hook 在特定时机触发。当事件触发且 Matcher 匹配时,Claude Code 将事件的 JSON 上下文传给 Hook 处理器,处理器可以检查输入、采取行动,并可选地返回决策。
完整事件列表
| 事件 | 触发时机 |
|---|---|
SessionStart | 会话开始或恢复时 |
UserPromptSubmit | 提交 Prompt 后,Claude 处理前 |
PreToolUse | 工具调用执行前(可阻止) |
PermissionRequest | 权限对话框出现时 |
PostToolUse | 工具调用成功后 |
PostToolUseFailure | 工具调用失败后 |
Notification | Claude Code 发送通知时 |
SubagentStart | Subagent 被启动时 |
SubagentStop | Subagent 完成时 |
Stop | Claude 完成响应时 |
TeammateIdle | Agent Team 成员即将空闲时 |
TaskCompleted | 任务被标记为完成时 |
ConfigChange | 会话中配置文件变更时 |
WorktreeCreate | 通过 --worktree 创建 Worktree 时 |
WorktreeRemove | Worktree 被移除时 |
PreCompact | 上下文压缩前 |
SessionEnd | 会话终止时 |
配置结构
Hooks 在 JSON 设置文件中定义,三层嵌套:
- Hook 事件:响应哪个生命周期点
- Matcher 组:过滤何时触发
- Hook 处理器:触发时运行什么
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block-rm.sh"
}
]
}
]
}
}Hook 存储位置与作用域
| 位置 | 作用域 | 可共享 |
|---|---|---|
~/.claude/settings.json | 你的所有项目 | 否,本机私有 |
.claude/settings.json | 单个项目 | 是,可提交到仓库 |
.claude/settings.local.json | 单个项目 | 否,gitignore |
| 托管策略设置 | 组织范围 | 是,管理员控制 |
Plugin 的 hooks/hooks.json | Plugin 启用时 | 是,随 Plugin 打包 |
Matcher 模式
matcher 字段是一个正则表达式字符串。不同事件类型匹配不同字段:
| 事件 | Matcher 过滤的内容 | 示例值 |
|---|---|---|
PreToolUse, PostToolUse | 工具名称 | Bash, Edit|Write, mcp__.* |
SessionStart | 会话启动方式 | startup, resume, clear |
SessionEnd | 会话结束原因 | clear, logout, other |
Notification | 通知类型 | permission_prompt, idle_prompt |
SubagentStart/Stop | Agent 类型 | Bash, Explore, Plan |
PreCompact | 压缩触发原因 | manual, auto |
UserPromptSubmit、Stop、TeammateIdle、TaskCompleted 不支持 Matcher,每次都触发。
实际示例:阻止危险命令
场景:阻止 Claude 执行 rm -rf 命令
json
// .claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/block-rm.sh"
}
]
}
]
}
}bash
#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "危险命令已被 Hook 阻止"
}
}'
else
exit 0 # 允许命令执行
fi工作流程:
PreToolUse事件触发- Matcher
Bash匹配工具名,运行脚本 - 脚本从 stdin 读取 JSON,发现
rm -rf,输出deny决策 - Claude Code 阻止工具调用,向 Claude 显示原因
MCP 工具的 Hook 匹配
MCP 工具遵循命名模式 mcp__<server>__<tool>:
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__memory__.*",
"hooks": [{ "type": "command", "command": "./log-memory-ops.sh" }]
},
{
"matcher": "mcp__.*__write.*",
"hooks": [{ "type": "command", "command": "./validate-writes.sh" }]
}
]
}
}文件编辑后自动运行 Lint
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "/path/to/lint-check.sh"
}
]
}
]
}
}异步 Hook(后台运行)
某些 Hook 不需要阻塞 Claude 等待结果,可以配置为异步运行:
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "./run-tests.sh",
"background": true
}
]
}
]
}
}HTTP Hook
将事件数据 POST 到外部 HTTP 端点:
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "http",
"url": "https://your-webhook.example.com/claude-stop",
"method": "POST"
}
]
}
]
}
}Prompt-Based Hook
使用 LLM Prompt 作为 Hook 处理器,实现复杂的多条件判断:
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "检查会话是否完成了所有要求的任务。如果没有,返回 continue 要求 Claude 继续工作。"
}
]
}
]
}
}安全注意事项
- 将 Hook 中的 Shell 命令视为代码,不要运行不受信任的 Hook
- 对第三方输入进行清理,避免命令注入
- 使用
settings.local.json(gitignore)存储包含敏感路径的 Hook - 用
allowManagedHooksOnly企业设置来阻止用户级 Hook
调试 Hook
bash
# 查看 Hook 运行日志
openclaw logs --follow | grep hook
# 或设置环境变量开启详细日志
CLAUDE_HOOKS_DEBUG=1 claude原文:Hooks reference - Claude Code Docs | 来源:Claude Code 官方文档