关于 enhance-hooks
面向 Agent 开发者的 Claude Code Hook 审查技能,检查 Hook 配置与脚本的安全性、正确性和生命周期适配,并可应用部分修复。
主要功能
- 01发现并分类 Hook 文件
- 02验证元数据与配置
- 03检查脚本中的不安全模式
- 04审查退出码、事件类型和匹配器
- 05建议或应用部分修复
适用场景
- 01审查项目的 Hook 配置
- 02检查命令脚本中的不安全操作
- 03验证阻止行为与事件是否匹配
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agent-sh/ agentsys --skill enhance-hooks安装后在 Claude Code 中输入/enhance-hooks确认已加载。
详细介绍
enhance-hooks
分析钩子定义和脚本的安全性、正确性与最佳实践。
解析参数
const args = '$ARGUMENTS'.split(' ').filter(Boolean);
const targetPath = args.find(a => !a.startsWith('--')) || '.';
const fix = args.includes('--fix');
工作流
- 发现 - 查找钩子文件(.md、.sh、.json)
- 分类 - 确定钩子类型和事件
- 解析 - 提取 frontmatter 和脚本内容
- 检查 - 根据下方知识运行所有模式检查
- 筛选 - 应用确定性筛选
- 报告 - 生成 Markdown 输出
- 修复 - 如果存在 --fix 标志,则应用自动修复
钩子知识参考
什么是钩子
钩子是在 Claude Code 会话中特定时间点触发的自动化操作。它们通过 bash 命令或基于 LLM 的评估,实现对 Claude 操作的验证、监控和控制。
钩子生命周期(完整参考)
钩子按以下顺序触发:
| 顺序 | 事件 | 描述 | 是否需要匹配器 |
|---|---|---|---|
| 1 | SessionStart | 会话开始或恢复 | 否 |
| 2 | UserPromptSubmit | 用户提交提示词 | 否 |
| 3 | PreToolUse | 工具执行前(可修改或阻止) | 是 |
| 4 | PermissionRequest | 权限对话框出现时 | 是 |
| 5 | PostToolUse | 工具成功执行后 | 是 |
| 6 | SubagentStart | 启动子 Agent 时 | 否 |
| 7 | SubagentStop | 子 Agent 结束时 | 否 |
| 8 | Stop | Claude 完成响应时 | 否 |
| 9 | PreCompact | 压缩上下文前 | 否 |
| 10 | SessionEnd | 会话终止时 | 否 |
| 11 | Notification | Claude Code 发送通知时 | 否 |
钩子类型
命令钩子(type: "command"):
- 执行 bash 命令,并完全控制 stdin/stdout
- 适用于所有事件
提示词钩子(type: "prompt"):
- 使用 LLM 评估进行智能且上下文感知的决策
- 仅支持
Stop和SubagentStop事件
配置位置
| 文件 | 位置 | 范围 | 是否提交 |
|---|---|---|---|
| 用户设置 | ~/.claude/settings.json | 所有项目 | 否 |
| 项目设置 | .claude/settings.json | 当前项目 | 是 |
| 本地设置 | .claude/settings.local.json | 当前项目 | 否 |
配置结构
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/validate-bash.sh",
"timeout": 30
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/format-code.sh"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check if all requested tasks are complete.",
"timeout": 30
}
]
}
]
}
}
匹配器语法
| 模式 | 描述 |
|---|---|
Write | 精确匹配工具名称 |
Edit|Write | 匹配多个工具(正则 OR) |
Notebook.* | 正则模式匹配 |
* 或 "" | 匹配所有工具 |
| (省略) | Stop、SubagentStop、UserPromptSubmit 必需 |
输入架构(通过 stdin 传入的 JSON)
所有钩子都会接收以下 JSON 结构:
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript",
"cwd": "/project/root",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test",
"description": "Run test suite"
}
}
退出代码
| 退出代码 | 行为 |
|---|---|
| 0 | 成功 - stdout 显示给用户或添加为上下文 |
| 2 | 阻止性错误 - 显示 stderr,并阻止操作 |
| 其他 | 非阻止性错误 - 在详细模式下显示 stderr |
输出架构
PreToolUse 决策控制:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow|deny|ask",
"permissionDecisionReason": "Reason for decision",
"updatedInput": {
"command": "modified command"
},
"additionalContext": "Context for Claude"
}
}
Stop/SubagentStop 控制:
{
"decision": "block",
"reason": "Tasks incomplete: missing test coverage"
}
环境变量
| 变量 | 描述 | 可用范围 |
|---|---|---|
CLAUDE_PROJECT_DIR | 项目根目录的绝对路径 | 所有钩子 |
CLAUDE_CODE_REMOTE | 远程会话时为 "true" | 所有钩子 |
CLAUDE_ENV_FILE | 用于持久化环境变量的路径 | 仅 SessionStart |
CLAUDE_FILE_PATHS | 以空格分隔的文件路径 | PostToolUse(Write/Edit) |
实用钩子示例
安全防火墙(PreToolUse):
#!/usr/bin/env bash
set -euo pipefail
cmd=$(jq -r '.tool_input.command // ""')
# Block dangerous patterns
if echo "$cmd" | grep -qE 'rm -rf|git reset --hard|curl.*\|.*sh'; then
echo '{"decision": "block", "reason": "Dangerous command blocked"}' >&2
exit 2
fi
exit 0
自动格式化工具(PostToolUse):
#!/usr/bin/env bash
set -euo pipefail
files=$(jq -r '.tool_input.file_path // ""')
for file in $files; do
case "$file" in
*.py) black "$file" 2>/dev/null || true ;;
*.js|*.ts) prettier --write "$file" 2>/dev/null || true ;;
esac
done
exit 0
命令记录器(PreToolUse):
#!/usr/bin/env bash
set -euo pipefail
cmd=$(jq -r '.tool_input.command // ""')
printf '%s %s\n' "$(date -Is)" "$cmd" >> .claude/bash-commands.log
exit 0
工作流编排(SubagentStop - prompt 类型):
{
"hooks": {
"SubagentStop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Review the subagent's work. Did it complete all tasks?"
}
]
}
]
}
}
检测模式
1. Frontmatter 验证(确定性高)
必需:
- 使用
---分隔符的 YAML frontmatter - frontmatter 中包含
name字段 - frontmatter 中包含
description字段
建议:
- 为命令钩子设置
timeout(默认值:30s) - 指定钩子类型
标记:
- 缺少 frontmatter 分隔符
- 缺少 name 或 description
2. 脚本安全性(高确定性)
必需的安全模式:
- 在脚本开头使用
set -euo pipefail - 处理 jq/JSON 解析错误
- 正确引用变量
需要标记的危险模式:
| 模式 | 风险 | 确定性 |
|---|---|---|
rm -rf | 未经确认即执行破坏性操作 | 高 |
git reset --hard | 存在数据丢失风险 | 高 |
curl | sh | 远程代码执行 | 高 |
eval "$input" | 任意代码执行 | 高 |
rm -r | 递归删除(可能是有意操作) | 中 |
git push --force | 强制推送(可能是有意操作) | 中 |
3. 退出代码处理(高确定性)
检查: 脚本是否使用了正确的退出代码
标记:
- 成功路径缺少
exit 0 - 阻止操作时使用退出代码 1(应使用 2)
- 脚本末尾没有退出代码
4. Hook 类型是否适当(高确定性)
检查: Hook 类型是否与事件匹配
标记:
- 将提示词 Hook 用于 Stop/SubagentStop 以外的事件
- 缺少类型声明
5. 生命周期事件是否适当(中确定性)
| 事件 | 适用场景 |
|---|---|
PreToolUse | 安全验证、阻止命令、修改输入 |
PostToolUse | 格式化、日志记录、通知 |
Stop | 完成检查、清理、摘要 |
SubagentStop | 工作流编排、结果验证 |
SessionStart | 环境设置、初始化 |
标记:
- PostToolUse Hook 尝试阻止操作(为时已晚)
- PreToolUse Hook 执行繁重处理(应保持快速)
- 在不支持的事件上使用提示词 Hook
6. 超时配置(中确定性)
指南:
- 默认值:命令 Hook 为 30 秒
- 网络操作:始终设置明确的超时时间
- 外部服务调用:根据预期延迟设置超时时间
标记:
- 网络操作没有超时时间
- 外部服务调用缺少超时时间
- 超时时间长得不合理(超过 60 秒且没有说明理由)
7. 输出格式(中确定性)
PreToolUse 输出字段:
permissionDecision:allow、deny 或 askpermissionDecisionReason:决策说明updatedInput:修改后的工具输入(可选)additionalContext:提供给 Claude 的上下文(可选)
标记:
- 无效的 permissionDecision 值
- deny 决策缺少理由
- JSON 输出格式错误
8. 匹配器模式(中确定性)
检查: 匹配器语法是否有效
标记:
- 无效的正则表达式模式
- 过于宽泛的匹配器(使用
*且没有说明理由) - 在不支持匹配器的事件上使用匹配器(Stop、SubagentStop)
9. 反模式(低确定性)
- Hook 中包含复杂逻辑(应简单且快速)
- 缺少文档或注释
- 硬编码路径(应使用
$CLAUDE_PROJECT_DIR) - 网络调用没有错误处理
- Hook 脚本中包含密钥/凭据
自动修复实现
1. 缺少安全头部
#!/usr/bin/env bash
set -euo pipefail
2. 缺少退出代码
在脚本末尾添加 exit 0
3. 缺少 frontmatter 字段
---
name: hook-name
description: Hook description
timeout: 30
---
4. 错误的阻止操作退出代码
阻止操作出错时,将 exit 1 替换为 exit 2
输出格式
## Hook Analysis: {hook-name}
**File**: {path}
**Type**: {command|prompt|config}
**Event**: {PreToolUse|PostToolUse|Stop|...}
### Summary
- HIGH: {count} issues
- MEDIUM: {count} issues
### Frontmatter Issues ({n})
| Issue | Fix | Certainty |
### Safety Issues ({n})
| Issue | Fix | Certainty |
### Exit Code Issues ({n})
| Issue | Fix | Certainty |
### Lifecycle Issues ({n})
| Issue | Fix | Certainty |
### Output Format Issues ({n})
| Issue | Fix | Certainty |
模式统计
| 类别 | 模式数 | 可自动修复 |
|---|---|---|
| Frontmatter | 3 | 2 |
| 安全性 | 6 | 2 |
| 退出代码 | 3 | 2 |
| Hook 类型 | 2 | 0 |
| 生命周期 | 5 | 0 |
| 超时 | 3 | 0 |
| 输出 | 3 | 0 |
| 匹配器 | 3 | 0 |
| 反模式 | 5 | 0 |
| 总计 | 33 | 6 |
### 示例:缺少安全头部
<bad_example>
#!/usr/bin/env bash
cmd=$(jq -r '.tool_input.command // ""')
问题原因:缺少 set -euo pipefail,意味着错误可能会被悄悄忽略。
</bad_example>
<good_example>
#!/usr/bin/env bash
set -euo pipefail
cmd=$(jq -r '.tool_input.command // ""')
优点:遇到错误、未设置的变量和管道失败时会快速失败。 </good_example>
示例:用于阻止操作的退出代码错误
<bad_example>
if [[ "$cmd" == *"rm -rf"* ]]; then
echo "Blocked dangerous command" >&2
exit 1 # Wrong!
fi
为什么不好:退出代码 1 不会阻止操作。操作仍会继续。 </bad_example>
<good_example>
if [[ "$cmd" == *"rm -rf"* ]]; then
echo '{"decision": "block", "reason": "Dangerous command"}' >&2
exit 2 # Correct blocking exit code
fi
为什么好:退出代码 2 会阻止操作。JSON 输出提供上下文。 </good_example>
示例:在错误事件上使用提示词钩子
<bad_example>
{
"hooks": {
"PreToolUse": [
{
"hooks": [{ "type": "prompt", "prompt": "Is this safe?" }]
}
]
}
}
为什么不好:提示词钩子仅适用于 Stop 和 SubagentStop 事件。 </bad_example>
<good_example>
{
"hooks": {
"PreToolUse": [
{
"hooks": [{ "type": "command", "command": "./validate.sh" }]
}
]
}
}
为什么好:命令钩子适用于所有事件。 </good_example>
示例:危险的命令模式
<bad_example>
if echo "$cmd" | grep -q 'rm'; then
exit 2
fi
为什么不好:范围过广——会阻止合法的 rm file.tmp 命令。
</bad_example>
<good_example>
if echo "$cmd" | grep -qE 'rm\s+(-rf|-fr)\s+/'; then
exit 2
fi
为什么好:针对性模式可匹配实际危险的命令。 </good_example>
示例:硬编码路径
<bad_example>
log_file="/home/user/project/.claude/commands.log"
为什么不好:硬编码路径在其他机器上会失效。 </bad_example>
<good_example>
log_file="$CLAUDE_PROJECT_DIR/.claude/commands.log"
为什么好:使用环境变量以确保可移植性。 </good_example>
约束
- 仅对确定性高的问题应用自动修复
- 谨慎处理安全模式——漏报比误报更糟
- 绝不删除内容,只提出改进建议
- 根据上方嵌入的知识参考进行验证