第三部分:15种Hook类型详解
本节目的:掌握所有Hook类型的用法
⏱️ 预计时间:1.5-2小时
3.1 PreToolUse(工具调用前)
一句话理解:PreToolUse就像"安检门",在工具执行前检查是否允许通过。
触发时机
在Claude准备调用工具(如Write、Edit、Bash)时,但尚未执行。
输入参数(通过stdin的JSON)
{
"tool_name": "Write",
"tool_input": {
"file_path": "C:/project/src/app.js",
"content": "console.log('Hello World');"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
tool_name | string | 工具名称(Write, Edit, Bash, Read等) |
tool_input | object | 工具的输入参数 |
决策输出(通过stdout的JSON)
PreToolUse Hook可以返回决策指令控制工具是否执行。
新旧API并存:v2.1.49+ 推荐使用新版
hookSpecificOutput.permissionDecision字段,旧版decision字段仍然支持但已废弃。
新版API(推荐) — 通过 hookSpecificOutput.permissionDecision 字段:
{
"hookSpecificOutput": {
"permissionDecision": "deny"
},
"message": "禁止修改production目录下的文件"
}旧版API(已废弃但仍支持) — 通过 decision 字段:
{
"decision": "deny",
"message": "禁止修改production目录下的文件"
}新版决策值(hookSpecificOutput.permissionDecision):
| 决策值 | 说明 | 工具是否执行 |
|---|---|---|
"allow" | 允许执行,绕过权限系统 | ✅ 是 |
"deny" | 拒绝执行,原因会反馈给Claude | ❌ 否 |
"ask" | 暂停,询问用户决定 | 🤔 等待用户决定 |
| 无输出 | 默认允许 | ✅ 是 |
旧版决策值(decision,已废弃):
| 旧版值 | 等同于新版 | 说明 |
|---|---|---|
"approve" | "allow" | 允许执行 |
"block" | "deny" | 拒绝执行 |
PostToolUse 和 UserPromptSubmit 的决策值:这两个Hook类型使用
"block"来阻止/提供反馈,无输出则默认允许。
完整示例1:文件保护Hook
场景:禁止修改production/目录下的文件
脚本 .claude/hooks/pre-protect-production.py:
#!/usr/bin/env python3
"""
PreToolUse Hook - 保护production目录
禁止Write/Edit工具修改production目录下的文件
"""
import sys
import json
# 读取stdin的JSON输入
try:
input_data = json.loads(sys.stdin.read())
except json.JSONDecodeError:
sys.exit(0)
tool_name = input_data.get('tool_name', '')
tool_input = input_data.get('tool_input', {})
file_path = tool_input.get('file_path', '')
# 只检查Write和Edit工具
if tool_name not in ['Write', 'Edit']:
sys.exit(0)
# 规范化路径(处理Windows和Unix路径)
file_path_normalized = file_path.replace('\\', '/')
# 检查是否是保护目录
protected_dirs = ['production/', 'prod/', '.env']
for protected in protected_dirs:
if protected in file_path_normalized:
# 拒绝执行
decision = {
"decision": "deny",
"message": f"❌ 禁止修改受保护的路径!\n路径: {file_path}\n原因: 包含受保护目录 '{protected}'\n\n请先切换到dev环境或手动操作。"
}
print(json.dumps(decision, ensure_ascii=False))
sys.exit(0)
# 允许执行(无输出=默认allow)
sys.exit(0)配置 .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/pre-protect-production.py",
"timeout": 5
}
]
}
]
}
}运行效果:
当Claude尝试修改production/config.json时:
Claude:我来修改production/config.json...
❌ 禁止修改受保护的路径!
路径: C:/project/production/config.json
原因: 包含受保护目录 'production/'
请先切换到dev环境或手动操作。完整示例2:危险命令拦截Hook
场景:拦截危险的Bash命令(如rm -rf)
脚本 .claude/hooks/pre-block-dangerous-cmd.py:
#!/usr/bin/env python3
"""
PreToolUse Hook - 拦截危险命令
阻止执行可能造成破坏的Shell命令
"""
import sys
import json
import re
# 危险命令模式
DANGEROUS_PATTERNS = [
r'rm\s+-rf\s+/', # rm -rf /
r'rm\s+-rf\s+~', # rm -rf ~
r'rm\s+-rf\s+\*', # rm -rf *
r'rm\s+-rf\s+\.\.', # rm -rf ..
r':()\s*{\s*:\|:&\s*};:', # Fork炸弹
r'mkfs\.', # 格式化磁盘
r'dd\s+if=.+of=/dev/', # 覆盖磁盘
r'>\s*/dev/sda', # 覆盖磁盘
r'chmod\s+-R\s+777\s+/', # 危险权限
]
# 读取输入
try:
input_data = json.loads(sys.stdin.read())
except json.JSONDecodeError:
sys.exit(0)
tool_name = input_data.get('tool_name', '')
tool_input = input_data.get('tool_input', {})
command = tool_input.get('command', '')
# 只检查Bash工具
if tool_name != 'Bash':
sys.exit(0)
# 检查危险模式
for pattern in DANGEROUS_PATTERNS:
if re.search(pattern, command, re.IGNORECASE):
decision = {
"decision": "deny",
"message": f"🚨 危险命令已拦截!\n\n命令: {command}\n\n匹配的危险模式: {pattern}\n\n如果确实需要执行,请在终端手动运行。"
}
print(json.dumps(decision, ensure_ascii=False))
sys.exit(0)
sys.exit(0)配置:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/pre-block-dangerous-cmd.py",
"timeout": 5
}
]
}
]
}
}3.2 PostToolUse(工具调用后)
一句话理解:PostToolUse就像"快递签收通知",在工具成功执行后自动触发后续操作。
触发时机
在工具成功执行后立即触发,可以处理工具的输出结果。
输入参数(通过stdin的JSON)
{
"tool_name": "Write",
"tool_input": {
"file_path": "C:/project/src/app.js",
"content": "console.log('Hello');"
},
"tool_output": {
"success": true,
"message": "File written successfully"
}
}| 字段 | 类型 | 说明 |
|---|---|---|
tool_name | string | 工具名称 |
tool_input | object | 工具的输入参数 |
tool_output | object | 工具的输出结果 |
输出格式
⚠️ 重要:PostToolUse Hook的输出机制
v2.1.133 前:PostToolUse Hook 无法向用户输出信息,只能做后处理(格式化、备份、日志)。
v2.1.133 起:PostToolUse Hook 可以通过
hookSpecificOutput.updatedToolOutput替换工具的实际输出内容,适用于所有工具类型。
- ✅ 可以做:执行后处理任务(格式化、备份、测试、写日志文件)
- ✅ v2.1.133+ 可以做:通过
hookSpecificOutput.updatedToolOutput替换工具输出- ❌ 常见误区:
print(..., file=sys.stderr)不会显示在界面,只在终端可见如果需要向用户注入额外上下文信息,也可以使用 UserPromptSubmit Hook 的
additionalContext机制。
PostToolUse Hook不返回决策(工具已经执行完了),可以:
- 执行后处理任务(格式化、备份、测试)
- 写入日志文件(用于调试)
- v2.1.133+:通过
hookSpecificOutput.updatedToolOutput替换工具输出
完整示例1:自动代码格式化
场景:在Write工具保存.js/.ts文件后,自动运行Prettier格式化
脚本 .claude/hooks/post-auto-format.py:
#!/usr/bin/env python3
"""
PostToolUse Hook - 自动代码格式化
保存代码文件后自动运行对应的格式化工具
"""
import sys
import json
import subprocess
from pathlib import Path
# 格式化工具配置
FORMATTERS = {
'.js': 'npx prettier --write "{file}"',
'.ts': 'npx prettier --write "{file}"',
'.jsx': 'npx prettier --write "{file}"',
'.tsx': 'npx prettier --write "{file}"',
'.json': 'npx prettier --write "{file}"',
'.css': 'npx prettier --write "{file}"',
'.py': 'black "{file}"',
'.go': 'gofmt -w "{file}"',
}
# 排除的目录
EXCLUDED_DIRS = {'node_modules', 'venv', '.venv', '__pycache__', 'dist', 'build', '.git'}
def should_format(file_path: str) -> bool:
"""检查是否应该格式化该文件"""
path = Path(file_path)
# 检查是否在排除目录中
for part in path.parts:
if part in EXCLUDED_DIRS:
return False
# 检查文件扩展名
return path.suffix in FORMATTERS
def run_formatter(file_path: str) -> str:
"""运行格式化工具"""
path = Path(file_path)
suffix = path.suffix
if suffix not in FORMATTERS:
return None
cmd = FORMATTERS[suffix].format(file=file_path)
try:
result = subprocess.run(
cmd,
shell=True,
capture_output=True,
text=True,
timeout=30
)
if result.returncode == 0:
return f"✅ 格式化成功"
else:
return f"⚠️ 格式化失败: {result.stderr[:100]}"
except subprocess.TimeoutExpired:
return "⚠️ 格式化超时"
except FileNotFoundError:
return "⚠️ 格式化工具未安装"
except Exception as e:
return f"⚠️ 格式化错误: {str(e)}"
def main():
# 读取输入
try:
input_data = json.loads(sys.stdin.read())
except json.JSONDecodeError:
return
tool_name = input_data.get('tool_name', '')
tool_input = input_data.get('tool_input', {})
file_path = tool_input.get('file_path', '')
# 只处理Write工具
if tool_name != 'Write':
return
# 检查是否需要格式化
if not file_path or not should_format(file_path):
return
# 运行格式化
result = run_formatter(file_path)
if result:
print(f"\n[AutoFormat] {Path(file_path).name}: {result}", file=sys.stderr)
if __name__ == '__main__':
main()配置:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/post-auto-format.py",
"timeout": 30
}
]
}
]
}
}运行效果:
Claude:我来创建app.js文件...
[Write工具执行成功]
[AutoFormat] app.js: ✅ 格式化成功完整示例2:自动备份Hook
场景:在Edit工具修改重要文件后,自动创建备份
脚本 .claude/hooks/post-auto-backup.py:
#!/usr/bin/env python3
"""
PostToolUse Hook - 自动备份
编辑重要文件后自动创建.bak备份
"""
import sys
import json
import shutil
from pathlib import Path
from datetime import datetime
# 需要备份的目录
BACKUP_DIRS = ['config', 'src', 'docs', '.claude']
def main():
try:
input_data = json.loads(sys.stdin.read())
except json.JSONDecodeError:
return
tool_name = input_data.get('tool_name', '')
tool_input = input_data.get('tool_input', {})
file_path = tool_input.get('file_path', '')
# 只处理Edit工具
if tool_name != 'Edit':
return
# 检查是否在需要备份的目录中
should_backup = any(dir_name in file_path for dir_name in BACKUP_DIRS)
if should_backup and Path(file_path).exists():
# 创建备份
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
backup_path = f"{file_path}.{timestamp}.bak"
try:
shutil.copy2(file_path, backup_path)
print(f"[Backup] ✅ 已创建备份: {Path(backup_path).name}", file=sys.stderr)
except Exception as e:
print(f"[Backup] ⚠️ 备份失败: {e}", file=sys.stderr)
if __name__ == '__main__':
main()3.3 UserPromptSubmit(用户提示词提交)
一句话理解:UserPromptSubmit就像"邮件发送前的自动补充",可以在用户输入发送给Claude之前自动增强或过滤。
触发时机
在用户提交提示词后,Claude处理之前。
输入参数(通过stdin的JSON)
⚠️ 重要:UserPromptSubmit的输入是JSON格式,不是纯文本!
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../session.jsonl",
"cwd": "/your/project/path",
"permission_mode": "default",
"hook_event_name": "UserPromptSubmit",
"prompt": "请帮我写一篇关于AI的文章"
}| 字段 | 类型 | 说明 |
|---|---|---|
session_id | string | 当前会话ID |
prompt | string | 用户输入的原始提示词 |
cwd | string | Claude Code的工作目录 |
permission_mode | string | 权限模式(default/plan/bypassPermissions等) |
hook_event_name | string | 事件类型标识 |
输出格式
Hook的stdout输出会被添加到AI上下文(作为系统消息注入)。
方式1:直接输出文本(会作为额外上下文添加)
## 写作要求
- 字数:1500字
- 风格:老金式接地气风格
- 包含实战案例方式2:输出JSON格式(更多控制)
{
"continue": true,
"suppressOutput": false
}完整示例:写作规范自动追加
场景:检测到写作任务时,自动追加写作规范
脚本 .claude/hooks/user-prompt-enhance.py:
#!/usr/bin/env python3
"""
UserPromptSubmit Hook - 提示词自动增强
检测到特定任务时自动追加相关规范
【重要】:输入是JSON格式,必须先解析!
"""
import sys
import json
# 从stdin读取JSON输入
try:
input_data = json.loads(sys.stdin.read())
except json.JSONDecodeError:
# JSON解析失败,直接退出
sys.exit(0)
# 获取用户输入的提示词
user_input = input_data.get('prompt', '').strip()
# 如果没有prompt字段,直接退出
if not user_input:
sys.exit(0)
# 过滤简单回复(不需要增强)
simple_responses = ['好的', '是的', '继续', 'ok', 'yes', 'no', '确认', '取消']
if user_input.lower() in simple_responses or len(user_input) < 5:
# 不需要增强,不输出任何内容
sys.exit(0)
# 过滤斜杠命令
if user_input.startswith('/'):
sys.exit(0)
# 检查是否是写作任务
writing_keywords = ['写', '文章', '生成', '创作', 'write', 'article', '内容']
is_writing_task = any(kw in user_input.lower() for kw in writing_keywords)
if is_writing_task:
# 输出额外上下文(会添加到AI上下文中)
enhancement = """
---
## 写作规范提醒(Hook自动注入)
1. **风格**:接地气、说人话,避免AI腔
2. **结构**:开头金句 -> 核心要点 -> 实战案例 -> 总结升华
3. **字数**:1500-2000字
4. **检查**:完成后运行 /pre-check 进行质量检查
---"""
print(enhancement)
print(f"[Hook] 已为写作任务注入规范", file=sys.stderr)
sys.exit(0)💡 注意:
- 输入是JSON,必须用
json.loads()解析- 用户原始输入在
prompt字段中- 通过stdout输出JSON格式的
additionalContext字段来注入上下文- stderr仅用于调试,不会显示在Claude Code界面
配置:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/user-prompt-enhance.py",
"timeout": 5
}
]
}
]
}
}运行效果:
用户输入:
帮我写一篇关于Claude Code的介绍文章Claude实际收到:
帮我写一篇关于Claude Code的介绍文章
---
## 写作规范提醒(自动追加)
1. **风格**:接地气、说人话,避免AI腔
2. **结构**:开头金句 -> 核心要点 -> 实战案例 -> 总结升华
3. **字数**:1500-2000字
4. **检查**:完成后运行 /pre-check 进行质量检查
---3.4 Notification(通知)
一句话理解:Notification Hook就像"消息推送处理器",在Claude发送通知时触发。
触发时机
当Claude Code需要请求用户权限或提示输入空闲超过60秒时。
输入参数(通过stdin的JSON)
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../session.jsonl",
"cwd": "/your/project/path",
"hook_event_name": "Notification",
"message": "Claude is waiting for your input"
}| 字段 | 类型 | 说明 |
|---|---|---|
session_id | string | 当前会话ID |
transcript_path | string | 会话记录文件路径 |
cwd | string | 工作目录 |
hook_event_name | string | 事件类型标识(固定为"Notification") |
message | string | 通知消息内容(Notification特有字段) |
⚠️ 注意:根据官方实现,Notification Hook没有
title字段,只有message字段。
完整示例:桌面通知Hook
场景:将Claude的通知转发到系统桌面通知
脚本 .claude/hooks/notification-desktop.py:
#!/usr/bin/env python3
"""
Notification Hook - 桌面通知
将Claude的通知转发到系统桌面通知
"""
import sys
import json
import subprocess
import platform
def send_notification(title: str, message: str):
"""发送系统桌面通知"""
system = platform.system()
try:
if system == 'Darwin': # macOS
subprocess.run([
'osascript', '-e',
f'display notification "{message}" with title "{title}"'
])
elif system == 'Linux':
subprocess.run(['notify-send', title, message])
elif system == 'Windows':
# Windows Toast通知(推荐方式)
# 需要先安装:Install-Module -Name BurntToast -Scope CurrentUser
try:
# 优先使用BurntToast(更可靠)
ps_cmd = f'New-BurntToastNotification -Text "{title}", "{message}"'
result = subprocess.run(['powershell', '-Command', ps_cmd], capture_output=True)
if result.returncode != 0:
raise Exception("BurntToast not available")
except:
# 回退方案:使用Windows原生Toast
ps_cmd = f'''
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
$template = [Windows.UI.Notifications.ToastTemplateType]::ToastText02
$xml = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent($template)
$text = $xml.GetElementsByTagName("text")
$text[0].AppendChild($xml.CreateTextNode("{title}")) | Out-Null
$text[1].AppendChild($xml.CreateTextNode("{message}")) | Out-Null
$toast = [Windows.UI.Notifications.ToastNotification]::new($xml)
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Claude Code").Show($toast)
'''
subprocess.run(['powershell', '-Command', ps_cmd], capture_output=True)
except Exception as e:
print(f"通知发送失败: {e}", file=sys.stderr)
def main():
try:
input_data = json.loads(sys.stdin.read())
except json.JSONDecodeError:
return
# 获取消息内容(注意:没有notification_type字段)
message = input_data.get('message', '')
session_id = input_data.get('session_id', '')
if not message:
return
# 构建通知标题
title = "Claude Code"
# 发送桌面通知
send_notification(title, message)
print(f"[Notification] 已发送桌面通知: {message[:50]}...", file=sys.stderr)
if __name__ == '__main__':
main()3.5 SessionStart(会话开始)
一句话理解:SessionStart就像"开机自启动程序",在Claude Code启动时自动执行初始化任务。
触发时机
Claude Code启动时触发。
用途
- 初始化环境
- 检查依赖
- 加载配置
完整示例:环境检查Hook
脚本 .claude/hooks/session-start-check.py:
#!/usr/bin/env python3
"""
SessionStart Hook - 环境检查
启动时检查必需的工具和依赖是否已安装
"""
import sys
import shutil
# 检查必需的工具
required_tools = {
'node': 'Node.js (npm install)',
'python': 'Python 3.x',
'git': 'Git版本控制',
}
# 可选但推荐的工具
optional_tools = {
'prettier': 'Prettier代码格式化 (npm install -g prettier)',
'black': 'Black Python格式化 (pip install black)',
}
missing_required = []
missing_optional = []
# 检查必需工具
for tool, desc in required_tools.items():
if not shutil.which(tool):
missing_required.append(f" X {tool}: {desc}")
# 检查可选工具
for tool, desc in optional_tools.items():
if not shutil.which(tool):
missing_optional.append(f" ! {tool}: {desc}")
# 输出检查结果
if missing_required or missing_optional:
print("\n" + "="*50, file=sys.stderr)
print("环境检查结果", file=sys.stderr)
print("="*50, file=sys.stderr)
if missing_required:
print("\nX 缺少必需工具(请安装):", file=sys.stderr)
for item in missing_required:
print(item, file=sys.stderr)
if missing_optional:
print("\n! 缺少可选工具(建议安装):", file=sys.stderr)
for item in missing_optional:
print(item, file=sys.stderr)
print("\n" + "="*50 + "\n", file=sys.stderr)
else:
print("V 环境检查通过,所有工具已就绪", file=sys.stderr)
sys.exit(0)配置:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/session-start-check.py",
"timeout": 10
}
]
}
]
}
}3.6 SessionEnd 和 Stop
SessionEnd(会话结束)
触发时机:Claude Code正常退出时
用途:
- 清理临时文件
- 保存会话状态
- 备份日志
示例脚本 .claude/hooks/session-end-cleanup.sh:
#!/bin/bash
# SessionEnd Hook - 清理临时文件
temp_dir="$HOME/.claude/temp"
if [ -d "$temp_dir" ]; then
rm -rf "$temp_dir"/*
echo "V 临时文件已清理" >&2
fi
exit 0Stop(AI停止响应)
触发时机:当Claude Code代理完成响应时
输入参数(通过stdin的JSON):
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../session.jsonl",
"cwd": "/your/project/path",
"hook_event_name": "Stop"
}⚠️ 注意:Stop Hook没有
reason字段,只有标准字段。
用途:
- 保存当前状态
- 记录会话信息
- 可以通过输出
\{"continue": false\}强制停止
示例脚本 .claude/hooks/stop-save-state.py:
#!/usr/bin/env python3
"""
Stop Hook - 保存状态
AI停止时保存当前会话状态
"""
import sys
import json
from datetime import datetime
from pathlib import Path
try:
input_data = json.loads(sys.stdin.read())
except json.JSONDecodeError:
input_data = {}
# 获取标准字段(注意:没有reason字段)
session_id = input_data.get('session_id', 'unknown')
cwd = input_data.get('cwd', str(Path.cwd()))
# 保存状态
state_dir = Path.home() / '.claude' / 'state'
state_dir.mkdir(parents=True, exist_ok=True)
state_file = state_dir / 'last-session-state.json'
state = {
"stopped_at": datetime.now().isoformat(),
"session_id": session_id,
"project_dir": cwd
}
with open(state_file, 'w', encoding='utf-8') as f:
json.dump(state, f, indent=2, ensure_ascii=False)
print(f"V 会话状态已保存到: {state_file}", file=sys.stderr)
sys.exit(0)3.7 WorktreeCreate 和 WorktreeRemove(工作树管理)🆕
v2.1.49+ 新增:这两个Hook类型配合 Claude Code 内置的 Git Worktree 功能使用,用于 Git Worktree(工作树)的生命周期管理。
什么是 Git Worktree?
💡 生活类比:Git Worktree 就像在同一个项目里开了多个"平行工作台"。你可以在工作台A修bug,同时在工作台B开发新功能,互不干扰。Claude Code 使用 Worktree 来实现并行任务隔离。
WorktreeCreate(工作树创建时触发)
触发时机:Claude Code 创建新的 Git Worktree 时自动触发
典型用途:
- 初始化工作树特定的环境配置
- 安装工作树所需的依赖
- 设置工作树专属的环境变量
- 记录工作树创建日志
配置示例:
{
"hooks": {
"WorktreeCreate": [
{
"command": "echo \"新工作树已创建: $(date)\" >> ~/.claude/worktree.log",
"timeout": 10000
}
]
}
}输入数据(通过 stdin 接收 JSON):
{
"worktree_path": "/path/to/worktree",
"branch": "feature/new-feature"
}WorktreeRemove(工作树删除时触发)
触发时机:Claude Code 删除 Git Worktree 时自动触发
典型用途:
- 清理工作树相关的临时文件
- 释放工作树占用的资源
- 记录工作树删除日志
- 归档工作树的工作成果
配置示例:
{
"hooks": {
"WorktreeRemove": [
{
"command": "echo \"工作树已删除: $(date)\" >> ~/.claude/worktree.log",
"timeout": 10000
}
]
}
}实战场景:工作树生命周期管理
{
"hooks": {
"WorktreeCreate": [
{
"command": ".claude/hooks/worktree-init.sh",
"timeout": 30000
}
],
"WorktreeRemove": [
{
"command": ".claude/hooks/worktree-cleanup.sh",
"timeout": 15000
}
]
}
}⚠️ 注意:WorktreeCreate 和 WorktreeRemove 都是不可阻止的Hook,它们只用于执行附加操作,不能阻止工作树的创建或删除。
与 --worktree 启动参数的关系
🔥 重要:2026年2月(v2.1.49),Claude Code 正式内置了 Git Worktree 支持,这是一个核心功能级别的更新,不仅仅是 Hook。
--worktree(-w)启动参数:
# 在独立工作树中启动 Claude Code
claude --worktree
# 或简写
claude -w工作原理:
你的项目仓库(主工作目录)
├── .git/ # 共享的 Git 历史
├── .claude/worktrees/ # 工作树存放目录(加到 .gitignore)
│ ├── worktree-abc123/ # Agent A 的独立工作目录
│ └── worktree-def456/ # Agent B 的独立工作目录
├── src/ # 主工作目录的文件
└── ...使用场景:
| 场景 | 说明 |
|---|---|
| 并行开发 | 终端1: claude -w 开发新功能 / 终端2: claude -w 修复 bug |
| 代码审查 | 主工作目录继续开发,工作树中运行审查 Agent |
| 实验性修改 | 在工作树中试验方案,不影响主目录 |
配置步骤:
# 1. 将工作树目录加到 .gitignore
echo ".claude/worktrees/" >> .gitignore
# 2. 启动第一个 Agent(在工作树中)
claude -w
# 3. 打开另一个终端,启动第二个 Agent(在另一个工作树中)
claude -wHook 的角色:
- Git 用户:直接使用
claude -w即可,Hook 是可选的增强(如自动安装依赖) - 非 Git 用户(SVN/Perforce/Mercurial):通过 WorktreeCreate/WorktreeRemove Hook 自定义工作树的创建和清理逻辑,替代默认的 Git 行为
3.8 SubagentStart 和 SubagentStop(子代理生命周期)🆕
v2.1.49+ 新增:这两个Hook类型用于监控和管理子代理(Subagent/Agent)的生命周期。
SubagentStart(子代理启动时触发)
触发时机:Claude Code 启动子代理(通过 Agent 工具;旧资料可能写作 Task)时自动触发
典型用途:
- 记录子代理启动日志
- 初始化子代理专属的环境配置
- 监控并发子代理数量
配置示例:
{
"hooks": {
"SubagentStart": [
{
"command": "echo \"子代理已启动: $(date)\" >> ~/.claude/subagent.log",
"timeout": 5000
}
]
}
}SubagentStop(子代理停止时触发)
触发时机:子代理完成任务或被终止时自动触发
典型用途:
- 收集子代理执行结果
- 清理子代理使用的临时资源
- 记录子代理执行耗时
3.9 PermissionRequest(权限请求)🆕
v2.1.49+ 新增:在权限请求时触发,可用于实现自动审批策略。
触发时机:Claude Code 请求执行需要用户授权的操作时
典型用途:
- 根据规则自动批准/拒绝权限请求
- 记录权限请求审计日志
- 实现基于项目的权限策略
配置示例:
{
"hooks": {
"PermissionRequest": [
{
"command": "python .claude/hooks/permission-policy.py",
"timeout": 5000
}
]
}
}3.10 PreCompact(上下文压缩前)🆕
v2.1.49+ 新增:在 Claude Code 执行上下文压缩(Compact)前触发。
触发时机:当对话上下文即将被压缩时
典型用途:
- 在压缩前保存关键上下文信息
- 记录压缩事件日志
- 导出当前对话状态
3.11 ConfigChange(配置变更)🆕
v2.1.49+ 新增:在 Claude Code 配置发生变更时触发。
触发时机:settings.json 或其他配置文件被修改时
典型用途:
- 配置变更审计和日志记录
- 自动同步配置到其他环境
- 配置变更通知
3.12 TeammateIdle(队友空闲)🆕
v2.1.49+ 新增:在多代理协作场景中,当队友代理进入空闲状态时触发。
触发时机:协作中的队友代理完成当前任务、进入空闲状态时
典型用途:
- 自动分配待处理任务给空闲队友
- 发送状态通知
- 协调多代理工作流
3.13 新增Hook事件类型 🆕
v2.1+ 新增:以下Hook类型进一步扩展了Claude Code的事件覆盖范围,让你能捕捉到更多关键时刻。
🎯 生活类比:如果之前的Hook是"门窗报警器",这些新Hook就是"烟雾报警器、水浸传感器、燃气检测器"——覆盖了之前监控不到的异常场景。
StopFailure(API异常停止时触发)
触发时机:当API错误(429限流、401认证失败、500服务器错误等)导致会话异常停止时触发
⚡ 与Stop的区别:
Stop是正常结束(用户主动退出、任务完成),就像下班正常关灯锁门;StopFailure是异常中断,就像突然停电——你需要知道发生了什么并采取措施。
典型用途:
- 发送告警通知(Slack/邮件/钉钉)
- 记录错误日志用于后续分析
- 触发自动重试或降级逻辑
配置示例:
{
"hooks": {
"StopFailure": [
{
"command": "python .claude/hooks/alert-on-failure.py",
"timeout": 10000
}
]
}
}📝 输入数据:StopFailure的stdin JSON中包含
error字段,携带具体的错误类型和消息,可用于区分限流、认证失败等不同场景。
PostCompact(上下文压缩后触发)
触发时机:执行 /compact 命令或上下文自动压缩完成后触发
🎯 生活类比:就像搬家后清点物品——压缩完成后,你想知道"丢掉了多少东西、还剩多少空间"。
典型用途:
- 记录压缩前后的token数量变化
- 触发上下文恢复操作(如重新加载关键文件)
- 日志记录用于监控上下文使用趋势
配置示例:
{
"hooks": {
"PostCompact": [
{
"command": "echo \"[$(date)] 上下文已压缩\" >> ~/.claude/compact.log",
"timeout": 5000
}
]
}
}⚠️ 注意:PostCompact 是不可阻止的Hook,压缩已经完成,你只能执行后续操作。与
PreCompact(压缩前)配合使用效果更佳。
InstructionsLoaded(指令文件加载时触发)
触发时机:CLAUDE.md 等指令文件加载到上下文时触发
🎯 生活类比:就像新员工入职时检查培训手册是否齐全——确保AI读到了正确且完整的指令。
典型用途:
- 验证指令文件的完整性和正确性
- 记录哪些指令文件被加载(便于调试)
- 触发项目特定的初始化操作
配置示例:
{
"hooks": {
"InstructionsLoaded": [
{
"command": "python .claude/hooks/verify-instructions.py",
"timeout": 5000
}
]
}
}📝 输入数据:stdin JSON中包含已加载指令文件的路径列表,可用于检查关键指令文件是否缺失。
Elicitation 和 ElicitationResult(MCP交互输入)🆕
v2.1+ 新增:这两个Hook配合MCP Elicitation功能使用,让你能监控和管理MCP服务器与用户之间的交互输入。
🎯 生活类比:
Elicitation就像客服系统弹出问卷调查——MCP服务器需要向用户询问信息;ElicitationResult就像用户填完问卷提交——你可以记录和验证填写的内容。
Elicitation — MCP请求输入时触发
触发时机:MCP服务器通过Elicitation API向用户发起交互输入请求时
典型用途:
- 记录MCP交互请求日志
- 自动填充常用值(如默认配置)
ElicitationResult — 用户完成输入后触发
触发时机:用户完成MCP交互输入并提交后
典型用途:
- 验证用户输入数据的合法性
- 记录交互结果用于审计
- 触发后续自动化流程
配置示例:
{
"hooks": {
"Elicitation": [
{
"command": "echo \"MCP请求输入: $(date)\" >> ~/.claude/elicitation.log",
"timeout": 5000
}
],
"ElicitationResult": [
{
"command": "python .claude/hooks/validate-elicitation.py",
"timeout": 5000
}
]
}
}3.14 HTTP Hooks(远程Webhook集成)🆕
v2.1+ 新增:除了传统的Shell命令Hook,现在可以直接将Hook事件POST到远程URL,无需编写本地脚本。
🎯 生活类比:之前的Hook像是"自家装的门铃"——得自己接线、自己写响铃逻辑;HTTP Hook像是"接入物业监控中心"——事件发生时自动通知远程服务,你只管接收处理。
配置格式:
{
"hooks": {
"PostToolUse": [
{
"type": "http",
"url": "https://your-webhook.example.com/hook",
"timeout": 5000
}
]
}
}工作原理:
- 当Hook事件触发时,Claude Code 自动向配置的URL发送 POST 请求
- 请求体为JSON格式,自动包含该Hook的标准输入数据(与Shell Hook的stdin内容相同)
- 支持设置超时时间,防止远程服务无响应时阻塞工作流
Shell Hook vs HTTP Hook 对比:
| 特性 | Shell Hook | HTTP Hook 🆕 |
|---|---|---|
| 执行方式 | 运行本地脚本/命令 | POST JSON到远程URL |
| 需要本地脚本 | ✅ 是 | ❌ 否 |
| 跨平台一致性 | ❌ 需处理OS差异 | ✅ 任何平台行为一致 |
| 适合场景 | 本地文件操作、代码检查 | 远程通知、日志收集、CI/CD |
| 网络依赖 | ❌ 无 | ✅ 需要网络连接 |
| 配置复杂度 | 中(需写脚本) | 低(只需URL) |
适用场景:
- Slack/Discord通知:代码提交、构建完成时自动发送消息
- CI/CD触发:Hook事件触发远程构建管道
- 日志收集服务:将所有Hook事件发送到Datadog/ELK等平台
- 团队协作:多人开发时自动同步状态到共享服务
⚠️ 安全提示:HTTP Hook会将Hook数据发送到外部服务,确保目标URL可信且使用HTTPS。避免在Hook数据中包含敏感信息(API密钥、密码等)。