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