钩子开发Skill hook-development

这个技能用于开发和实施Claude Code插件的钩子,实现事件驱动的自动化,包括工具使用前验证、后处理、会话管理等,适用于软件开发中的自动化流程和集成需求,关键词包括钩子开发、Claude Code、插件、事件驱动、自动化、验证。

DevOps 0 次安装 0 次浏览 更新于 3/16/2026

name: hook-development description: 当用户询问“创建钩子”、“添加PreToolUse/PostToolUse/Stop钩子”、“验证工具使用”、“实施基于提示的钩子”、“使用${CLAUDE_PLUGIN_ROOT}”、“设置事件驱动的自动化”、“阻止危险命令”,或提到钩子事件(PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification)时,应使用此技能。提供创建和实施Claude Code插件钩子的全面指导,重点是基于提示的钩子API。

Claude Code插件的钩子开发

概述

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

关键能力:

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

钩子类型

基于提示的钩子(推荐)

使用LLM驱动的决策进行上下文感知验证:

{
  "type": "prompt",
  "prompt": "评估此工具使用是否合适:$TOOL_INPUT",
  "timeout": 30
}

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

优点:

  • 基于自然语言推理的上下文感知决策
  • 无需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": [...]
}

关键点:

  • 无包装 - 事件直接在顶层
  • 无description字段
  • 这是设置格式

重要: 以下示例显示钩子事件结构,可放入任一格式。对于插件hooks.json,将这些包装在{"hooks": {...}}中。

钩子事件

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钩子,但用于子代理。

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发送通知时执行。用于对用户通知做出反应。

钩子输出格式

标准输出(所有钩子)

{
  "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"
}

事件特定字段:

  • 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": "验证文件写入安全性"
        }
      ]
    }
  ],
  "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.shexamples/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": "验证..."}   // 并行
      ]
    }
  ]
}

设计影响:

  • 钩子看不到彼此的输出
  • 非确定性顺序
  • 设计为独立

优化

  1. 使用命令钩子进行快速确定性检查
  2. 使用提示钩子进行复杂推理
  3. 在临时文件中缓存验证结果
  4. 在热路径中最小化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

测试钩子更改:

  1. 编辑钩子配置或脚本
  2. 退出Claude Code会话
  3. 重启:claudecc
  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 "退出代码:$?"

验证JSON输出

确保钩子输出有效JSON:

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

快速参考

钩子事件摘要

事件 何时 用于
PreToolUse 工具前 验证、修改
PostToolUse 工具后 反馈、记录
UserPromptSubmit 用户输入 上下文、验证
Stop 代理停止 完整性检查
SubagentStop 子代理完成 任务验证
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 - 检查钩子脚本的常见问题和最佳实践

外部资源

实施工作流

在插件中实施钩子:

  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中记录钩子

专注于基于提示的钩子用于大多数使用案例。保留命令钩子用于性能关键或确定性检查。