Claude Code Hooks 完全指南:让 AI 编程更智能、更安全
TLDR;
给非计算机领域的:Hook 就是你先向系统‘登记’一个你自己的运行逻辑,并告诉系统:‘等某某事情发生的时候,你记得调一下我这个逻辑。系统就会在合适的时候回调你。
Claude Code hooks 提供了,工具使用,对话,会话等的开始结束时机,在这些时机里,把运行的会话信息传递给你指定的逻辑,借助这个机制,你可以做很多自定义的事。
你有没有想过,如果能在 Claude Code 执行某些操作之前或之后自动运行自己的脚本,会是什么样的体验?比如:
- ✅ Claude 刚写完代码,自动运行格式化工具
- 🚫 Claude 准备删除重要文件时,自动阻止并警告
- 📊 每次会话结束,自动记录工作日志
- 🔒 检测到敏感信息泄露,立即阻止请求
这些都可以通过 Claude Code Hooks 功能实现!无论你是编程新手还是经验丰富的开发者,这篇文章都会让你快速掌握这个强大功能。
什么是 Hooks?
通俗理解
想象一下,你雇佣了一位超级聪明的 AI 助手(Claude)帮你写代码。但有时候你希望:
- 在它做某些事情之前先检查一下(比如删除文件)
- 在它做完某些事情之后自动处理(比如代码写完后自动格式化)
- 在特定时刻自动执行某些任务(比如启动时加载配置)
**Hooks(钩子)**就是这些"关键时刻"的触发器。就像游戏里的检查点系统,让你能在关键节点进行干预或自动化处理。
技术定义
Hooks 是用户自定义的 Shell 命令,在 Claude Code 生命周期的特定节点自动执行。它们提供了对 Claude Code 行为的确定性控制,让 AI 编程更可靠、更安全。
Hooks 能做什么?
实际应用场景
1. 代码质量自动检查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| {
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npm run lint"
}
]
}
]
}
}
|
每当 Claude 写完或编辑完文件,自动运行代码检查工具。
2. 阻止危险操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| {
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 scripts/check-dangerous.sh"
}
]
}
]
}
}
|
在 Claude 执行 Shell 命令前,检查是否包含危险操作(如 rm -rf)。
3. 自动添加上下文
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| {
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "python3 scripts/load-issues.py"
}
]
}
]
}
}
|
会话开始时,自动加载项目中的 Issues 或近期变更。
4. 敏感信息防护
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| #!/usr/bin/env python3
import json
import sys
import re
# 从 stdin 读取用户输入
input_data = json.load(sys.stdin)
prompt = input_data.get("prompt", "")
# 检测敏感模式
if re.search(r'(?i)\b(password|secret|key)\s*[:=]', prompt):
print(json.dumps({
"decision": "block",
"reason": "检测到可能的敏感信息,请重新表述请求"
}))
sys.exit(0)
|
Hooks 的 9 个触发时机
Claude Code 提供了 9 个关键事件点,让你可以在不同阶段介入:
在 Claude 执行工具(如写文件、运行命令)之前触发。
- 用途: 验证、阻止、自动批准操作
- 常见场景: 检查危险命令、验证文件路径、自动批准安全操作
2. PostToolUse - 工具使用后
在 Claude 执行完工具之后立即触发。
- 用途: 自动化后续任务
- 常见场景: 代码格式化、运行测试、发送通知
3. UserPromptSubmit - 用户提交提示时
在你发送消息给 Claude 之前触发。
- 用途: 验证用户输入、添加上下文
- 常见场景: 敏感信息检测、自动补充项目信息
4. Stop - Claude 完成响应时
在 Claude 完成一轮对话之后触发。
- 用途: 质量检查、自动继续
- 常见场景: 运行测试套件、代码质量门禁
5. SubagentStop - 子任务完成时
当 Claude 的子任务(独立执行的专门任务)完成时触发。
- 用途: 子任务特定的后处理
- 常见场景: 处理子任务结果、日志记录
6. Notification - 通知时
当 Claude 需要你授权使用工具,或等待你输入时触发。
- 用途: 自定义通知处理
- 常见场景: 发送外部通知、记录待处理事项
7. SessionStart - 会话开始时
当你启动 Claude Code 或恢复会话时触发。
- 用途: 环境准备、上下文加载
- 常见场景: 安装依赖、加载项目信息、设置环境变量
8. SessionEnd - 会话结束时
当你关闭 Claude Code 时触发。
- 用途: 清理任务、日志记录
- 常见场景: 保存工作日志、清理临时文件
9. PreCompact - 压缩上下文前
当 Claude 准备压缩对话历史以节省 Token 时触发。
- 用途: 保存重要信息
- 常见场景: 提取关键决策、保存重要上下文
如何配置 Hooks?
配置文件位置
Hooks 配置保存在 settings.json 文件中,有三个层级:
全局配置 - 作用于所有项目
~/.claude/settings.json
项目配置 - 作用于特定项目
.claude/settings.json
本地项目配置 - 不提交到版本控制
.claude/settings.local.json
基本配置结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| {
"hooks": {
"事件名称": [
{
"matcher": "工具匹配模式",
"hooks": [
{
"type": "command",
"command": "你的命令",
"timeout": 30
}
]
}
]
}
}
|
字段说明
简单示例:自动格式化代码
创建项目配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # 在项目根目录创建配置
mkdir -p .claude
cat > .claude/settings.json << 'EOF'
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "prettier --write \"$CLAUDE_PROJECT_DIR\"/**/*.{js,jsx,ts,tsx,json,md}",
"timeout": 30
}
]
}
]
}
}
EOF
|
进阶:用脚本实现智能控制
Hooks 的真正威力在于可以用编程方式实现复杂逻辑。
示例 1:阻止危险命令
创建 scripts/check-dangerous.sh:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| #!/bin/bash
# 读取 Claude 传来的 JSON 输入
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // empty')
# 危险命令列表
dangerous_patterns=(
"rm -rf /"
"rm -rf \\.\\./"
"dd if=/dev/zero"
"> /dev/sd[a-z]"
"mkfs\\."
)
# 检查是否包含危险模式
for pattern in "${dangerous_patterns[@]}"; do
if echo "$command" | grep -qE "$pattern"; then
echo "⚠️ 检测到危险命令: $command" >&2
echo "此操作可能导致数据丢失,请确认是否继续。" >&2
exit 2 # 退出码 2 会阻止操作
fi
done
exit 0 # 允许执行
|
配置 Hook:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| {
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/scripts/check-dangerous.sh"
}
]
}
]
}
}
|
示例 2:智能代码质量门禁
创建 scripts/quality-gate.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
| #!/usr/bin/env python3
import json
import sys
import subprocess
def read_input():
"""读取 Claude 传来的输入"""
return json.load(sys.stdin)
def run_tests():
"""运行测试套件"""
try:
result = subprocess.run(
["npm", "test", "--", "--coverage"],
capture_output=True,
text=True,
timeout=60
)
return result.returncode == 0
except Exception as e:
print(f"测试执行失败: {e}", file=sys.stderr)
return False
def check_coverage():
"""检查代码覆盖率"""
# 简化的示例 - 实际应该解析覆盖率报告
return True
def main():
input_data = read_input()
# 运行质量检查
tests_passed = run_tests()
coverage_ok = check_coverage()
if not (tests_passed and coverage_ok):
# 返回 JSON 让 Claude 知道需要修复
output = {
"decision": "block",
"reason": "❌ 代码质量检查失败:\n"
f"- 测试: {'✅ 通过' if tests_passed else '❌ 失败'}\n"
f"- 覆盖率: {'✅ 符合要求' if coverage_ok else '❌ 不足'}\n\n"
"请修复这些问题后再继续。"
}
print(json.dumps(output))
sys.exit(0)
print("✅ 所有质量检查通过")
sys.exit(0)
if __name__ == "__main__":
main()
|
示例 3:会话开始时自动加载项目上下文
创建 scripts/load-context.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
| #!/usr/bin/env python3
import json
import sys
import subprocess
from datetime import datetime
def get_recent_commits():
"""获取最近的 Git 提交"""
try:
result = subprocess.run(
["git", "log", "-5", "--oneline"],
capture_output=True,
text=True
)
if result.returncode == 0:
return f"\n最近的提交:\n{result.stdout}"
except:
pass
return ""
def get_open_issues():
"""获取打开的 Issues(如果有 GitHub CLI)"""
try:
result = subprocess.run(
["gh", "issue", "list", "--limit", "5"],
capture_output=True,
text=True
)
if result.returncode == 0:
return f"\n当前打开的 Issues:\n{result.stdout}"
except:
pass
return ""
def main():
# 收集上下文信息
context = []
context.append(f"当前时间: {datetime.now().strftime('%Y-%m-%d %H:%M')}")
commits = get_recent_commits()
if commits:
context.append(commits)
issues = get_open_issues()
if issues:
context.append(issues)
# 返回 JSON 格式,添加到 Claude 的上下文
output = {
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "\n".join(context)
}
}
print(json.dumps(output))
sys.exit(0)
if __name__ == "__main__":
main()
|
Hook 的输入和输出
输入:JSON 数据
每个 Hook 都会通过 stdin 接收 JSON 数据,包含:
1
2
3
4
5
6
7
8
| {
"session_id": "会话ID",
"transcript_path": "对话记录路径",
"cwd": "当前工作目录",
"permission_mode": "权限模式",
"hook_event_name": "事件名称",
// ... 事件特定字段
}
|
PreToolUse 额外字段:
1
2
3
4
5
6
7
| {
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.txt",
"content": "文件内容"
}
}
|
PostToolUse 额外字段:
1
2
3
4
5
6
7
| {
"tool_name": "Write",
"tool_input": { /* ... */ },
"tool_response": {
"success": true
}
}
|
输出:控制 Claude 的行为
方式 1:退出码(简单)
- 退出码 0: 成功,继续执行
- 退出码 2: 阻止操作,
stderr 会显示给 Claude - 其他退出码: 非阻塞错误,显示给用户
方式 2:JSON 输出(高级)
在 stdout 输出 JSON 以获得精细控制:
1
2
3
4
5
6
| {
"continue": true, // 是否继续
"stopReason": "原因", // 停止原因(如果不继续)
"suppressOutput": false, // 是否隐藏输出
"systemMessage": "警告消息" // 显示给用户的警告
}
|
PreToolUse 特定输出:
1
2
3
4
| {
"permissionDecision": "allow|deny|ask",
"permissionDecisionReason": "原因"
}
|
UserPromptSubmit 特定输出:
1
2
3
4
5
6
7
8
| {
"decision": "block",
"reason": "阻止原因",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "要添加的上下文"
}
}
|
实战案例集合
案例 1:防止意外提交敏感文件
scripts/block-secrets.sh:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| #!/bin/bash
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
# 检查是否是敏感文件
sensitive_files=(
"\\.env$"
"\\.pem$"
"\\.key$"
"credentials\\.json"
"secrets\\.yaml"
)
for pattern in "${sensitive_files[@]}"; do
if echo "$file_path" | grep -qE "$pattern"; then
echo "⚠️ 警告: 可能正在提交敏感文件: $file_path" >&2
echo "请确认不会泄露密钥或密码。" >&2
exit 2
fi
done
exit 0
|
案例 2:自动记录工作日志
scripts/log-work.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| #!/usr/bin/env python3
import json
import sys
from datetime import datetime
def log_session_end(input_data):
reason = input_data.get("reason", "unknown")
timestamp = datetime.now().isoformat()
log_entry = {
"timestamp": timestamp,
"reason": reason,
"session_id": input_data.get("session_id")
}
# 追加到日志文件
with open("~/claude-work-log.jsonl", "a") as f:
f.write(json.dumps(log_entry) + "\n")
print(f"✅ 会话已记录: {timestamp}")
if __name__ == "__main__":
input_data = json.load(sys.stdin)
log_session_end(input_data)
sys.exit(0)
|
案例 3:自动优化代码
scripts/optimize-code.py:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| #!/usr/bin/env python3
import json
import sys
import subprocess
input_data = json.load(sys.stdin)
file_path = input_data.get("tool_input", {}).get("file_path", "")
# 只处理 JavaScript/TypeScript 文件
if not file_path.endswith(('.js', '.jsx', '.ts', '.tsx')):
sys.exit(0)
# 运行代码优化工具
try:
subprocess.run([
"prettier", "--write", file_path
], check=True)
print(f"✨ 已优化: {file_path}")
except Exception as e:
print(f"优化失败: {e}", file=sys.stderr)
sys.exit(0)
|
安全最佳实践
⚠️ 重要警告
Hooks 会自动执行你配置的命令,使用不当可能导致数据丢失!
安全检查清单
✅ 验证输入
- 永远不要盲目信任输入数据
- 检查路径遍历攻击(
..) - 验证文件类型和路径
✅ 使用绝对路径
"${CLAUDE_PROJECT_DIR}/scripts/script.sh"- 而不是
scripts/script.sh
✅ 引号保护变量
✅ 避免敏感文件
- 跳过
.env, .git/, 密钥文件 - 添加显式检查
✅ 测试环境优先
不安全示例 ❌
1
2
3
| {
"command": "rm $CLAUDE_PROJECT_DIR/temp/*"
}
|
问题: 如果 $CLAUDE_PROJECT_DIR 为空,会删除系统根目录的 temp 文件!
安全示例 ✅
1
2
3
| {
"command": "rm -f \"$CLAUDE_PROJECT_DIR\"/temp/* 2>/dev/null || true"
}
|
改进:
- 使用引号保护变量
- 添加
-f 防止交互 - 错误重定向
- 失败也不中断
调试技巧
1. 查看 Hook 状态
1
2
| # 在 Claude Code 中输入
/hooks
|
会显示所有已配置的 Hooks 及其状态。
2. 启用调试模式
会显示详细的 Hook 执行信息:
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Hook command completed with status 0
3. 测试 Hook 脚本
手动测试脚本,确保逻辑正确:
1
2
3
| # 模拟输入
echo '{"tool_name": "Write", "tool_input": {"file_path": "/tmp/test.txt"}}' \
| python3 scripts/check-dangerous.py
|
4. 常见问题排查
| 问题 | 可能原因 | 解决方法 |
|---|
| Hook 没有运行 | JSON 格式错误 | 使用 jq . 验证 JSON |
| Hook 执行失败 | 脚本没有执行权限 | 运行 chmod +x script.sh |
| 命令找不到 | 没有使用绝对路径 | 使用完整路径或 $CLAUDE_PROJECT_DIR |
| Hook 无限循环 | Stop Hook 触发新操作 | 检查 stop_hook_active 字段 |
总结
Claude Code Hooks 是一个强大的自动化和质量控制工具:
核心优势
- 确定性控制 - 让 AI 编程更可预测
- 自动化工作流 - 减少重复操作
- 安全防护 - 阻止危险操作
- 质量保障 - 自动运行检查
- 灵活扩展 - 支持任意脚本
学习路径
初学者:
- 从简单配置开始(如自动格式化)
- 使用现有的 Hook 示例
- 理解 9 个事件的基本用途
进阶用户:
- 编写自定义脚本
- 组合多个 Hooks
- 实现 JSON 输出的高级控制
专家用户:
- 构建复杂的质量门禁系统
- 集成外部服务(如通知、日志)
- 创建可复用的 Hook 库
下一步
- 查看 官方 Hooks 文档
- 探索社区 Hooks 示例(GitHub 搜索 “claude-code-hooks”)
- 分享你的 Hooks 配置!
记住: Hooks 是让 AI 为你工作的强大工具,但要用得聪明、用得安全。从简单的配置开始,逐步构建你的自动化工作流!
有问题或想法?欢迎在评论区交流! 🚀