ClaudeCode钩子开发参考Skill hook-developer

这是一个完整的Claude Code钩子开发参考技能,提供钩子类型、输入输出模式、注册方法和测试模式等详细信息,帮助开发者创建和调试钩子,适用于自动化脚本和AI辅助编程环境。关键词:Claude Code, 钩子开发, 输入输出模式, 自动化, AI编程辅助, DevOps, 软件开发。

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

名称: 钩子-开发者 描述: Claude Code钩子完整参考 - 输入/输出模式、注册、测试模式

钩子开发者

Claude Code钩子开发的完整参考。使用此参考编写具有正确输入/输出模式的钩子。

何时使用

  • 创建新钩子
  • 调试钩子输入/输出格式
  • 理解可用字段
  • 在settings.json中设置钩子注册
  • 学习哪些钩子可以阻止或注入上下文

快速参考

钩子 触发时机 可以阻止? 主要用途
PreToolUse 工具执行前 阻止/修改工具调用
PostToolUse 工具完成后 部分 响应工具结果
UserPromptSubmit 用户提交提示时 验证/注入上下文
PermissionRequest 权限对话框显示时 自动批准/拒绝
SessionStart 会话开始时 加载上下文、设置环境变量
SessionEnd 会话结束时 清理/保存状态
Stop 代理完成时 强制继续
SubagentStart 子代理生成时 模式协调
SubagentStop 子代理完成时 强制继续
PreCompact 压缩前 保存状态
Notification 通知发送时 自定义警报

钩子类型选项: type: "command" (bash) 或 type: "prompt" (LLM评估)


钩子输入/输出模式

PreToolUse

目的: 在工具执行前阻止或修改。

输入:

{
  "session_id": "字符串",
  "transcript_path": "字符串",
  "cwd": "字符串",
  "permission_mode": "default|plan|acceptEdits|bypassPermissions",
  "hook_event_name": "PreToolUse",
  "tool_name": "字符串",
  "tool_input": {
    "file_path": "字符串",
    "command": "字符串"
  },
  "tool_use_id": "字符串"
}

输出 (JSON):

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow|deny|ask",
    "permissionDecisionReason": "字符串",
    "updatedInput": {}
  },
  "continue": true,
  "stopReason": "字符串",
  "systemMessage": "字符串",
  "suppressOutput": true
}

退出码 2: 阻止工具,stderr显示给Claude。

常见匹配器: Bash, Edit|Write, Read, Task, mcp__.*


PostToolUse

目的: 响应工具执行结果,向Claude提供反馈。

输入:

{
  "session_id": "字符串",
  "transcript_path": "字符串",
  "cwd": "字符串",
  "permission_mode": "字符串",
  "hook_event_name": "PostToolUse",
  "tool_name": "字符串",
  "tool_input": {},
  "tool_response": {
    "filePath": "字符串",
    "success": true,
    "output": "字符串",
    "exitCode": 0
  },
  "tool_use_id": "字符串"
}

关键: 响应字段是 tool_response,不是 tool_result

输出 (JSON):

{
  "decision": "block",
  "reason": "字符串",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "字符串"
  },
  "continue": true,
  "stopReason": "字符串",
  "suppressOutput": true
}

阻止: "decision": "block""reason" 提示Claude解决问题。

常见匹配器: Edit|Write, Bash


UserPromptSubmit

目的: 验证用户提示,在Claude处理前注入上下文。

输入:

{
  "session_id": "字符串",
  "transcript_path": "字符串",
  "cwd": "字符串",
  "permission_mode": "字符串",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "字符串"
}

输出 (纯文本):

任何stdout文本添加为Claude的上下文。

输出 (JSON):

{
  "decision": "block",
  "reason": "字符串",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "字符串"
  }
}

阻止: "decision": "block" 擦除提示,仅向用户显示 "reason" (不给Claude)。

退出码 2: 阻止提示,仅向用户显示stderr。


PermissionRequest

目的: 自动化权限对话框决策。

输入:

{
  "session_id": "字符串",
  "transcript_path": "字符串",
  "cwd": "字符串",
  "permission_mode": "字符串",
  "hook_event_name": "PermissionRequest",
  "tool_name": "字符串",
  "tool_input": {}
}

输出:

{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow|deny",
      "updatedInput": {},
      "message": "字符串",
      "interrupt": false
    }
  }
}

SessionStart

目的: 初始化会话,加载上下文,设置环境变量。

输入:

{
  "session_id": "字符串",
  "transcript_path": "字符串",
  "cwd": "字符串",
  "permission_mode": "字符串",
  "hook_event_name": "SessionStart",
  "source": "startup|resume|clear|compact"
}

环境变量: CLAUDE_ENV_FILE - 写入 export VAR=value 以持久化环境变量。

输出 (纯文本或JSON):

{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "字符串"
  },
  "suppressOutput": true
}

纯文本stdout添加为上下文。


SessionEnd

目的: 清理、保存状态、记录会话。

输入:

{
  "session_id": "字符串",
  "transcript_path": "字符串",
  "cwd": "字符串",
  "permission_mode": "字符串",
  "hook_event_name": "SessionEnd",
  "reason": "clear|logout|prompt_input_exit|other"
}

输出: 无法影响会话 (已结束)。仅用于清理。


Stop

目的: 控制Claude停止时机,强制继续。

输入:

{
  "session_id": "字符串",
  "transcript_path": "字符串",
  "cwd": "字符串",
  "permission_mode": "字符串",
  "hook_event_name": "Stop",
  "stop_hook_active": false
}

关键: 检查 stop_hook_active: true 以防止无限循环!

输出:

{
  "decision": "block",
  "reason": "字符串"
}

阻止: "decision": "block" 强制Claude继续,以 "reason" 作为提示。


SubagentStart

目的: 当子代理 (Task工具) 生成时运行。

输入:

{
  "session_id": "字符串",
  "transcript_path": "字符串",
  "cwd": "字符串",
  "permission_mode": "字符串",
  "hook_event_name": "SubagentStart",
  "agent_id": "字符串"
}

输出: 仅上下文注入 (不能阻止)。


SubagentStop

目的: 控制子代理 (Task工具) 停止时机。

输入:

{
  "session_id": "字符串",
  "transcript_path": "字符串",
  "cwd": "字符串",
  "permission_mode": "字符串",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false
}

输出: 与Stop相同。


PreCompact

目的: 在上下文压缩前保存状态。

输入:

{
  "session_id": "字符串",
  "transcript_path": "字符串",
  "cwd": "字符串",
  "permission_mode": "字符串",
  "hook_event_name": "PreCompact",
  "trigger": "manual|auto",
  "custom_instructions": "字符串"
}

匹配器: manual, auto

输出:

{
  "continue": true,
  "systemMessage": "字符串"
}

Notification

目的: 自定义通知处理。

输入:

{
  "session_id": "字符串",
  "transcript_path": "字符串",
  "cwd": "字符串",
  "permission_mode": "字符串",
  "hook_event_name": "Notification",
  "message": "字符串",
  "notification_type": "permission_prompt|idle_prompt|auth_success|elicitation_dialog"
}

匹配器: permission_prompt, idle_prompt, auth_success, elicitation_dialog, *

输出:

{
  "continue": true,
  "suppressOutput": true,
  "systemMessage": "字符串"
}

settings.json中的注册

标准结构

{
  "hooks": {
    "EventName": [
      {
        "matcher": "ToolPattern",
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.sh",
            "timeout": 60
          }
        ]
      }
    ]
  }
}

匹配器模式

模式 匹配
Bash 精确匹配Bash工具
Edit|Write 匹配Edit或Write
Read.* 正则表达式: Read*
mcp__.*__write.* MCP写工具
* 所有工具

区分大小写: Bashbash

需要匹配器的事件

  • PreToolUse - 是 (必需)
  • PostToolUse - 是 (必需)
  • PermissionRequest - 是 (必需)
  • Notification - 是 (可选)
  • SessionStart - 是 (startup|resume|clear|compact)
  • PreCompact - 是 (manual|auto)

无匹配器的事件

{
  "hooks": {
    "UserPromptSubmit": [
      {
        "hooks": [{ "type": "command", "command": "/path/to/hook.sh" }]
      }
    ]
  }
}

钩子类型

命令钩子 (type: “command”)

默认类型。执行bash命令或脚本。

{
  "type": "command",
  "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.sh",
  "timeout": 60
}

基于提示的钩子 (type: “prompt”)

使用LLM (Haiku) 进行上下文感知决策。最适合Stop/SubagentStop。

{
  "type": "prompt",
  "prompt": "评估Claude是否应该停止。上下文: $ARGUMENTS。检查所有任务是否完成。",
  "timeout": 30
}

响应模式:

{
  "decision": "approve" | "block",
  "reason": "解释",
  "continue": false,
  "stopReason": "给用户的消息",
  "systemMessage": "警告"
}

MCP工具命名

MCP工具使用模式 mcp__<server>__<tool>:

模式 匹配
mcp__memory__.* 所有memory服务器工具
mcp__.*__write.* 所有MCP写工具
mcp__github__.* 所有GitHub工具

环境变量

所有钩子可用

变量 描述
CLAUDE_PROJECT_DIR 项目根目录的绝对路径
CLAUDE_CODE_REMOTE 如果远程/网络则为"true",本地CLI则为空

仅SessionStart

变量 描述
CLAUDE_ENV_FILE 写入 export VAR=value 行的路径

仅插件钩子

变量 描述
CLAUDE_PLUGIN_ROOT 插件目录的绝对路径

退出码

退出码 行为 stdout stderr
0 成功 JSON处理 忽略
2 阻止错误 忽略 错误消息
其他 非阻止错误 忽略 详细模式

各钩子的退出码2

钩子 效果
PreToolUse 阻止工具,stderr给Claude
PostToolUse stderr给Claude (工具已运行)
UserPromptSubmit 阻止提示,仅stderr给用户
Stop 阻止停止,stderr给Claude

Shell包装模式

#!/bin/bash
set -e
cd "$CLAUDE_PROJECT_DIR/.claude/hooks"
cat | npx tsx src/my-hook.ts

或用于打包:

#!/bin/bash
set -e
cd "$HOME/.claude/hooks"
cat | node dist/my-hook.mjs

TypeScript处理程序模式

import { readFileSync } from 'fs';

interface HookInput {
  session_id: string;
  hook_event_name: string;
  tool_name?: string;
  tool_input?: Record<string, unknown>;
  tool_response?: Record<string, unknown>;
  // ... 其他字段按钩子类型
}

function readStdin(): string {
  return readFileSync(0, 'utf-8');
}

async function main() {
  const input: HookInput = JSON.parse(readStdin());

  // 处理输入

  const output = {
    decision: 'block',  // 或undefined以允许
    reason: '阻止原因'
  };

  console.log(JSON.stringify(output));
}

main().catch(console.error);

测试钩子

手动测试命令

# PostToolUse (Write)
echo '{"tool_name":"Write","tool_input":{"file_path":"test.md"},"tool_response":{"success":true},"session_id":"test"}' | \
  .claude/hooks/my-hook.sh

# PreToolUse (Bash)
echo '{"tool_name":"Bash","tool_input":{"command":"ls"},"session_id":"test"}' | \
  .claude/hooks/my-hook.sh

# SessionStart
echo '{"hook_event_name":"SessionStart","source":"startup","session_id":"test"}' | \
  .claude/hooks/session-start.sh

# SessionEnd
echo '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"test"}' | \
  .claude/hooks/session-end.sh

# UserPromptSubmit
echo '{"prompt":"测试提示","session_id":"test"}' | \
  .claude/hooks/prompt-submit.sh

TypeScript编辑后重建

cd .claude/hooks
npx esbuild src/my-hook.ts \
  --bundle --platform=node --format=esm \
  --outfile=dist/my-hook.mjs

常见模式

阻止危险文件 (PreToolUse)

#!/usr/bin/env python3
import json, sys

data = json.load(sys.stdin)
path = data.get('tool_input', {}).get('file_path', '')

BLOCKED = ['.env', 'secrets.json', '.git/']
if any(b in path for b in BLOCKED):
    print(json.dumps({
        "hookSpecificOutput": {
            "hookEventName": "PreToolUse",
            "permissionDecision": "deny",
            "permissionDecisionReason": f"阻止: {path} 受保护"
        }
    }))
else:
    print('{}')

自动格式化文件 (PostToolUse)

#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [[ "$FILE" == *.ts ]] || [[ "$FILE" == *.tsx ]]; then
  npx prettier --write "$FILE" 2>/dev/null
fi

echo '{}'

注入Git上下文 (UserPromptSubmit)

#!/bin/bash
echo "Git状态:"
git status --short 2>/dev/null || echo "(不是git仓库)"
echo ""
echo "最近提交:"
git log --oneline -5 2>/dev/null || echo "(无提交)"

强制测试验证 (Stop)

#!/usr/bin/env python3
import json, sys, subprocess

data = json.load(sys.stdin)

# 防止无限循环
if data.get('stop_hook_active'):
    print('{}')
    sys.exit(0)

# 检查测试是否通过
result = subprocess.run(['npm', 'test'], capture_output=True)
if result.returncode != 0:
    print(json.dumps({
        "decision": "block",
        "reason": "测试失败。请在停止前修复。"
    }))
else:
    print('{}')

调试清单

  • [ ] 钩子在settings.json中注册?
  • [ ] Shell脚本有 +x 权限?
  • [ ] TypeScript更改后重建打包?
  • [ ] 使用 tool_response 而不是 tool_result
  • [ ] 输出是有效的JSON (或纯文本)?
  • [ ] Stop钩子中检查 stop_hook_active
  • [ ] 使用 $CLAUDE_PROJECT_DIR 作为路径?

过往会话的关键学习点

  1. 字段名称重要 - tool_response 而不是 tool_result
  2. 输出格式 - decision: "block" + reason 用于阻止
  3. 退出码2 - stderr给Claude/用户,stdout忽略
  4. 重建打包 - TypeScript源代码编辑不会自动应用
  5. 手动测试 - 依赖前先 echo '{}' | ./hook.sh
  6. 先检查输出 - 编辑代码前先 ls .claude/cache/
  7. 分离生成隐藏错误 - 添加日志以调试

另见

  • /debug-hooks - 系统化调试工作流
  • .claude/rules/hooks.md - 钩子开发规则