名称: 钩子开发 描述: 此技能应在用户要求“创建钩子”、“添加预工具使用/后工具使用/停止钩子”、“验证工具使用”、“实现提示基础钩子”、“使用${CLAUDE_PLUGIN_ROOT}”、“设置事件驱动自动化”、“阻止危险命令”或提及钩子事件(预工具使用、后工具使用、停止、子代理停止、会话开始、会话结束、用户提示提交、预压缩、通知)时使用。为创建和实现Claude Code插件钩子提供全面指导,重点介绍高级提示基础钩子API。 版本: 0.1.0
Claude Code插件钩子开发
概述
钩子是事件驱动的自动化脚本,响应Claude Code事件执行。使用钩子来验证操作、执行策略、添加上下文,并将外部工具集成到工作流中。
关键能力:
- 在执行前验证工具调用(预工具使用)
- 对工具结果做出反应(后工具使用)
- 强制执行完成标准(停止、子代理停止)
- 加载项目上下文(会话开始)
- 在开发生命周期中自动化工作流
钩子类型
提示基础钩子(推荐)
使用LLM驱动的决策进行上下文感知验证:
{
"type": "prompt",
"prompt": "评估此工具使用是否合适:$TOOL_INPUT",
"timeout": 30
}
支持的事件: 停止、子代理停止、用户提示提交、预工具使用
好处:
- 基于自然语言推理的上下文感知决策
- 无需bash脚本的灵活评估逻辑
- 更好的边缘情况处理
- 更容易维护和扩展
命令钩子
执行bash命令进行确定性检查:
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 60
}
用于:
- 快速确定性验证
- 文件系统操作
- 外部工具集成
- 性能关键检查
钩子配置格式
插件hooks.json格式
对于插件钩子在hooks/hooks.json中,使用包装器格式:
{
"description": "钩子简要说明(可选)",
"hooks": {
"PreToolUse": [...],
"Stop": [...],
"SessionStart": [...]
}
}
关键点:
description字段可选hooks字段是必需包装器,包含实际钩子事件- 这是插件特定格式
示例:
{
"description": "代码质量验证钩子",
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/validate.sh"
}
]
}
]
}
}
设置格式(直接)
对于用户设置在.claude/settings.json中,使用直接格式:
{
"PreToolUse": [...],
"Stop": [...],
"SessionStart": [...]
}
关键点:
- 无包装器 - 事件直接位于顶层
- 无描述字段
- 这是设置格式
重要: 以下示例显示钩子事件结构,用于任一格式。对于插件hooks.json,将其包装在{"hooks": {...}}中。
钩子事件
预工具使用
在任何工具运行前执行。用于批准、拒绝或修改工具调用。
示例(提示基础):
{
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "prompt",
"prompt": "验证文件写入安全性。检查:系统路径、凭据、路径遍历、敏感内容。返回'approve'或'deny'。"
}
]
}
]
}
预工具使用输出:
{
"hookSpecificOutput": {
"permissionDecision": "allow|deny|ask",
"updatedInput": {"field": "modified_value"}
},
"systemMessage": "Claude解释"
}
后工具使用
在工具完成后执行。用于对结果做出反应、提供反馈或记录。
示例:
{
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "prompt",
"prompt": "分析编辑结果的潜在问题:语法错误、安全漏洞、破坏性更改。提供反馈。"
}
]
}
]
}
输出行为:
- 退出0: stdout显示在记录中
- 退出2: stderr反馈给Claude
- systemMessage包含在上下文中
停止
在主代理考虑停止时执行。用于验证完整性。
示例:
{
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "prompt",
"prompt": "验证任务完成:测试运行、构建成功、问题已回答。返回'approve'停止或'block'带原因继续。"
}
]
}
]
}
决策输出:
{
"decision": "approve|block",
"reason": "解释",
"systemMessage": "额外上下文"
}
子代理停止
在子代理考虑停止时执行。用于确保子代理完成任务。
类似于停止钩子,但用于子代理。
用户提示提交
在用户提交提示时执行。用于添加上下文、验证或阻止提示。
示例:
{
"UserPromptSubmit": [
{
"matcher": "*",
"hooks": [
{
"type": "prompt",
"prompt": "检查提示是否需要安全指导。如果讨论认证、权限或API安全,返回相关警告。"
}
]
}
]
}
会话开始
在Claude Code会话开始时执行。用于加载上下文和设置环境。
示例:
{
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh"
}
]
}
]
}
特殊能力: 使用$CLAUDE_ENV_FILE持久化环境变量:
echo "export PROJECT_TYPE=nodejs" >> "$CLAUDE_ENV_FILE"
参见examples/load-context.sh获取完整示例。
会话结束
在会话结束时执行。用于清理、记录和状态保存。
预压缩
在上下文压缩前执行。用于添加强制信息以保存。
通知
在Claude发送通知时执行。用于对用户通知做出反应。
钩子输出格式
标准输出(所有钩子)
{
"continue": true,
"suppressOutput": false,
"systemMessage": "给Claude的消息"
}
continue: 如果false,停止处理(默认true)suppressOutput: 隐藏记录中的输出(默认false)systemMessage: 显示给Claude的消息
退出码
0- 成功(stdout显示在记录中)2- 阻塞错误(stderr反馈给Claude)- 其他 - 非阻塞错误
钩子输入格式
所有钩子通过stdin接收JSON,具有公共字段:
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/current/working/dir",
"permission_mode": "ask|allow",
"hook_event_name": "PreToolUse"
}
事件特定字段:
- 预工具使用/后工具使用:
tool_name,tool_input,tool_result - 用户提示提交:
user_prompt - 停止/子代理停止:
reason
在提示中使用$TOOL_INPUT、$TOOL_RESULT、$USER_PROMPT等访问字段。
环境变量
在所有命令钩子中可用:
$CLAUDE_PROJECT_DIR- 项目根路径$CLAUDE_PLUGIN_ROOT- 插件目录(用于可移植路径)$CLAUDE_ENV_FILE- 仅会话开始:在此持久化环境变量$CLAUDE_CODE_REMOTE- 如果在远程上下文中运行则设置
始终在钩子命令中使用${CLAUDE_PLUGIN_ROOT}以确保可移植性:
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh"
}
插件钩子配置
在插件中,在hooks/hooks.json中定义钩子:
{
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "prompt",
"prompt": "验证文件写入安全性"
}
]
}
],
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "prompt",
"prompt": "验证任务完成"
}
]
}
],
"SessionStart": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh",
"timeout": 10
}
]
}
]
}
插件钩子与用户钩子合并并并行运行。
匹配器
工具名称匹配
精确匹配:
"matcher": "Write"
多个工具:
"matcher": "Read|Write|Edit"
通配符(所有工具):
"matcher": "*"
正则表达式模式:
"matcher": "mcp__.*__delete.*" // 所有MCP删除工具
注意: 匹配器区分大小写。
常见模式
// 所有MCP工具
"matcher": "mcp__.*"
// 特定插件的MCP工具
"matcher": "mcp__plugin_asana_.*"
// 所有文件操作
"matcher": "Read|Write|Edit"
// 仅Bash命令
"matcher": "Bash"
安全最佳实践
输入验证
始终在命令钩子中验证输入:
#!/bin/bash
set -euo pipefail
input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')
# 验证工具名称格式
if [[ ! "$tool_name" =~ ^[a-zA-Z0-9_]+$ ]]; then
echo '{"decision": "deny", "reason": "无效工具名称"}' >&2
exit 2
fi
路径安全
检查路径遍历和敏感文件:
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
# 拒绝路径遍历
if [[ "$file_path" == *".."* ]]; then
echo '{"decision": "deny", "reason": "检测到路径遍历"}' >&2
exit 2
fi
# 拒绝敏感文件
if [[ "$file_path" == *".env"* ]]; then
echo '{"decision": "deny", "reason": "敏感文件"}' >&2
exit 2
fi
参见examples/validate-write.sh和examples/validate-bash.sh获取完整示例。
引用所有变量
# 好:引用
echo "$file_path"
cd "$CLAUDE_PROJECT_DIR"
# 坏:未引用(注入风险)
echo $file_path
cd $CLAUDE_PROJECT_DIR
设置适当超时
{
"type": "command",
"command": "bash script.sh",
"timeout": 10
}
默认值: 命令钩子(60秒),提示钩子(30秒)
性能考虑
并行执行
所有匹配钩子并行运行:
{
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{"type": "command", "command": "check1.sh"}, // 并行
{"type": "command", "command": "check2.sh"}, // 并行
{"type": "prompt", "prompt": "验证..."} // 并行
]
}
]
}
设计影响:
- 钩子看不到彼此的输入
- 非确定性排序
- 设计为独立
优化
- 使用命令钩子进行快速确定性检查
- 使用提示钩子进行复杂推理
- 在临时文件中缓存验证结果
- 在热路径中最小化I/O
临时活动钩子
通过检查标志文件或配置创建条件激活的钩子:
模式:标志文件激活
#!/bin/bash
# 仅当标志文件存在时激活
FLAG_FILE="$CLAUDE_PROJECT_DIR/.enable-strict-validation"
if [ ! -f "$FLAG_FILE" ]; then
# 标志不存在,跳过验证
exit 0
fi
# 标志存在,运行验证
input=$(cat)
# ... 验证逻辑 ...
模式:基于配置的激活
#!/bin/bash
# 检查配置以激活
CONFIG_FILE="$CLAUDE_PROJECT_DIR/.claude/plugin-config.json"
if [ -f "$CONFIG_FILE" ]; then
enabled=$(jq -r '.strictMode // false' "$CONFIG_FILE")
if [ "$enabled" != "true" ]; then
exit 0 # 未启用,跳过
fi
fi
# 启用,运行钩子逻辑
input=$(cat)
# ... 钩子逻辑 ...
用例:
- 仅在需要时启用严格验证
- 临时调试钩子
- 项目特定钩子行为
- 钩子的功能标志
最佳实践: 在插件README中记录激活机制,以便用户知道如何启用/禁用临时钩子。
钩子生命周期和限制
钩子在会话开始时加载
重要: 钩子在Claude Code会话开始时加载。更改钩子配置需要重启Claude Code。
无法热交换钩子:
- 编辑
hooks/hooks.json不会影响当前会话 - 添加新钩子脚本不会被识别
- 更改钩子命令/提示不会更新
- 必须重启Claude Code:退出并再次运行
claude
测试钩子更改:
- 编辑钩子配置或脚本
- 退出Claude Code会话
- 重启:
claude或cc - 新钩子配置加载
- 使用
claude --debug测试钩子
启动时的钩子验证
钩子在Claude Code启动时验证:
- hooks.json中的无效JSON导致加载失败
- 缺少脚本导致警告
- 调试模式中报告语法错误
使用/hooks命令查看当前会话中加载的钩子。
调试钩子
启用调试模式
claude --debug
查看钩子注册、执行日志、输入/输出JSON和计时信息。
测试钩子脚本
直接测试命令钩子:
echo '{"tool_name": "Write", "tool_input": {"file_path": "/test"}}' | \
bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh
echo "退出码:$?"
验证JSON输出
确保钩子输出有效JSON:
output=$(./your-hook.sh < test-input.json)
echo "$output" | jq .
快速参考
钩子事件摘要
| 事件 | 何时 | 用途 |
|---|---|---|
| 预工具使用 | 工具前 | 验证、修改 |
| 后工具使用 | 工具后 | 反馈、记录 |
| 用户提示提交 | 用户输入 | 上下文、验证 |
| 停止 | 代理停止 | 完整性检查 |
| 子代理停止 | 子代理完成 | 任务验证 |
| 会话开始 | 会话开始 | 上下文加载 |
| 会话结束 | 会话结束 | 清理、记录 |
| 预压缩 | 压缩前 | 保存上下文 |
| 通知 | 用户通知 | 记录、反应 |
最佳实践
做:
- ✅ 使用提示基础钩子进行复杂逻辑
- ✅ 使用${CLAUDE_PLUGIN_ROOT}确保可移植性
- ✅ 在命令钩子中验证所有输入
- ✅ 引用所有bash变量
- ✅ 设置适当超时
- ✅ 返回结构化JSON输出
- ✅ 彻底测试钩子
不做:
- ❌ 使用硬编码路径
- ❌ 未经验证信任用户输入
- ❌ 创建长时间运行钩子
- ❌ 依赖钩子执行顺序
- ❌ 不可预测地修改全局状态
- ❌ 记录敏感信息
额外资源
参考文件
有关详细模式和高级技术,请参阅:
references/patterns.md- 常见钩子模式(8+已验证模式)references/migration.md- 从基础钩子迁移到高级钩子references/advanced.md- 高级用例和技术
示例钩子脚本
examples/中的工作示例:
validate-write.sh- 文件写入验证示例validate-bash.sh- Bash命令验证示例load-context.sh- 会话开始上下文加载示例
实用脚本
scripts/中的开发工具:
validate-hook-schema.sh- 验证hooks.json结构和语法test-hook.sh- 部署前使用样本输入测试钩子hook-linter.sh- 检查钩子脚本的常见问题和最佳实践
外部资源
- 官方文档: https://docs.claude.com/en/docs/claude-code/hooks
- 示例: 查看市场中的security-guidance插件
- 测试: 使用
claude --debug获取详细日志 - 验证: 使用
jq验证钩子JSON输出
实施工作流
在插件中实施钩子:
- 识别要挂钩的事件(预工具使用、停止、会话开始等)
- 决定使用提示基础(灵活)或命令(确定性)钩子
- 在
hooks/hooks.json中编写钩子配置 - 对于命令钩子,创建钩子脚本
- 对所有文件引用使用${CLAUDE_PLUGIN_ROOT}
- 使用
scripts/validate-hook-schema.sh hooks/hooks.json验证配置 - 部署前使用
scripts/test-hook.sh测试钩子 - 使用
claude --debug在Claude Code中测试 - 在插件README中记录钩子
关注提示基础钩子用于大多数用例。保留命令钩子用于性能关键或确定性检查。