mcpField
H
Hook Development

Hook Development

github.com/anthropicsAgent 工作流153.7k14.5k 次安装1 天前更新
代码仓库

关于 Hook Development

Hook Development 面向 Claude Code 插件开发者,介绍事件驱动的自动化钩子。内容说明如何验证工具调用、补充上下文及检查任务完成情况。

主要功能

  1. 01响应工具和会话事件
  2. 02使用提示词型或命令型钩子
  3. 03配置事件匹配器和输出
  4. 04验证输入并限制路径
  5. 05测试和调试钩子行为

适用场景

  1. 01在执行前检查文件写入操作
  2. 02在会话开始时加载项目上下文
  3. 03在 Agent 停止前检查任务是否完成

安装

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

$npx skills add https://github.com/anthropics/claude-code --skill hook-development

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

包含的文件

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

examples/
load-context.sh
validate-bash.sh
validate-write.sh
references/
advanced.md
migration.md
patterns.md
scripts/
hook-linter.sh
README.md
test-hook.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 上下文加载示例
实用脚本

/scripts/ 中的开发工具:

  • validate-hook-schema.sh - 验证 hooks.json 的结构和语法
  • test-hook.sh - 部署前使用示例输入测试钩子
  • hook-linter.sh - 检查钩子脚本中的常见问题和最佳实践
外部资源
  • 官方文档:https://docs.claude.com/en/docs/claude-code/hooks
  • 示例:参阅 claude-plugins-official marketplace 中的 security-guidance 插件
  • 测试:使用 claude --debug 查看详细日志
  • 验证:使用 jq 验证钩子的 JSON 输出

实施工作流

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

  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 --debug 在 Claude Code 中测试
  9. 在插件 README 中记录钩子

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

评论

支持 Markdown