name: claude-hook-writer description: 专家指导,用于编写安全、可靠、高性能的 Claude Code 钩子 - 验证设计决策,强制执行最佳实践,防止常见陷阱。在创建、审查或调试 Claude Code 钩子时使用。 metadata: version: 2.0.0 author: Claude Skills Maintainers last_verified: 2025-12-17 optimization_date: 2025-12-17 token_savings: ~55% errors_prevented: 5
Claude 钩子编写器
状态: 生产就绪 版本: 2.0.0(优化渐进式披露) 最后更新: 2025-12-17
概述
提供专家指导,用于编写安全、可靠、高性能的 Claude Code 钩子。此技能验证设计决策,强制执行最佳实践,并防止常见陷阱。
何时使用此技能
- 设计新的 Claude Code 钩子
- 审查现有钩子代码
- 调试钩子失败
- 优化慢速钩子
- 保护处理敏感数据的钩子
- 将钩子发布为 PRPM 包
核心原则
1. 安全不可妥协
钩子以用户权限自动执行,可以读取、修改或删除用户可访问的任何文件。
始终验证和消毒所有输入。 钩子通过标准输入接收 JSON - 切勿盲目信任。
完整安全模式:在实现验证或保护钩子时,加载 references/security-requirements.md。
2. 可靠性优于功能
一个 99% 时间工作的钩子是一个有缺陷的钩子。边缘情况(Unicode 文件名、路径空格、缺失工具)会发生。
部署前测试边缘情况。
可靠性模式:在处理错误或边缘情况时,加载 references/reliability-performance.md。
3. 性能至关重要
钩子会阻塞操作。一个 5 秒的钩子意味着 Claude 在继续前等待 5 秒。
保持钩子快速。在后台运行繁重操作。
性能优化:在优化钩子速度时,加载 references/reliability-performance.md。
4. 优雅失败
缺失依赖项、错误输入和磁盘错误会发生。
显式处理错误。记录失败。返回有意义的退出代码。
钩子设计检查清单
在编写代码前,回答这些问题:
此钩子针对什么事件?
PreToolUse- 在工具执行前(修改输入、验证、阻止)PostToolUse- 在工具完成后(格式化、记录、清理)UserPromptSubmit- 在用户输入处理前(验证、增强)SessionStart- 当 Claude Code 启动时(设置、环境检查)SessionEnd- 当 Claude Code 退出时(清理、持久化状态)Notification- 在警报期间(桌面通知、记录)Stop/SubagentStop- 当响应完成时(清理、总结)PreCompact- 在上下文压缩前(保存重要上下文)
常见错误:使用 PostToolUse 进行验证(太晚 - 工具已运行)。使用 PreToolUse 阻止操作。
哪些工具应触发此钩子?
要具体。matcher: "*" 在每个工具调用上运行。
好匹配器:
"Write"- 仅文件写入"Edit|Write"- 文件修改"Bash"- 外壳命令"mcp__github__*"- 所有 GitHub MCP 工具
坏匹配器:
"*"- 所有(仅用于记录/指标)
此钩子需要什么输入?
不同工具提供不同输入。检查可用内容:
# PreToolUse / PostToolUse
{
"input": {
"file_path": "/path/to/file.ts", // 读、写、编辑
"command": "npm test", // Bash
"old_string": "...", // 编辑
"new_string": "..." // 编辑
}
}
在使用前验证字段存在:
FILE=$(echo "$INPUT" | jq -r '.input.file_path // empty')
if [[ -z "$FILE" ]]; then
echo "未提供文件路径" >&2
exit 1
fi
这应是命令钩子还是提示钩子?
命令钩子(type: "command"):
- 快速(毫秒级)
- 确定性的
- 适用于:格式化、记录、文件检查
提示钩子(type: "prompt"):
- 慢速(2-10 秒)
- 上下文感知(使用 LLM)
- 适用于:复杂验证、安全分析、意图检测
经验法则:除非需要 LLM 推理,否则使用命令钩子。
什么退出代码表示成功/失败?
exit 0- 成功(继续操作)exit 2- 阻止操作(向 Claude 显示错误)exit 1或其他 - 非阻止错误(记录但继续)
对于 PreToolUse 钩子:
- 退出 2 阻止工具运行
- 退出 0 允许它(可选修改输入)
对于 PostToolUse 钩子:
- 退出代码不阻止(工具已运行)
- 使用退出 0 表示成功,1 记录错误
前 5 个陷阱(必须知道)
陷阱 #1:未引用变量
错误:钩子在带空格或特殊字符的文件名上中断
原因:未引用的变量在空白处拆分
示例:
# ❌ 错误 - 在 "my file.txt" 上中断
cat $FILE
prettier --write $FILE
rm $FILE
# ✅ 正确 - 处理空格和特殊字符
cat "$FILE"
prettier --write "$FILE"
rm "$FILE"
为何重要:带空格("my file.txt")、Unicode("文件.txt")或特殊字符("file (1).txt")的文件很常见。
引用最佳实践:加载 references/security-requirements.md 以获取全面输入处理模式。
陷阱 #2:未验证输入就信任
错误:钩子在恶意或错误输入上执行
原因:在使用前未验证 JSON 字段
示例:
# ❌ 危险 - 无验证
FILE=$(jq -r '.input.file_path')
rm "$FILE" # 可能删除 ../../../etc/passwd
# ✅ 安全 - 先验证
FILE=$(jq -r '.input.file_path // empty')
[[ -n "$FILE" ]] || exit 1
[[ "$FILE" == "$CLAUDE_PROJECT_DIR"* ]] || exit 2
[[ "$FILE" != *".."* ]] || exit 2
rm "$FILE"
为何重要:防止路径遍历攻击,保护项目外文件,防止错误输入崩溃。
完整安全模式:加载 references/security-requirements.md。
陷阱 #3:阻塞操作过长时间
错误:钩子花费 30+ 秒,阻塞 Claude
原因:在钩子中同步运行昂贵操作(测试、构建)
示例:
# ❌ 阻塞 Claude 30 秒
npm test
npm run build
# ✅ 在后台运行 - 立即返回
(npm test > /tmp/test-results.log 2>&1 &)
(npm run build > /tmp/build.log 2>&1 &)
exit 0
为何重要:慢速钩子造成不良用户体验。目标 < 100ms 用于 PreToolUse,< 500ms 用于 PostToolUse。
性能优化:加载 references/reliability-performance.md。
陷阱 #4:用于阻止的退出代码错误
错误:PreToolUse 钩子未实际阻止操作
原因:使用退出 1 而非退出 2
示例:
# ❌ 错误 - 记录错误但不阻止
if [[ $FILE == ".env" ]]; then
echo "不要编辑 .env" >&2
exit 1 # 工具仍运行!
fi
# ✅ 正确 - 实际阻止
if [[ $FILE == ".env" ]]; then
echo "已阻止:.env 受保护" >&2
exit 2 # 工具被阻止
fi
为何重要:退出 1 仅记录错误。退出 2 是 PreToolUse 钩子中阻止所必需的。
退出代码模式:加载 references/hook-templates.md 以获取完整钩子响应模式。
陷阱 #5:假设工具存在
错误:钩子在依赖缺失时崩溃
原因:在使用前未检查工具是否安装
示例:
# ❌ 如果 prettier 未安装则中断
prettier --write "$FILE"
# ✅ 安全 - 先检查
if command -v prettier &>/dev/null; then
prettier --write "$FILE"
else
echo "prettier 未安装,跳过" >&2
exit 0 # 成功退出,仅跳过
fi
为何重要:用户可能未安装所有工具。钩子应优雅降级。
可靠性模式:加载 references/reliability-performance.md。
关键规则
始终做
✅ 在使用前验证所有 JSON 输入(jq -r '... // empty')
✅ 引用所有包含路径或用户输入的变量
✅ 对脚本使用绝对路径(${CLAUDE_PLUGIN_ROOT}/...)
✅ 阻止敏感文件(.env, *.key, 凭证)
✅ 检查所需工具是否存在(command -v toolname)
✅ 设置合理超时(< 5s 用于 PreToolUse)
✅ 在后台运行繁重操作
✅ 测试边缘情况(空格、Unicode、特殊字符)
✅ 在 PreToolUse 钩子中使用退出 2 阻止
✅ 将错误记录到标准错误或文件,而非标准输出
永不
❌ 不验证就信任 JSON 输入
❌ 使用未引用的变量($FILE 而非 “$FILE”)
❌ 对脚本使用相对路径
❌ 跳过路径消毒(检查 ..,验证在项目中)
❌ 假设工具已安装
❌ 在 PreToolUse 钩子中阻塞 > 1 秒
❌ 使用退出 1 当你想阻止时(使用退出 2)
❌ 将敏感数据记录到标准输出或文件
❌ 使用 matcher: "*" 除非绝对必要
何时加载参考
在处理特定钩子方面时加载参考文件:
安全要求(references/security-requirements.md)
加载当:
- 实现输入验证和消毒
- 保护处理敏感数据的钩子
- 阻止敏感文件(
.env, 密钥, 凭证) - 防止路径遍历攻击
- 理解安全漏洞和最佳实践
- 测试安全与恶意输入
可靠性与性能(references/reliability-performance.md)
加载当:
- 处理缺失依赖项或工具
- 设置超时和处理慢速操作
- 优化钩子性能(< 100ms 目标)
- 在后台运行繁重操作
- 缓存昂贵结果
- 测试边缘情况(Unicode, 空格, 深路径)
- 去重昂贵操作
代码模板(references/code-templates.md)
加载当:
- 启动新钩子并需要工作示例
- 实现保存时格式化功能
- 阻止敏感文件修改
- 记录命令或操作
- 使用基于提示的安全分析
- 为特定用例定制模板
测试与调试(references/testing-debugging.md)
加载当:
- 为钩子编写测试用例
- 调试钩子失败或意外行为
- 测试边缘情况(错误 JSON, 缺失字段)
- 在记录中检查钩子执行(Ctrl-R)
- 分析钩子性能
- 创建自动化测试套件
发布指南(references/publishing-guide.md)
加载当:
- 将钩子发布到 PRPM 注册表
- 创建包清单(prpm.json)
- 配置 hook.json 与高级选项
- 使用
continue,stopReason,suppressOutput,systemMessage - 为用户编写 README.md
- 理解版本控制和发布命令
快速参考(references/quick-reference.md)
加载当:
- 需要快速语法查找(退出代码, jq 模式)
- 查找环境变量
- 查找常见 bash 模式(文件验证, 后台执行)
- 检查钩子事件和匹配器
- 需要性能提示摘要
- 查找 JSON 输入结构
最终检查清单
在发布钩子前:
- [ ] 使用 jq 验证所有标准输入输入
- [ ] 引用所有变量
- [ ] 对脚本使用绝对路径
- [ ] 阻止敏感文件(
.env,*.key, 等) - [ ] 优雅处理缺失工具
- [ ] 设置合理超时(< 5s 用于 PreToolUse)
- [ ] 将错误记录到标准错误或文件,而非标准输出
- [ ] 测试边缘情况(空格、Unicode、错误 JSON)
- [ ] 在真实 Claude Code 会话中测试
- [ ] 在 README 中记录依赖项
- [ ] 使用语义版本控制
- [ ] 清晰描述和标签
使用捆绑资源
此技能包括 6 个按需加载的参考文件:
安全与可靠性(2 个文件):
security-requirements.md- 输入验证、路径消毒、阻止敏感文件reliability-performance.md- 错误处理、超时、性能优化
实现(2 个文件):
code-templates.md- 工作钩子示例(保存时格式化、阻止敏感、记录器等)quick-reference.md- 快速语法查找(退出代码、jq 模式、环境变量)
测试与发布(2 个文件):
testing-debugging.md- 测试模式、边缘情况、调试技术publishing-guide.md- PRPM 打包、高级配置、README 模板
按需加载参考,当需要特定知识时。见“何时加载参考”部分的触发器。
资源
最后验证: 2025-12-17 | 版本: 2.0.0