mcpField
H
Hook Development

Hook Development

github.com/ovachieverAgent 工作流5641 次安装48 周前更新
代码仓库

关于 Hook Development

面向 Claude Code 插件开发的技能,介绍用于验证、策略执行、上下文加载和工作流自动化的事件钩子。内容涵盖提示词钩子与命令钩子、事件配置、输入输出、安全、性能和调试。

主要功能

  1. 01提示词钩子与命令钩子
  2. 02事件与匹配器配置
  3. 03钩子输入和输出格式
  4. 04安全与性能指导
  5. 05钩子测试和调试

适用场景

  1. 01执行前验证工具调用
  2. 02工具使用后检查结果
  3. 03会话开始时加载项目上下文
  4. 04停止前检查任务是否完成

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/ovachiever/droid-tings --skill hook-development

安装后在 Claude Code 中输入/hook-development确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

examples/
validate-write.sh
load-context.sh
validate-bash.sh
references/
patterns.md
migration.md
advanced.md
scripts/
README.md
test-hook.sh
hook-linter.sh
validate-hook-schema.sh

详细介绍

Claude Code 插件的 Hook 开发

概述

Hook 是事件驱动的自动化脚本,会在 Claude Code 事件触发时执行。使用 Hook 可验证操作、强制执行策略、添加上下文,并将外部工具集成到工作流中。

主要功能:

  • 在执行前验证工具调用(PreToolUse)
  • 对工具结果作出响应(PostToolUse)
  • 强制执行完成标准(Stop、SubagentStop)
  • 加载项目上下文(SessionStart)
  • 自动化开发生命周期中的工作流

Hook 类型

基于提示词的 Hook(推荐)

使用由大模型驱动的决策,实现基于上下文的验证:

{
  "type": "prompt",
  "prompt": "Evaluate if this tool use is appropriate: $TOOL_INPUT",
  "timeout": 30
}

支持的事件: Stop、SubagentStop、UserPromptSubmit、PreToolUse

优势:

  • 基于自然语言推理作出上下文感知的决策
  • 无需编写 bash 脚本即可实现灵活的评估逻辑
  • 更好地处理边缘情况
  • 更易维护和扩展
命令 Hook

执行 bash 命令进行确定性检查:

{
  "type": "command",
  "command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
  "timeout": 60
}

适用场景:

  • 快速的确定性验证
  • 文件系统操作
  • 外部工具集成
  • 对性能要求较高的检查

Hook 配置格式

插件 hooks.json 格式

对于插件 Hook,在 hooks/hooks.json 中使用包装格式:

{
  "description": "Brief explanation of hooks (optional)",
  "hooks": {
    "PreToolUse": [...],
    "Stop": [...],
    "SessionStart": [...]
  }
}

要点:

  • description 字段为可选
  • hooks 字段为必填包装字段,其中包含实际的 Hook 事件
  • 这是插件专用格式

示例:

{
  "description": "Validation hooks for code quality",
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/hooks/validate.sh"
          }
        ]
      }
    ]
  }
}
设置格式(直接)

对于用户设置,在 .claude/settings.json 中使用直接格式:

{
  "PreToolUse": [...],
  "Stop": [...],
  "SessionStart": [...]
}

要点:

  • 无包装层——事件直接位于顶层
  • 无 description 字段
  • 这是设置格式

重要说明: 以下示例展示的是可放入任一格式中的 Hook 事件结构。对于插件 hooks.json,请将其包装在 {"hooks": {...}} 中。

Hook 事件

PreToolUse

在任何工具运行前执行。用于批准、拒绝或修改工具调用。

示例(基于提示词):

{
  "PreToolUse": [
    {
      "matcher": "Write|Edit",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Validate file write safety. Check: system paths, credentials, path traversal, sensitive content. Return 'approve' or 'deny'."
        }
      ]
    }
  ]
}

PreToolUse 的输出:

{
  "hookSpecificOutput": {
    "permissionDecision": "allow|deny|ask",
    "updatedInput": {"field": "modified_value"}
  },
  "systemMessage": "Explanation for Claude"
}
PostToolUse

在工具完成后执行。用于对结果作出响应、提供反馈或记录日志。

示例:

{
  "PostToolUse": [
    {
      "matcher": "Edit",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Analyze edit result for potential issues: syntax errors, security vulnerabilities, breaking changes. Provide feedback."
        }
      ]
    }
  ]
}

输出行为:

  • 退出码为 0:stdout 显示在对话记录中
  • 退出码为 2:stderr 反馈给 Claude
  • systemMessage 会包含在上下文中
Stop

主 Agent 考虑停止时执行。用于验证任务是否完整。

示例:

{
  "Stop": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Verify task completion: tests run, build succeeded, questions answered. Return 'approve' to stop or 'block' with reason to continue."
        }
      ]
    }
  ]
}

决策输出:

{
  "decision": "approve|block",
  "reason": "Explanation",
  "systemMessage": "Additional context"
}
SubagentStop

子 Agent 考虑停止时执行。用于确保子 Agent 已完成任务。

与 Stop Hook 类似,但适用于子 Agent。

UserPromptSubmit

用户提交提示词时执行。用于添加上下文、验证提示词或阻止提示词。

示例:

{
  "UserPromptSubmit": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Check if prompt requires security guidance. If discussing auth, permissions, or API security, return relevant warnings."
        }
      ]
    }
  ]
}
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 输出格式

标准输出(所有 Hook)
{
  "continue": true,
  "suppressOutput": false,
  "systemMessage": "Message for Claude"
}
  • continue:若为 false,则停止处理(默认值为 true)
  • suppressOutput:隐藏对话记录中的输出(默认值为 false)
  • systemMessage:显示给 Claude 的消息
退出码
  • 0 - 成功(stdout 显示在对话记录中)
  • 2 - 阻止性错误(stderr 反馈给 Claude)
  • 其他 - 非阻止性错误

Hook 输入格式

所有 Hook 都会通过 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 等访问字段。

环境变量

所有命令钩子均可使用:

  • $CLAUDE_PROJECT_DIR - 项目根目录路径
  • $CLAUDE_PLUGIN_ROOT - 插件目录(用于可移植路径)
  • $CLAUDE_ENV_FILE - 仅限 SessionStart:在此处持久化环境变量
  • $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": "Validate file write safety"
        }
      ]
    }
  ],
  "Stop": [
    {
      "matcher": "*",
      "hooks": [
        {
          "type": "prompt",
          "prompt": "Verify task completion"
        }
      ]
    }
  ],
  "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.*"  // All MCP delete tools

注意: 匹配器区分大小写。

常见模式
// All MCP tools
"matcher": "mcp__.*"

// Specific plugin's MCP tools
"matcher": "mcp__plugin_asana_.*"

// All file operations
"matcher": "Read|Write|Edit"

// Bash commands only
"matcher": "Bash"

安全最佳实践

输入验证

始终在命令钩子中验证输入:

#!/bin/bash
set -euo pipefail

input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')

# Validate tool name format
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')

# Deny path traversal
if [[ "$file_path" == *".."* ]]; then
  echo '{"decision": "deny", "reason": "Path traversal detected"}' >&2
  exit 2
fi

# Deny sensitive files
if [[ "$file_path" == *".env"* ]]; then
  echo '{"decision": "deny", "reason": "Sensitive file"}' >&2
  exit 2
fi

完整示例请参见 examples/validate-write.sh 和 examples/validate-bash.sh。

为所有变量加引号
# GOOD: Quoted
echo "$file_path"
cd "$CLAUDE_PROJECT_DIR"

# BAD: Unquoted (injection risk)
echo $file_path
cd $CLAUDE_PROJECT_DIR
设置适当的超时时间
{
  "type": "command",
  "command": "bash script.sh",
  "timeout": 10
}

默认值: 命令钩子(60s)、提示词钩子(30s)

性能注意事项

并行执行

所有匹配的钩子都会并行运行:

{
  "PreToolUse": [
    {
      "matcher": "Write",
      "hooks": [
        {"type": "command", "command": "check1.sh"},  // Parallel
        {"type": "command", "command": "check2.sh"},  // Parallel
        {"type": "prompt", "prompt": "Validate..."}   // Parallel
      ]
    }
  ]
}

设计影响:

  • 钩子无法看到彼此的输出
  • 执行顺序不确定
  • 应按彼此独立的方式设计
优化
  1. 使用命令钩子执行快速、确定性的检查
  2. 使用提示词钩子处理复杂推理
  3. 将验证结果缓存到临时文件中
  4. 减少热点路径中的 I/O

暂时启用的钩子

创建可通过检查标记文件或配置来有条件激活的钩子:

模式:通过标记文件激活

#!/bin/bash
# Only active when flag file exists
FLAG_FILE="$CLAUDE_PROJECT_DIR/.enable-strict-validation"

if [ ! -f "$FLAG_FILE" ]; then
  # Flag not present, skip validation
  exit 0
fi

# Flag present, run validation
input=$(cat)
# ... validation logic ...

模式:通过配置激活

#!/bin/bash
# Check configuration for activation
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  # Not enabled, skip
  fi
fi

# Enabled, run hook logic
input=$(cat)
# ... hook logic ...

使用场景:

  • 仅在需要时启用严格验证
  • 临时调试钩子
  • 项目专属的钩子行为
  • 钩子的功能标志

最佳实践: 在插件 README 中说明激活机制,让用户知道如何启用或禁用临时钩子。

钩子生命周期与限制

钩子在会话开始时加载

重要: 钩子会在 Claude Code 会话启动时加载。更改钩子配置后,必须重启 Claude Code。

无法热切换钩子:

  • 编辑 hooks/hooks.json 不会影响当前会话
  • 新增的钩子脚本不会被识别
  • 更改钩子命令或提示词不会生效
  • 必须重启 Claude Code:退出后再次运行 claude

测试钩子更改:

  1. 编辑钩子配置或脚本
  2. 退出 Claude Code 会话
  3. 重启:claude 或 cc
  4. 加载新的钩子配置
  5. 使用 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 "Exit code: $?"
验证 JSON 输出

确保钩子输出有效的 JSON:

output=$(./your-hook.sh < test-input.json)
echo "$output" | jq .

快速参考

钩子事件摘要
事件时机用途
PreToolUse工具调用前验证、修改
PostToolUse工具调用后反馈、日志记录
UserPromptSubmit用户输入时上下文、验证
StopAgent 停止时完整性检查
SubagentStop子 Agent 完成时任务验证
SessionStart会话开始时加载上下文
SessionEnd会话结束时清理、日志记录
PreCompact压缩前保留上下文
Notification通知用户时日志记录、响应
最佳实践

应该:

  • ✅ 复杂逻辑使用基于提示词的钩子
  • ✅ 使用 ${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 - SessionStart 上下文加载示例
实用脚本

script/ 中的开发工具:

  • validate-hook-schema.sh - 验证 hooks.json 结构和语法
  • test-hook.sh - 部署前使用示例输入测试钩子
  • hook-linter.sh - 检查钩子脚本中的常见问题并验证其是否符合最佳实践
外部资源

实施工作流

在插件中实现钩子的步骤:

  1. 确定要接入的事件(PreToolUse、Stop、SessionStart 等)
  2. 选择基于提示词(灵活)或命令(确定性)钩子
  3. 在 hooks/hooks.json 中编写钩子配置
  4. 对于命令钩子,创建钩子脚本
  5. 所有文件引用均使用 ${CLAUDE_PLUGIN_ROOT}
  6. 使用 scripts/validate-hook-schema.sh hooks/hooks.json 验证配置
  7. 部署前使用 scripts/test-hook.sh 测试钩子
  8. 在 Claude Code 中使用 claude --debug 进行测试
  9. 在插件 README 中记录钩子

大多数用例应优先使用基于提示词的钩子。仅在性能要求严格或需要确定性检查时使用命令钩子。

评论

支持 Markdown