关于 hook-authoring
为 Claude Code 和 Claude Agent SDK 编写生命周期钩子的指南型技能,涵盖声明式 JSON 配置与 Python 实现。内容还涉及事件处理、安全和性能注意事项。
主要功能
- 01Claude Code JSON 钩子配置
- 02使用 Claude Agent SDK 编写 Python 钩子
- 03生命周期事件处理
- 04输入验证与输出处理
- 05安全与性能指导
适用场景
- 01执行前验证工具输入
- 02记录或转换工具输出
- 03注入上下文或过滤提交的提示词
- 04上下文压缩前保存状态
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ athola/ claude-night-market --skill hook-authoring安装后在 Claude Code 中输入/hook-authoring确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
modules/hook-types.mdobservability-warnings.mdperformance-guidelines.mdscope-selection.mdsdk-callbacks.mdtesting-hooks.mdscripts/README.md详细介绍
不应使用的情况
- 审核已存在的钩子(使用
abstract:hooks-eval) - 选择钩子应放在哪里(使用
abstract:hook-scope-guide) - 编写技能而非钩子(使用
abstract:skill-authoring)
钩子编写指南
概述
钩子是事件拦截器,可在 Agent 生命周期的特定节点执行自定义逻辑,从而扩展 Claude Code 和 Claude Agent SDK 的行为。它们支持在使用工具前进行验证、在操作后记录日志、注入上下文、自动化工作流以及执行安全策略。
本技能将教你如何为声明式 JSON(Claude Code)和编程式 Python(Claude Agent SDK)用例编写有效、安全且高性能的钩子。
主要功能
- PreToolUse:在执行前验证、筛选或转换工具输入;注入上下文(2.1.9+)
- PostToolUse:在执行后记录、分析或修改工具输出
- UserPromptSubmit:在处理用户消息前注入上下文或筛选消息
- Stop/SubagentStop:清理、最终报告或结果汇总
- TeammateIdle/TaskCompleted:多 Agent 协调与编排(2.1.33+)
- PreCompact:在上下文窗口压缩前保留状态
2.1.9 新增:PreToolUse 钩子现在可以返回
additionalContext,以便在工具执行前注入信息。这支持缓存提示、安全警告或相关上下文注入等模式。
快速开始
你的第一个钩子(JSON - Claude Code)
在 .claude/settings.json 中创建一个简单的日志钩子:
{
"PostToolUse": [
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "echo \"$(date): Executed $(jq -r '.tool_name')\" >> ~/.claude/audit.log"
}]
}
]
}
注意:使用字符串匹配器("Bash"),而非对象匹配器({"toolName": "Bash"})。
**验证:**使用 --help 标志运行命令,以验证其是否可用。
此钩子会记录每次 Bash 命令执行及其时间戳。
你的第一个钩子(Python - Claude Agent SDK)
使用 SDK 创建一个验证钩子:
from claude_agent_sdk import AgentHooks
class ValidationHooks(AgentHooks):
async def on_pre_tool_use(self, tool_name: str, tool_input: dict) -> dict | None:
"""Validate tool inputs before execution."""
if tool_name == "Bash":
command = tool_input.get("command", "")
if "rm -rf /" in command:
raise ValueError("Dangerous command blocked by hook")
# Return None to proceed unchanged, or modified dict to transform
return None
**验证:**使用 --help 标志运行命令,以验证其是否可用。
钩子事件类型
所有受支持钩子事件的快速参考:
| 事件 | 触发点 | 参数 | 常见用途 |
|---|---|---|---|
| PreToolUse | 工具执行前 | tool_name、tool_input | 验证、筛选、输入转换 |
| PostToolUse | 工具执行后 | tool_name、tool_input、tool_output | 日志记录、指标、输出转换 |
| UserPromptSubmit | 用户发送消息时 | message | 上下文注入、内容筛选 |
| PermissionRequest | 显示权限对话框时 | tool_name、tool_input | 使用自定义逻辑自动批准或拒绝 |
| Notification | Claude Code 发送通知时 | message | 自定义通知处理 |
| Stop | Agent 完成时 | reason、result | 最终清理、摘要报告 |
| SubagentStop | 子 Agent 完成时 | subagent_id、result | 结果处理、汇总 |
| TeammateIdle | 队友 Agent 进入空闲状态时 | agent_id、session_id | 任务分配、负载均衡(2.1.33+) |
| TaskCompleted | 任务执行完成时 | task_id、result | 协调、链接、报告(2.1.33+) |
| PreCompact | 上下文压缩前 | context_size | 状态保留、检查点 |
| SessionStart | 会话开始或恢复时 | session_id、source、agent_type | 初始化、加载上下文 |
| SessionEnd | 会话终止时 | session_id | 清理、最终日志记录 |
| WorktreeCreate | 创建 Agent 工作树时 | worktree_path、session_id | 自定义 VCS 设置、软链接 .venv、预填充缓存(2.1.50+) |
| WorktreeRemove | 移除 Agent 工作树时 | worktree_path、session_id | 清理临时文件、拆除工作树范围内的资源(2.1.50+) |
SessionStart 输入架构(Claude Code 2.1.2+)
SessionStart 钩子通过 stdin 接收 JSON 输入,其中包含以下字段:
{
"session_id": "abc123",
"source": "startup",
"agent_type": "my-agent"
}
字段:source 的值为 "startup"、"resume"、"clear" 或 "compact"。使用 --agent 标志时会填充 agent_type。
agent_type 字段:使用 --agent my-agent 启动 Claude Code 时,此字段包含 Agent 名称,可用于 Agent 专属初始化:
# Python example: Agent-aware SessionStart hook
input_data = json.loads(sys.stdin.read())
agent_type = input_data.get("agent_type", "")
if agent_type in ["code-reviewer", "quick-query"]:
# Skip heavy context injection for lightweight agents
print(json.dumps({"hookSpecificOutput": {"additionalContext": "Minimal context"}}))
else:
# Full initialization for implementation agents
print(json.dumps({"hookSpecificOutput": {"additionalContext": full_context}}))
# Bash example: Agent-aware SessionStart hook
HOOK_INPUT=$(cat)
AGENT_TYPE=$(echo "$HOOK_INPUT" | jq -r '.agent_type // empty')
case "$AGENT_TYPE" in
code-reviewer|quick-query)
echo '{"hookSpecificOutput": {"additionalContext": "Minimal context"}}'
;;
*)
echo '{"hookSpecificOutput": {"additionalContext": "Full context"}}'
;;
esac
Frontmatter 中的钩子(Claude Code 2.1.0+)
**2.1.0 新增:**可直接在技能、命令或 Agent 的 frontmatter 中定义钩子。这些钩子的作用范围限定为相应组件的生命周期。
技能/命令/Agent Frontmatter 钩子
---
name: validated-skill
description: Skill with lifecycle hooks
hooks:
PreToolUse:
- matcher: "Bash"
command: "./validate-command.sh"
once: true # NEW: Run only once per session
- matcher: "Write|Edit"
command: "./pre-edit-check.sh"
PostToolUse:
- matcher: "Write|Edit"
command: "./format-on-save.sh"
Stop:
- command: "./cleanup-and-report.sh"
---
once: true 配置
**2.1.0 新增:**使用 once: true 可让钩子在每个会话中仅执行一次,适用于:
- 一次性设置/初始化
- 不应重复执行的资源分配
- 会话级配置
hooks:
PreToolUse:
- matcher: "Bash"
command: "./setup-environment.sh"
once: true # Runs only on first Bash call
SessionStart:
- command: "./initialize-session.sh"
once: true # Runs only once at session start
Frontmatter 钩子与设置钩子
| 方面 | Frontmatter 钩子 | 设置钩子 |
|---|---|---|
| 作用范围 | 组件生命周期 | 全局/项目 |
| 位置 | 技能/Agent/命令中 | settings.json |
| 持续时间 | 仅在组件运行时生效 | 始终生效 |
| 使用场景 | 组件专属验证 | 跨领域问题 |
PreToolUse updatedInput(2.1.0 修复)
PreToolUse 钩子现在可以在返回 ask 权限决策时返回 updatedInput,让钩子在请求用户同意的同时充当中间件:
{
"decision": "ask",
"updatedInput": {
"command": "modified-command --safe-flag"
}
}
Claude Code 与 SDK
JSON 钩子(Claude Code)
在 .claude/settings.json、项目的 .claude/settings.json 或插件的 hooks/hooks.json 中进行声明式配置:
{
"PreToolUse": [
{
"matcher": "Edit",
"hooks": [{
"type": "command",
"command": "echo 'WARNING: Editing production file' >&2"
}]
}
]
}
**重要:**使用字符串匹配器(正则表达式模式),而非对象匹配器。对象格式 {"toolName": "Edit"} 已弃用。
匹配器模式:
"Edit"- 匹配单个工具"Read|Write|Edit"- 匹配多个工具(正则表达式 OR)".*"- 匹配所有工具
**验证:**使用 --help 标志运行命令,以确认其可用性。
**优点:**简单,无需编写代码,易于进行版本控制 **缺点:**逻辑能力有限,仅支持 shell 命令
HTTP 钩子(Claude Code 2.1.63+)
**2.1.63 新增:**钩子可以向 URL 发送 JSON POST 请求并接收 JSON 响应,而不是运行 shell 命令。使用 "type": "http" 并指定 "url" 字段:
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{
"type": "http",
"url": "https://my-service.example.com/hooks/validate-bash"
}]
}
]
}
钩子会将标准钩子输入作为 JSON 发送 POST 请求,并要求响应正文为标准钩子响应 JSON。
适合使用 HTTP 钩子而非命令钩子的场景:
- 限制 shell 执行的企业环境
- 通过 Web 服务在团队间共享的集中式钩子逻辑
- 无法访问本地脚本的沙箱或容器化环境
- 与外部验证/日志服务集成
**优点:**无需本地脚本,逻辑集中,可在沙箱环境中运行 **缺点:**网络延迟,需要运行 HTTP 服务,依赖外部服务
Python SDK 钩子
使用 AgentHooks 基类实现编程式回调:
from claude_agent_sdk import AgentHooks
class MyHooks(AgentHooks):
async def on_pre_tool_use(self, tool_name: str, tool_input: dict) -> dict | None:
# Complex validation logic
if self._is_dangerous(tool_input):
raise ValueError("Operation blocked")
return None # or return modified input
**验证:**使用 --help 标志运行命令,以确认其可用性。
**优点:**充分利用 Python 功能,支持复杂逻辑和状态管理 **缺点:**需要 Python,设置较复杂
Bash 权限匹配说明
环境变量包装(2.1.38+)
现在,权限规则能够正确匹配带有环境变量赋值前缀的命令。在 2.1.38 之前,NODE_ENV=production npm test 无法匹配 Bash(npm *) 规则。
# These now all match `Bash(npm *)`:
npm test
NODE_ENV=production npm test
FORCE_COLOR=1 CI=true npm test
编写用于检查 bash 命令的 PreToolUse 钩子时请注意:权限系统会在匹配时去除环境变量前缀,但钩子接收的完整命令字符串仍包含这些前缀。
Heredoc 分隔符安全(2.1.38+)
Claude Code 现在会验证 heredoc 分隔符,以防止命令注入。推荐使用的模式 <<'EOF'(单引号括起)仍是最安全的方式。在 heredoc 模式中始终使用单引号分隔符,以防止变量展开。
安全要点
关键安全规则
- 输入验证:处理前始终验证工具输入
- 不记录机密:切勿记录 API 密钥、令牌、密码或凭据
- 注意沙箱边界:遵守沙箱边界,不要越界。注意:在沙箱模式下,
.claude/skills/为只读(2.1.38+) - 安全失败默认值:发生错误时返回 None,而不是阻塞 Agent
- 速率限制:防止恶意或有缺陷的代码滥用钩子
- 防止注入:清理所有记录的内容,防止日志注入
示例:安全日志记录钩子
import re
from claude_agent_sdk import AgentHooks
class SecureLoggingHooks(AgentHooks):
# Patterns that might contain secrets
SECRET_PATTERNS = [
r"api[_-]?key",
r"password",
r"token",
r"secret",
r"credential",
r"auth",
]
def _sanitize_output(self, text: str) -> str:
"""Remove potential secrets from log output."""
for pattern in self.SECRET_PATTERNS:
text = re.sub(
rf'({pattern}["\s:=]+)([^\s,}}]+)',
r"\1***REDACTED***",
text,
flags=re.IGNORECASE,
)
return text
async def on_post_tool_use(
self, tool_name: str, tool_input: dict, tool_output: str
) -> str | None:
"""Log tool use with sanitization."""
safe_output = self._sanitize_output(tool_output)
# Log safe_output...
return None # Don't modify output
**验证:**运行带有 --help 标志的命令,以确认其可用。
有关详细的安全指南,请参阅 modules/testing-hooks.md。
性能指南
性能最佳实践
- 非阻塞:正确使用
async/await,不要阻塞事件循环 - 超时处理:钩子超时时间为 10 分钟(在 2.1.3 中从 60s 延长)。对于大多数钩子,目标应为 < 30s;仅在 CI/CD 集成、复杂验证或调用外部 API 时使用延长后的时间
- 高效日志记录:批量写入,使用异步 I/O
- 内存管理:不要无限累积状态
- 快速失败:快速验证、尽早返回,避免开销大的操作
示例:高效钩子
import asyncio
from claude_agent_sdk import AgentHooks
class EfficientHooks(AgentHooks):
def __init__(self):
self._log_queue = asyncio.Queue()
self._log_task = None
async def on_pre_tool_use(self, tool_name: str, tool_input: dict) -> dict | None:
# Quick validation only
if not self._is_valid_input(tool_input):
raise ValueError("Invalid input")
return None
async def on_post_tool_use(
self, tool_name: str, tool_input: dict, tool_output: str
) -> str | None:
# Queue log entry without blocking
await self._log_queue.put({"tool": tool_name, "timestamp": time.time()})
return None
def _is_valid_input(self, tool_input: dict) -> bool:
"""Fast validation check."""
# Simple checks only, < 10ms
return len(str(tool_input)) < 1_000_000
**验证:**运行带有 --help 标志的命令,以确认其可用。
有关详细的优化技巧,请参阅 modules/performance-guidelines.md。
范围选择
根据受众和用途,为钩子选择合适的位置。
重要:自动加载行为
启用插件时,会自动加载
hooks/hooks.json。 不要在plugin.json中添加"hooks": "./hooks/hooks.json"——这会导致重复加载错误。 只有在需要加载标准位置以外的其他钩子文件时,才使用hooks字段。
决策框架
**Verification:** Run the command with `--help` flag to verify availability.
Is this hook part of a plugin's core functionality?
├─ YES → Plugin hooks (hooks/hooks.json in plugin)
└─ NO ↓
Should all team members on this project have this hook?
├─ YES → Project hooks (.claude/settings.json)
└─ NO ↓
Should this hook apply to all my Claude sessions?
├─ YES → Global hooks (~/.claude/settings.json)
└─ NO → Reconsider if you need a hook at all
**验证:**运行带有 --help 标志的命令,以确认其可用。
范围比较
| 范围 | 位置 | 受众 | 是否提交? | 示例用途 |
|---|---|---|---|---|
| 插件 | hooks/hooks.json | 插件用户 | 是(随插件提交) | 在 YAML 插件中验证 YAML |
| 项目 | .claude/settings.json | 团队成员 | 是(在仓库中) | 阻止编辑生产配置 |
| 全局 | ~/.claude/settings.json | 仅你自己 | 从不 | 个人审计日志记录 |
有关详细的范围决策指南,请参阅 modules/scope-selection.md。
常见模式
验证钩子
在执行前阻止危险操作:
async def on_pre_tool_use(self, tool_name: str, tool_input: dict) -> dict | None:
if tool_name == "Bash":
command = tool_input.get("command", "")
# Block dangerous patterns
if any(pattern in command for pattern in ["rm -rf /", ":(){ :|:& };:"]):
raise ValueError(f"Dangerous command blocked: {command}")
# Block production access
if "production" in command and not self._has_approval():
raise ValueError("Production access requires approval")
return None
**验证:**运行带有 --help 标志的命令,以确认其可用。
日志记录钩子
审计所有工具操作:
async def on_post_tool_use(
self, tool_name: str, tool_input: dict, tool_output: str
) -> str | None:
await self._log_entry(
{
"timestamp": datetime.now().isoformat(),
"tool": tool_name,
"input_size": len(str(tool_input)),
"output_size": len(tool_output),
"success": True,
}
)
return None
**验证:**运行带有 --help 标志的命令,以确认其可用。
上下文注入钩子
在用户提示词前添加相关上下文:
async def on_user_prompt_submit(self, message: str) -> str | None:
# Inject project-specific context
context = await self._load_project_context()
enhanced_message = f"{context}\n\n{message}"
return enhanced_message
PreToolUse 上下文注入(Claude Code 2.1.9+)
使用 additionalContext 在工具执行前注入上下文:
#!/usr/bin/env python3
"""PreToolUse hook that injects context before WebFetch."""
import json
import sys
def main():
payload = json.load(sys.stdin)
tool_name = payload.get("tool_name", "")
if tool_name == "WebFetch":
url = payload.get("tool_input", {}).get("url", "")
# Check cache or knowledge base
cached = lookup_knowledge_base(url)
if cached:
print(
json.dumps(
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"additionalContext": f"Relevant cached info: {cached}",
}
}
)
)
sys.exit(0)
if __name__ == "__main__":
main()
此模式适用于:在 Web 请求前提供缓存提示、在高风险操作前发出安全警告,以及在文件操作前注入相关项目上下文。
测试钩子
单元测试
import pytest
from my_hooks import ValidationHooks
@pytest.mark.asyncio
async def test_dangerous_command_blocked():
hooks = ValidationHooks()
with pytest.raises(ValueError, match="Dangerous command"):
await hooks.on_pre_tool_use("Bash", {"command": "rm -rf /"})
@pytest.mark.asyncio
async def test_safe_command_allowed():
hooks = ValidationHooks()
result = await hooks.on_pre_tool_use("Bash", {"command": "ls -la"})
assert result is None # Allows execution
**验证:**运行 pytest -v from 进行验证。
有关详细的测试策略,请参阅 modules/testing-hooks.md。
模块参考
有关特定主题的详细指南:
- 钩子类型:
modules/hook-types.md- 详细的事件签名和参数 - SDK 回调:
modules/sdk-callbacks.md- Python SDK 实现模式 - 安全模式:
modules/testing-hooks.md- 详细的安全指南 - 性能指南:
modules/performance-guidelines.md- 优化技巧 - 作用域选择:
modules/scope-selection.md- 选择插件/项目/全局作用域 - 钩子测试:
modules/testing-hooks.md- 测试策略和固件 - 可观测性警告:
modules/observability-warnings.md- 可复制粘贴的解决方案模式,适用于可通过二进制操作的漂移钩子
工具
- hook_validator.py:验证钩子结构和语法(位于
plugins/abstract/scripts/hook_validator.py)
相关技能
- hook-scope-guide:钩子放置决策框架(现有)
- modular-skills:技能架构设计模式
- skills-eval:质量评估和改进框架
后续步骤
- 根据复杂度需求选择钩子类型(JSON 或 SDK)
- 选择适当的作用域(插件/项目/全局)
- 遵循安全和性能最佳实践进行实现
- 使用单元测试和集成测试进行全面测试
- 部署前使用
hook_validator.py验证
环境变量(Claude Code 2.1.2+)
FORCE_AUTOUPDATE_PLUGINS
即使主 Claude Code 自动更新程序已禁用,也强制自动更新插件。
适用场景:
- 需要最新插件版本的 CI/CD 流水线
- 用于测试插件更新的开发环境
- 企业环境中受控的更新推出
# Enable forced plugin updates
export FORCE_AUTOUPDATE_PLUGINS=1
claude
# Or inline
FORCE_AUTOUPDATE_PLUGINS=1 claude --agent my-agent
注意:这只影响插件更新,不影响 Claude Code 核心更新。
参考资料
钩子退出代码
钩子通过退出代码向 Claude Code 传达决策:
| 退出代码 | 含义 | stdout | stderr |
|---|---|---|---|
| 0 | 成功/允许 | 作为系统上下文显示给 Claude | 忽略 |
| 2 | 阻止/拒绝 | 忽略 | 作为说明显示给用户(2.1.39+ 修复) |
| 其他 | 错误 | 忽略 | 作为错误消息显示给用户 |
使用退出代码 2 阻止操作(2.1.39+)
使用退出代码 2 阻止操作并向用户显示消息:
#!/bin/bash
# Example: Block force pushes with user-facing message
command=$(echo "$1" | jq -r '.tool_input.command // empty')
if echo "$command" | grep -q 'push.*--force'; then
echo "Force push blocked: use --force-with-lease instead" >&2
exit 2
fi
exit 0
重要:在 Claude Code 2.1.39 之前,退出代码 2 的 stderr 会被静默丢弃(#10964)。用户看到的会是通用的“钩子错误”,而不是自定义消息。此问题现已修复:stderr 会正确显示给用户。
插件钩子:在 2.1.39 之前,插件安装的钩子使用了单独的代码路径,同样无法显示退出代码 2 的 stderr(#10412)。现在插件钩子和项目钩子都能正常工作。
故障排查
常见问题
钩子未触发 确认钩子模式与事件匹配。检查钩子日志中是否有错误
语法错误 部署前验证 JSON/Python 语法
权限被拒绝 检查钩子文件的权限和所有权
未显示钩子阻止消息(2.1.39 之前) 如果使用退出代码 2 阻止操作并向用户显示消息,但消息未显示,请升级到 Claude Code 2.1.39+。在较旧版本中,可使用退出代码 0 并通过 stdout 输出消息作为替代方案。
退出条件
- 编写的钩子文件位于有效的作用域位置(
hooks/hooks.json、.claude/settings.json或~/.claude/settings.json),且 JSON 或 Python 语法正确。 - 钩子会在目标事件发生时触发:调用匹配的工具时,钩子命令或回调会正常运行。
- 钩子不会记录机密:日志输出路径中不会出现与
api[_-]?key、password、token、secret或credential匹配的字段名。 - 阻塞型钩子以代码 2 退出,并将面向用户的说明写入 stderr(而非 stdout)。
- 如果
abstract:validate-hook可用,它会对编写的钩子文件返回退出码 0。