关于 hook-development
介绍 Claude Code 插件中事件驱动 Hook 的开发,说明事件类型、配置格式、提示词与命令 Hook、输入输出、安全及调试方法。
主要功能
- 01Hook 事件及用途
- 02插件与设置配置格式
- 03提示词和命令 Hook
- 04Hook 输入与输出结构
- 05安全与调试指南
适用场景
- 01验证或限制工具操作
- 02文件变更后运行测试
- 03在会话开始时加载项目上下文
- 04检查 Hook 配置和输出
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ josiahsiegel/ claude-plugin-marketplace --skill hook-development安装后在 Claude Code 中输入/hook-development确认已加载。
详细介绍
Claude Code 插件的 Hook 开发
概述
Hook 是事件驱动的自动化机制,会在 Claude Code 事件发生时执行。使用 Hook 可验证操作、强制执行策略、加载上下文并集成外部工具。
两种 Hook 类型:
- 基于提示词(推荐):由大模型驱动,可根据上下文作出决策
- 基于命令:使用 Shell 命令进行快速、确定性检查
Hook 事件参考
| 事件 | 触发时机 | 常见用途 |
|---|---|---|
| PreToolUse | 工具执行前 | 验证、批准/拒绝、修改输入 |
| PostToolUse | 工具执行后 | 测试、Lint、记录日志、提供反馈 |
| Stop | 主 Agent 停止时 | 验证任务是否完成 |
| SubagentStop | 子 Agent 停止时 | 验证子 Agent 的工作 |
| UserPromptSubmit | 用户发送提示词时 | 添加上下文、验证、预处理 |
| SessionStart | 会话开始时 | 加载上下文、设置环境 |
| SessionEnd | 会话结束时 | 清理、记录日志 |
| PreCompact | 上下文压缩前 | 保留关键信息 |
| Notification | 显示通知时 | 自定义提醒响应 |
配置格式
插件 hooks.json(位于 hooks/hooks.json)
使用带有 hooks 字段的包装格式:
{
"description": "What these hooks do (optional)",
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 10
}
]
}
]
}
}
用户设置格式(位于 .claude/settings.json)
直接格式,不使用包装结构:
{
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [{ "type": "command", "command": "script.sh" }]
}
]
}
**关键区别:**插件 hooks.json 会将事件包裹在 {"hooks": {...}} 中。设置格式则将事件放在顶层。
基于提示词的 Hook(推荐)
使用大模型推理,根据上下文作出决策:
{
"type": "prompt",
"prompt": "Evaluate if this tool use is appropriate. Check for: system paths, credentials, path traversal. Return 'approve' or 'deny'.",
"timeout": 30
}
**支持的事件:**PreToolUse、PostToolUse、Stop、SubagentStop、UserPromptSubmit
**优势:**能结合上下文、灵活、能更好地处理边缘情况、更易维护。
命令 Hook
执行 Shell 命令进行确定性检查:
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 60
}
始终使用 ${CLAUDE_PLUGIN_ROOT},以确保路径可移植。
退出码
| 代码 | 含义 |
|---|---|
| 0 | 成功(stdout 显示在记录中) |
| 2 | 阻止性错误(stderr 会反馈给 Claude) |
| 其他 | 非阻止性错误 |
匹配器
控制哪些工具会触发 Hook:
"matcher": "Write" // Exact match
"matcher": "Write|Edit|Bash" // Multiple tools
"matcher": "mcp__.*__delete.*" // Regex (all MCP delete tools)
"matcher": "*" // All tools (use sparingly)
匹配器区分大小写。
Hook 输入/输出
输入(所有 Hook 均通过 stdin 接收)
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/current/working/dir",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": { "file_path": "/path/to/file" }
}
特定事件字段:tool_name、tool_input、tool_result、user_prompt、reason
在提示词中访问:$TOOL_INPUT、$TOOL_RESULT、$USER_PROMPT
输出
标准格式(所有 Hook):
{
"continue": true,
"suppressOutput": false,
"systemMessage": "Message for Claude"
}
PreToolUse 决策:
{
"hookSpecificOutput": {
"permissionDecision": "allow|deny|ask",
"updatedInput": { "field": "modified_value" }
}
}
Stop/SubagentStop 决策:
{
"decision": "approve|block",
"reason": "Explanation"
}
环境变量
| 变量 | 适用范围 | 用途 |
|---|---|---|
$CLAUDE_PLUGIN_ROOT | 所有 Hook | 插件目录(用于可移植路径) |
$CLAUDE_PROJECT_DIR | 所有 Hook | 项目根目录路径 |
$CLAUDE_ENV_FILE | 仅限 SessionStart | 为会话持久化环境变量 |
SessionStart 可以持久化变量:
echo "export PROJECT_TYPE=nodejs" >> "$CLAUDE_ENV_FILE"
常见模式
验证文件写入(PreToolUse)
{
"PreToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "prompt",
"prompt": "Check if this file write is safe. Deny writes to: .env, credentials, system paths, or files with path traversal (..). Return 'approve' or 'deny' with reason."
}]
}]
}
修改后自动测试(PostToolUse)
{
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "npm test -- --bail",
"timeout": 60
}]
}]
}
验证任务完成情况(Stop)
{
"Stop": [{
"matcher": "*",
"hooks": [{
"type": "prompt",
"prompt": "Verify: tests run, build succeeded, all questions answered. Return 'approve' to stop or 'block' with reason to continue."
}]
}]
}
加载项目上下文(SessionStart)
{
"SessionStart": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh",
"timeout": 10
}]
}]
}
安全最佳实践
在命令 Hook 脚本中:
#!/bin/bash
set -euo pipefail
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
# Always validate inputs
if [[ ! "$file_path" =~ ^[a-zA-Z0-9_./-]+$ ]]; then
echo '{"decision": "deny", "reason": "Invalid path"}' >&2
exit 2
fi
# Block path traversal
if [[ "$file_path" == *".."* ]]; then
echo '{"decision": "deny", "reason": "Path traversal detected"}' >&2
exit 2
fi
# Block sensitive files
if [[ "$file_path" == *".env"* ]]; then
echo '{"decision": "deny", "reason": "Sensitive file"}' >&2
exit 2
fi
# Always quote variables
echo "$file_path"
生命周期和限制
**Hook 会在会话开始时加载。**修改 Hook 配置后,必须重启 Claude Code 才能生效。
- 编辑
hooks/hooks.json不会影响当前会话 - 新增 Hook 脚本后,必须重启才能识别
- 所有匹配的 Hook 都会并行运行(而非顺序运行)
- Hook 无法看到彼此的输出,因此应独立设计
**测试更改:**退出 Claude Code,然后使用 claude 或 claude --debug 重启。
调试
# Enable debug mode to see hook execution
claude --debug
# Test hook scripts directly
echo '{"tool_name": "Write", "tool_input": {"file_path": "/test"}}' | \
bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh
# Validate hook JSON output
output=$(./hook-script.sh < test-input.json)
echo "$output" | jq .
# View loaded hooks in session
# Use /hooks command
验证清单
- hooks.json 使用正确的格式(插件封装或直接使用设置)
- 所有脚本路径均使用
${CLAUDE_PLUGIN_ROOT}(不使用硬编码路径) - 脚本可执行且能处理错误(
set -euo pipefail) - 脚本会验证所有输入,并为所有变量加引号
- 匹配器设置具体(除非必要,否则避免使用
*) - 超时时间设置合理(默认值:命令 60s,提示词 30s)
- 钩子输出为有效 JSON
- 已使用
claude --debug进行测试