透过注册 shell 命令,客制化并扩展您的 Claude Code 工作流程。
Hooks 是您定义的 shell 命令,它们会在 Claude Code 生命周期的特定时间点自动执行。您可以把它们想像成是为您的 AI 程式设计伙伴设定的「自动化规则」或「触发器」。
与其在提示中反复告诉 Claude 要遵守某些规则,不如将这些规则编码为 Hooks,使其成为您开发环境中可靠且自动化的一部分。
Hooks 让您能够对 Claude Code 的行为进行确定性的控制,确保某些动作总是会发生。常见的使用情境包括:
- ✍️ 自动格式化: 在 Claude 每次编辑完毕后,自动对
.ts档案执行prettier,或对.go档案执行gofmt。 - 🔔 自订通知: 当 Claude 等待您的指令或执行权限时,透过系统通知或声音提醒您。
- 🛡️ 安全防护: 阻止 Claude 执行危险的命令(例如
rm -rf)或修改生产环境的设定档。 - 📝 合规性记录: 追踪所有由 Claude 执行的 shell 命令,以满足稽核或除错需求。
- 💡 自动回馈: 当 Claude 产生的程式码不符合您的专案规范时,自动向它提供修正建议。
本教学将引导您建立三个 Hooks,从简单到复杂,逐步掌握核心概念。
我们的第一个 Hook 非常简单:每当 Claude 储存一个档案时,我们就在一个日志档中记录下来。这个范例不需要任何外部工具。
- 在终端机中执行
/hooks命令,打开 Hooks 设定介面。 - 从事件列表中选择
PostToolUse。这个事件会在 Claude 成功执行工具之后触发。 - 选择
+ Add new matcher…,输入Write|Edit|MultiEdit来匹配档案操作工具。 - 选择
+ Add new hook…,然后输入以下命令:echo "Claude edited a file at $(date)" >> ~/.claude/file-edit-log.txt
- 按
Enter储存 Hook。系统会询问您储存位置,选择User settings(使用者设定),这样这个 Hook 就会在您所有的专案中生效。 - 按
Esc退出设定介面。
验证一下:现在,请 Claude 随意修改您专案中的任何一个档案。完成后,检查日志档的内容:
cat ~/.claude/file-edit-log.txt您应该会看到类似 "Claude edited a file at [日期时间]" 的讯息。恭喜,您已经成功建立了第一个 Hook!
上一个 Hook 会在任何档案被编辑后触发。如果我们只想在特定工具被使用时触发呢?这时就需要使用「匹配器」。
- 再次执行
/hooks,选择PostToolUse事件。 - 选择
+ Add new matcher…,输入Bash。这样 Hook 就只会在 Claude 执行 shell 命令后触发。 - 新增一个 Hook 命令:
echo "Claude ran a bash command at $(date)" >> ~/.claude/bash-log.txt
- 储存设定并退出。
验证一下:
- 请 Claude 执行一个 shell 命令,例如「列出目前目录的档案」。检查
~/.claude/bash-log.txt,您会发现新增了一条日志。 - 再请 Claude 编辑一个档案。这次只会在
file-edit-log.txt中看到记录,而不会在bash-log.txt中看到。
匹配器让您可以精准控制 Hook 的触发时机与对象。
现在,我们来挑战一个更进阶的 Hook:记录 Claude 尝试执行的所有 shell 命令详细资讯。这个 Hook 会读取 Claude 传递的资料,并需要 jq 工具来解析 JSON。
先决条件:请确保您已安装 jq。如果没有,请使用您的套件管理器安装:
- macOS:
brew install jq - Ubuntu/Debian:
sudo apt-get install jq - Windows: 下载自 https://stedolan.github.io/jq/
- 执行
/hooks,这次选择PreToolUse事件。这个事件在 Claude 准备执行一个工具(如bash命令)之前触发。 - 为这个 Hook 新增一个匹配器,输入
Bash,这样它就只会监控 shell 命令。 - 选择
+ Add new hook…并输入以下命令:这个命令会从传入的 JSON 资料中提取jq -r '"COMMAND: \(.tool_input.command) | DESCRIPTION: \(.tool_input.description // "None")"' >> ~/.claude/bash-command-log.txt
command和description栏位,并将其格式化后写入日志档。 - 储存为
User settings并退出。
验证一下:请 Claude 执行一个 shell 命令,例如「列出目前目录的所有档案」。在您授权 Claude 执行之前,这个 Hook 就已经被触发了。检查日志档:
cat ~/.claude/bash-command-log.txt您应该会看到类似 COMMAND: ls -l | DESCRIPTION: List all files in the current directory 的记录。
透过这个教学,您已经学会了:
- 如何在特定事件上建立 Hook
- 如何使用匹配器来过滤事件
- 如何读取 Claude 传递的上下文资料
Hooks 可以在 Claude Code 生命周期的多个时间点触发。以下是主要的事件类型:
| 事件名称 | 触发时机 | 常见用途 | 可阻止操作 |
|---|---|---|---|
UserPromptSubmit |
使用者提交提示后,Claude 处理之前 | 验证提示、根据提示内容注入额外上下文 | ✅ 是 |
PreToolUse |
在工具(如 Bash)被执行之前 |
阻止危险命令、记录意图、修改命令 | ✅ 是 |
PostToolUse |
在工具执行之后 | 记录执行结果、基于结果触发下一步 | ❌ 否 |
Notification |
当需要发送通知时 | 自订通知方式、声音提醒 | ❌ 否 |
Stop |
当 Claude 完成任务或遇到错误时 | 清理工作、后续处理 | ✅ 是 |
SubagentStop |
当子代理(Task 工具)完成时 | 针对子任务的后续处理 | ✅ 是 |
PreCompact |
在 Claude 执行上下文压缩之前 | 根据压缩类型执行不同操作、备份重要上下文 | ❌ 否 |
每个 Hook 在执行时,都会透过标准输入(stdin)接收一个包含上下文资讯的 JSON 物件。您可以使用 jq 或其他工具来解析它。
通用栏位:
{
"session_id": "string",
"transcript_path": "string", // 对话记录档路径
"cwd": "string", // Hook 被调用时的当前工作目录
"hook_event_name": "string" // 触发的 Hook 事件名称
}PreToolUse 的输入范例:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/path/to/project",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "ls -a",
"description": "List all files, including hidden ones."
}
}PostToolUse 的输入范例:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/path/to/project",
"hook_event_name": "PostToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.ts",
"content": "console.log('Hello World');"
},
"tool_response": {
"filePath": "/path/to/file.ts",
"success": true
}
}Notification 的输入范例:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/path/to/project",
"hook_event_name": "Notification",
"message": "Claude needs your permission to use Bash"
}UserPromptSubmit 的输入范例:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/path/to/project",
"hook_event_name": "UserPromptSubmit",
"prompt": "Write a function to calculate the factorial of a number"
}Stop / SubagentStop 的输入范例:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/path/to/project",
"hook_event_name": "Stop",
"stop_hook_active": true
}
stop_hook_active为true表示 Claude 正在因为前一个StopHook 的结果而继续执行。您可以检查此值以避免无限循环。
PreCompact 的输入范例:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../session.jsonl",
"cwd": "/path/to/project",
"hook_event_name": "PreCompact",
"trigger": "manual",
"custom_instructions": ""
}
trigger: 压缩触发原因
"manual": 由使用者透过/compact命令手动触发"auto": 由于上下文视窗已满而自动触发custom_instructions: 对于manual触发,包含使用者传递给/compact的自订指令;对于auto触发,此栏位为空字串
Hook 可以透过两种方式影响 Claude Code 的行为:简单的退出码或进阶的 JSON 输出。输出主要用来沟通是否要阻止操作,以及应该向 Claude 和使用者显示什么回馈。
这是最基本的回馈机制。
- 退出代码 0: 成功。
stdout的内容会以一般资讯的形式显示给使用者(在 transcript 模式下可见),但 Claude 不会看到。 - 退出代码 2: 阻挡性错误。
stderr的内容会作为回馈提供给 Claude 进行处理。不同事件的具体行为不同(见下表)。 - 其他退出代码: 非阻挡性错误。
stderr的内容只会显示给使用者,操作会继续执行。
退出代码 2 的行为细节
| Hook 事件 | 阻挡行为 |
|---|---|
PreToolUse |
阻挡工具执行,并将 stderr 内容交给 Claude 分析。 |
PostToolUse |
工具已经执行,但会将 stderr 内容交给 Claude 进行后续修正。 |
UserPromptSubmit |
阻挡使用者提示的处理,清除该提示,并将 stderr 显示给使用者。 |
Stop / SubagentStop |
阻挡 Claude 停止,并将 stderr 内容交给 Claude 以决定下一步。 |
Notification / PreCompact |
无特殊阻挡效果,仅将 stderr 显示给使用者。 |
stdout 的内容。只有 stderr 在退出代码为 2 时才会被 Claude 处理。
为了进行更精细的控制,Hook 可以透过 stdout 返回一个 JSON 物件。
通用 JSON 栏位 (适用于所有事件):
{
"continue": true,
"stopReason": "使用者要求的操作已终止",
"suppressOutput": true
}continue(boolean, 预设true): 设为false可在 Hook 执行后完全终止 Claude 的后续处理。stopReason(string): 当continue为false时,向使用者显示的停止原因(不会显示给 Claude)。suppressOutput(boolean, 预设false): 设为true可以隐藏 Hook 的stdout,使其不在 transcript 模式中显示。
重要行为说明:
- 当
continue为false时,会优先于任何"decision": "block"设定 - 对于
PreToolUse,这与"decision": "block"不同 - 后者只阻挡特定工具呼叫并提供自动回馈给 Claude - 对于
PostToolUse,这与"decision": "block"不同 - 后者提供自动回馈给 Claude - 对于
UserPromptSubmit,这会阻止提示被处理 - 对于
Stop和SubagentStop,这会优先于任何"decision": "block"输出
特定事件的决策控制 (decision):
-
PreToolUse: 控制工具是否执行。{ "decision": "approve" | "block", "reason": "批准原因或阻挡原因" }"approve": 绕过权限询问,直接执行。reason显示给使用者。"block": 阻止工具执行。reason会提供给 Claude。undefined: 保持预设的权限询问流程。
-
PostToolUse/Stop/SubagentStop: 控制是否需要 Claude 进行后续处理。{ "decision": "block", "reason": "需要 Claude 处理的回馈资讯" }"block": 提示 Claude 根据reason继续工作。例如,在PostToolUse中指出程式码格式问题,或在Stop中要求 Claude 继续执行下一步。
-
UserPromptSubmit: 控制使用者提示是否被处理。{ "decision": "block", "reason": "向使用者显示的阻挡原因" }"block": 阻止提示被处理,并清除该提示。reason只会显示给使用者。
Claude Code 的设定档结构如下,您可以将 Hooks 设定放在其中:
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "your-command-here",
"timeout": 30
}
]
}
]
}
}matcher: 工具名称模式匹配(例如Edit|Write),支援简单字串精确匹配和正规表示式,对大小写敏感。如果省略或为空字串,则会对所有工具生效。hooks: 一组要执行的命令。command: 要执行的 shell 命令。timeout(可选): 命令执行的超时时间(秒)。
设定档层级:
- 使用者设定 (
~/.claude/settings.json): 在这里设定的 Hooks 会在您所有的专案中生效。适合通用规则,如通知、日志记录。 - 专案设定 (
<project_root>/.claude/settings.json): 在这里设定的 Hooks 仅对当前专案生效。适合专案特定的规则,如程式码格式化、特定于专案的安全防护。 - 本地专案设定 (
<project_root>/.claude/settings.local.json): 本地设定,不会被版本控制。 - 企业管理政策设定: 如果您在企业环境中,管理员可能已配置全域策略设定。
"matcher": "*" 是无效的语法。如果要匹配所有工具,请省略 matcher 栏位或使用 "matcher": ""。
这里有一些可以直接使用的范例,帮助您快速提升效率。
在 Claude 每次修改完档案后,自动执行 Prettier 格式化。
设定:
- 事件:
PostToolUse - 匹配器:
Edit|MultiEdit|Write - Hook 命令:
# 检查是否为 JS/TS 档案并执行 prettier
file_path=$(jq -r '.tool_input.file_path // ""')
if [[ "$file_path" =~ \.(js|jsx|ts|tsx)$ ]] && [[ -f "$file_path" ]] && [[ -f "package.json" ]]; then
npx --no-install prettier --write "$file_path"
echo "✨ 已自动格式化档案: $file_path"
fi设定:
- 事件:
PostToolUse - 匹配器:
Edit|MultiEdit|Write - Hook 命令:
# 检查是否为 Go 档案并执行 gofmt
file_path=$(jq -r '.tool_input.file_path // ""')
if [[ "$file_path" =~ \.go$ ]] && [[ -f "$file_path" ]]; then
gofmt -w "$file_path"
echo "✨ 已自动格式化 Go 档案: $file_path"
fi这个范例展示了如何使用 Python 脚本,在 Claude 执行 Bash 命令前进行验证,并使用 JSON 输出 提供结构化的回馈。
设定:
- 事件:
PreToolUse - 匹配器:
Bash - Hook 命令:
python .claude/hooks/validate_bash.py
建立脚本 (.claude/hooks/validate_bash.py):
#!/usr/bin/env python3
import json
import re
import sys
# 定义验证规则 (正规表示式, 建议讯息)
VALIDATION_RULES = [
(
r"\bgrep\b(?!.*\|)",
"请改用 'rg' (ripgrep),它在专案范围内的搜寻效能更好。",
),
(
r"\bfind\s+\S+\s+-name\b",
"建议使用 'rg --files | rg <pattern>' 或 'rg -g '<pattern>'' 来取代 'find -name',速度更快。",
),
]
def validate_command(command: str) -> list[str]:
issues = []
for pattern, message in VALIDATION_RULES:
if re.search(pattern, command):
issues.append(message)
return issues
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError:
sys.exit(1) # 无法解析 JSON,静默退出
command = input_data.get("tool_input", {}).get("command", "")
if not command:
sys.exit(0) # 如果没有命令,则不进行任何操作
issues = validate_command(command)
if issues:
# 如果发现问题,使用 JSON 输出格式来阻挡并提供回馈
response = {
"decision": "block",
"reason": "发现以下可以改进的地方:\n- " + "\n- ".join(issues)
}
print(json.dumps(response))
else:
# 如果没有问题,可以明确批准或不输出任何东西让流程继续
print(json.dumps({"decision": "approve", "reason": "命令检查通过"}))
sys.exit(0)当 Claude 等待您授权时,发出语音提醒。
设定:
- 事件:
Notification - 匹配器: (留空,适用于所有通知)
- Hook 命令:
message=$(jq -r '.message // "Claude Code notification"')
say "Claude says: $message"设定:
- 事件:
Notification - 匹配器: (留空)
- Hook 命令:
# 需要安装 libnotify-bin
title=$(jq -r '.title // "Claude Code"')
message=$(jq -r '.message // "Notification from Claude"')
notify-send "$title" "$message"阻止 Claude 执行可能危险的命令。
设定:
- 事件:
PreToolUse - 匹配器:
Bash - Hook 命令:
command=$(jq -r '.tool_input.command // ""')
# 检查危险命令模式
dangerous_patterns=("rm -rf" "sudo rm" "dd if=" "mkfs" "fdisk" "> /dev/")
for pattern in "${dangerous_patterns[@]}"; do
if [[ "$command" == *"$pattern"* ]]; then
# 使用退出码 2 来阻挡操作,并将 stderr 的内容传递给 Claude
echo "🚫 安全警告: 已阻止潜在危险命令: $command" >&2
echo "💡 建议: 请使用更安全的替代方案或明确指定档案" >&2
exit 2
fi
done
echo "✅ 命令安全检查通过: $command"防止 Claude 修改重要的设定档案。
设定:
- 事件:
PreToolUse - 匹配器:
Edit|MultiEdit|Write - Hook 命令:
file_path=$(jq -r '.tool_input.file_path // ""')
# 敏感档案模式
sensitive_files=(".env" ".env.local" ".env.production" "id_rsa" "id_ed25519" "package-lock.json" "yarn.lock")
for pattern in "${sensitive_files[@]}"; do
if [[ "$file_path" == *"$pattern"* ]]; then
reason=""
case "$pattern" in
"package-lock.json"|"yarn.lock")
reason="💡 提示: 如需更新依赖,请让 Claude 使用 'npm install' 或 'yarn install'"
;;
".env"*)
reason="💡 提示: 环境变数档案包含敏感资讯,请手动编辑"
;;
"id_rsa"|"id_ed25519")
reason="💡 提示: SSH 金钥档案不应被 Claude 修改"
;;
*)
reason="🔒 安全限制: 不允许 Claude 修改敏感档案: $file_path"
;;
esac
# 使用 JSON 输出阻挡操作
jq -n --arg reason "$reason" '{decision: "block", reason: $reason}'
exit 0
fi
done在程式码修改后自动执行测试。
设定:
- 事件:
PostToolUse - 匹配器:
Edit|MultiEdit|Write - Hook 命令:
file_path=$(jq -r '.tool_input.file_path // ""')
# 检查是否为测试相关档案或源码档案
if [[ "$file_path" =~ \.(test|spec)\. ]] || [[ "$file_path" =~ /src/ ]]; then
echo "🧪 检测到程式码变更,执行相关测试..."
# 检查专案类型并执行适当的测试命令
if [ -f "package.json" ]; then
# --no-install 避免在没有 node_modules 时的交互提示
if jq -e '.scripts.test' package.json > /dev/null; then
npm test -- --testPathPattern="$(basename "$file_path" | sed 's/\.[^.]*$//')"
fi
elif [ -f "go.mod" ]; then
go test ./...
elif [ -f "Cargo.toml" ]; then
cargo test
elif [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then
python -m pytest -xvs
fi
fi以下是官方提供的 Python 脚本范例,展示如何使用 JSON 输出进行 Bash 命令验证:
设定:
- 事件:
PreToolUse - 匹配器:
Bash - Hook 命令:
python /path/to/bash-validator.py
脚本内容 (bash-validator.py):
#!/usr/bin/env python3
import json
import re
import sys
# 定义验证规则为 (正规表示式模式, 讯息) 的元组列表
VALIDATION_RULES = [
(
r"\bgrep\b(?!.*\|)",
"建议使用 'rg' (ripgrep) 而非 'grep',效能和功能都更好",
),
(
r"\bfind\s+\S+\s+-name\b",
"建议使用 'rg --files | rg pattern' 或 'rg --files -g pattern' 而非 'find -name',效能更好",
),
]
def validate_command(command: str) -> list[str]:
issues = []
for pattern, message in VALIDATION_RULES:
if re.search(pattern, command):
issues.append(message)
return issues
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
print(f"错误: 无效的 JSON 输入: {e}", file=sys.stderr)
sys.exit(1)
tool_name = input_data.get("tool_name", "")
tool_input = input_data.get("tool_input", {})
command = tool_input.get("command", "")
if tool_name != "Bash" or not command:
sys.exit(1)
# 验证命令
issues = validate_command(command)
if issues:
for message in issues:
print(f"• {message}", file=sys.stderr)
# 退出代码 2 阻挡工具执行并将 stderr 传递给 Claude
sys.exit(2)设定:
- 事件:
UserPromptSubmit - Hook 命令:
python /path/to/prompt-validator.py
脚本内容 (prompt-validator.py):
#!/usr/bin/env python3
import json
import sys
import re
import datetime
# 从 stdin 载入输入
try:
input_data = json.load(sys.stdin)
except json.JSONDecodeError as e:
print(f"错误: 无效的 JSON 输入: {e}", file=sys.stderr)
sys.exit(1)
prompt = input_data.get("prompt", "")
# 检查敏感模式
sensitive_patterns = [
(r"(?i)\b(password|secret|key|token)\s*[:=]", "提示包含潜在的机密资讯"),
]
for pattern, message in sensitive_patterns:
if re.search(pattern, prompt):
# 使用 JSON 输出阻挡并提供特定原因
output = {
"decision": "block",
"reason": f"安全政策违规: {message}。请重新表述您的请求,不要包含敏感资讯。"
}
print(json.dumps(output))
sys.exit(0)
# 添加当前时间到上下文
context = f"当前时间: {datetime.datetime.now()}"
print(context)
# 允许提示继续处理,并包含额外的上下文
sys.exit(0)在重要档案被修改前自动建立备份。
设定:
- 事件:
PreToolUse - 匹配器:
Edit|MultiEdit|Write - Hook 命令:
file_path=$(jq -r '.tool_input.file_path // ""')
# 需要备份的重要档案模式
important_patterns=("config" "settings" ".json" ".yaml" ".yml" "Dockerfile" "Makefile")
should_backup=false
for pattern in "${important_patterns[@]}"; do
if [[ "$file_path" == *"$pattern"* ]]; then
should_backup=true
break
fi
done
if [ "$should_backup" = true ] && [ -f "$file_path" ]; then
backup_dir="$(dirname "$file_path")/.claude-backups"
mkdir -p "$backup_dir"
timestamp=$(date +"%Y%m%d_%H%M%S")
backup_file="$backup_dir/$(basename "$file_path").backup.$timestamp"
cp "$file_path" "$backup_file"
echo "💾 已建立备份: $backup_file"
fi- 执行超时: 预设 60 秒执行限制,可针对个别命令设定
timeout参数- 个别命令的超时不会影响其他命令
- 并行执行: 所有匹配的 Hooks 会并行执行
- 执行环境: 在当前目录中执行,使用 Claude Code 的环境变数
- 输入方式: 透过 stdin 接收 JSON 资料
- 输出处理:
PreToolUse/PostToolUse/Stop: 进度显示在 transcript 模式 (Ctrl-R)Notification: 仅记录在除错日志中 (--debug)
对于 PreToolUse 和 PostToolUse 事件:
matcher是必要的,用于指定要监控的工具- 支援精确字串匹配:
"Write"只匹配 Write 工具 - 支援正规表示式:
"Edit|Write"或"Notebook.*" - 大小写敏感
- 如果省略或为空字串,则匹配所有工具
对于其他事件:
UserPromptSubmit,Notification,Stop,SubagentStop,PreCompact不使用 matcher- 可以省略
matcher栏位或将其设为空字串
以下是可以在 PreToolUse 和 PostToolUse 中使用的常见工具名称:
Task- 代理任务Bash- Shell 命令Glob- 档案模式匹配Grep- 内容搜寻Read- 档案读取Edit,MultiEdit- 档案编辑Write- 档案写入WebFetch,WebSearch- 网路操作
Claude Code 支援 MCP (Model Context Protocol) 工具,您也可以为这些工具设定 Hooks。MCP 工具遵循 mcp__<server>__<tool> 的命名模式。
范例:监控记忆体操作
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__memory__.*",
"hooks": [
{
"type": "command",
"command": "echo 'Memory operation: ' $(jq -r '.tool_name') >> ~/.claude/memory-log.txt"
}
]
}
]
}
}对于复杂的逻辑,建议建立独立的脚本档案:
建立脚本档案 (.claude/hooks/security_check.js):
#!/usr/bin/env node
async function main() {
const chunks = [];
for await (const chunk of process.stdin) {
chunks.push(chunk);
}
const toolData = JSON.parse(Buffer.concat(chunks).toString());
const command = toolData.tool_input?.command || "";
// 复杂的安全检查逻辑
const dangerousPatterns = [
/rm\s+-rf\s+\//, // 删除根目录
/sudo\s+rm/, // sudo 删除
/chmod\s+777/, // 过于宽松的权限
];
for (const pattern of dangerousPatterns) {
if (pattern.test(command)) {
console.error(`🚫 检测到危险命令模式: ${command}`);
process.exit(2);
}
}
console.log(`✅ 安全检查通过`);
process.exit(0);
}
main().catch(console.error);在 Hook 中使用脚本:
# 方式一:使用 node 命令执行(推荐)
node .claude/hooks/security_check.js
# 方式二:直接执行(需要 shebang 行和执行权限)
# 确保脚本第一行包含: #!/usr/bin/env node
# 并设定执行权限: chmod +x .claude/hooks/security_check.js
.claude/hooks/security_check.js风险自负: Claude Code Hooks 会在您的系统上自动执行任意 shell 命令。使用 Hooks 表示您确认:
- 您对配置的命令承担全部责任
- Hooks 可以修改、删除或存取您的使用者帐户能存取的任何档案
- 恶意或编写不当的 Hooks 可能导致资料遗失或系统损害
- Anthropic 不提供任何保证,并且不对 Hook 使用导致的任何损害承担责任
- 您应该在生产环境使用前,在安全环境中彻底测试 Hooks
在添加到您的配置之前,请务必审查并理解任何 Hook 命令。
- 最小权限原则: 只授予 Hook 完成任务所需的最小权限
- 输入验证: 始终验证来自 Claude 的输入资料
- 避免命令注入: 如果使用来自 Claude 的资料建构命令,请正确转义
- 定期审查: 定期检查您的 Hook 设定和执行日志
- 使用绝对路径: 在脚本中尽量使用绝对路径指定命令,避免 PATH 被劫持
- 避开敏感档案: 设定规则以跳过
.env,.git/, SSH 金钥等档案
# 安全地处理档案路径
file_path=$(jq -r '.tool_input.file_path // ""' | sed 's/[^a-zA-Z0-9._/-]//g')
# 防止路径遍历攻击
if [[ "$file_path" == *".."* ]]; then
echo "❌ 不允许路径遍历操作"
exit 2
fi# 检查档案是否在允许的目录中
allowed_dirs=("/home/user/projects" "/tmp")
file_path=$(jq -r '.tool_input.file_path // ""')
is_allowed=false
for dir in "${allowed_dirs[@]}"; do
if [[ "$file_path" == "$dir"* ]]; then
is_allowed=true
break
fi
done
if [ "$is_allowed" = false ]; then
echo "❌ 档案路径不在允许的目录中: $file_path"
exit 2
fi当您载入包含专案级别 Hooks 的专案时,Claude Code 会显示警告并要求您确认。这是为了防止恶意设定档。
对设定档的直接编辑不会立即生效。Claude Code 采用以下安全措施:
- 启动时快照: Claude Code 在启动时撷取 Hooks 设定的快照
- 全程使用快照: 整个对话期间使用该快照
- 外部变更警告: 如果 Hooks 设定档被外部修改,Claude Code 会发出警告
- 需要审查确认: 变更后的 Hooks 需要在
/hooks管理介面中审查后才能生效
这可以防止恶意 Hook 修改在您目前的对话期间生效。
请务必:
- 仔细检查
.claude/settings.json中的所有 Hook 命令 - 理解每个命令的作用,特别是那些您不熟悉的
- 在沙盒环境中测试未知的 Hook 配置
- 不要盲目信任来自网路的专案设定
可能原因及解决方法:
-
事件类型或匹配器错误
# 检查设定档语法 cat ~/.claude/settings.json | jq .
-
命令路径问题
# 使用绝对路径 /usr/bin/echo "test" >> ~/.claude/debug.log
-
权限问题
# 检查脚本权限 chmod +x .claude/hooks/your-script.sh
在 Hook 中加入调试输出:
# 在命令开头加入
set -x # 打开 shell 的详细执行日志
echo "Hook executed at $(date)" >> ~/.claude/hook-debug.log
echo "Input: $(cat)" >> ~/.claude/hook-debug.log
set +x # 关闭详细日志检查 Claude 传递的资料:
# 将完整输入保存到档案
cat > /tmp/claude-hook-input-$(date +%s).json安全的 JSON 处理:
# 检查 JSON 是否有效
if echo "$input" | jq . > /dev/null 2>&1; then
# JSON 有效,继续处理
command=$(echo "$input" | jq -r '.tool_input.command // ""')
else
echo "❌ 无效的 JSON 输入" >&2
exit 1
fi如果您的 Hooks 无法正常运作,请依序检查:
- 检查配置 - 执行
/hooks查看您的 Hook 是否已注册 - 验证语法 - 确保 JSON 设定有效
- 测试命令 - 先手动执行 Hook 命令
- 检查权限 - 确保脚本档案具有执行权限
- 查看日志 - 使用
claude --debug查看详细的 Hook 执行资讯
常见问题:
- 引号未跳脱 - JSON 字串中使用
\" - 匹配器错误 - 检查工具名称是否精确匹配(大小写敏感)
- 命令找不到 - 使用脚本的完整路径
使用 --debug 标志启动 Claude Code 以查看详细的日志输出:
claude --debug您将会看到类似以下的日志:
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 60000ms
[DEBUG] Hook command completed with status 0: <Your stdout>
进度讯息会出现在 transcript 模式 (Ctrl-R) 中,显示:
- 正在执行哪个 Hook
- 执行的命令
- 成功/失败状态
- 输出或错误讯息
对于复杂的 Hook 问题:
- 检查 Hook 执行 - 使用
claude --debug查看详细的 Hook 执行过程 - 验证 JSON Schema - 使用外部工具测试 Hook 输入/输出
- 检查环境变数 - 验证 Claude Code 的环境是否正确
- 测试边缘情况 - 尝试 Hook 处理异常档案路径或输入
- 监控系统资源 - 检查 Hook 执行期间是否有资源耗尽
- 使用结构化记录 - 在 Hook 脚本中实作日志记录
# 建立全域监控 Hook
echo 'echo "$(date): Hook executed" >> ~/.claude/all-hooks.log' > ~/.claude/hooks/monitor.sh
chmod +x ~/.claude/hooks/monitor.sh- 从简单开始: 先实作基本的日志记录,再逐步加入复杂功能
- 分层设定: 通用规则放在使用者设定,专案特定规则放在专案设定
- 充分测试: 在安全环境中测试所有 Hook 再部署到生产环境
- 详细注释: 在设定档中为每个 Hook 添加说明注释
- 定期维护: 定期检查和更新 Hook 设定,移除不需要的规则
- 过度复杂化: 避免在单一 Hook 中包含过多逻辑
- 忽略错误处理: 确保 Hook 能够妥善处理异常情况
- 硬编码路径: 使用相对路径或环境变数而非绝对路径
- 忽略效能: 避免在 Hook 中执行耗时操作
- 盲目信任: 不要无条件信任来自外部的 Hook 设定
-
条件执行: 使用环境变数控制 Hook 行为
if [ "$CLAUDE_ENV" = "production" ]; then # 生产环境专用逻辑 fi
-
并行处理: 对于独立的操作,可以使用背景执行
long_running_task & echo "Task started in background"
-
状态追踪: 使用暂存档案追踪 Hook 状态
echo "$(date): Hook started" > /tmp/claude-hook-status
-
整合外部服务: 透过 API 与外部服务整合
curl -X POST "https://api.slack.com/..." -d "text=Claude completed task"
Claude Code Hooks 是一个强大的自动化工具,能够显著提升您的开发效率和程式码品质。透过合理的设定和使用,您可以:
- 🎯 自动化重复性任务: 格式化程式码、执行测试、产生文件
- 🛡️ 增强安全性: 防止危险操作、保护敏感档案
- 📊 提升可见度: 记录操作历史、监控系统状态
- 🔄 优化工作流程: 整合现有工具链、自订通知机制
记住,Hooks 的真正价值在于让您专注于创造性的工作,而将重复性的、规则化的任务交给自动化系统处理。
开始您的 Claude Code Hooks 之旅,让 AI 助手成为您开发团队中最可靠的成员!