tool-hooks-doctor

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Hooks Doctor (Claude Code)

Hooks 诊断工具(Claude Code)

Goal: quickly verify whether Claude Code hooks for
skill-evolution
are installed and enabled.
This is an atomic diagnostic tool used by other workflows so they can warn early when the evolution loop is not active.
目标:快速验证用于
skill-evolution
Claude Code hooks是否已安装并启用。
这是一个原子化诊断工具,供其他工作流使用,以便在evolution循环未激活时提前发出警告。

Scope

适用范围

  • Applies to: Claude Code hook installation / configuration
  • Does not apply to: Cursor/OpenCode/etc that do not support Claude-style hooks
  • 适用场景:Claude Code hook的安装/配置
  • 不适用场景:不支持Claude风格hooks的Cursor/OpenCode等工具

What "healthy" looks like

正常状态的标准

  1. Hook scripts exist at:
  • ~/.claude/skills/skill-evolution/hooks/
  1. Settings enable the hooks in either:
  • Project settings:
    <repo_root>/.claude/settings.json
  • Or global settings:
    ~/.claude/settings.json
  1. A session produces artifacts under:
  • <project_root>/runs/evolution/<run_id>/...
  1. Hook脚本存在于以下路径:
  • ~/.claude/skills/skill-evolution/hooks/
  1. 在以下任一配置文件中启用了hooks:
  • 项目配置:
    <repo_root>/.claude/settings.json
  • 或全局配置:
    ~/.claude/settings.json
  1. 会话会在以下路径生成产物:
  • <project_root>/runs/evolution/<run_id>/...

Check (read-only)

检查步骤(只读)

Run these checks and report status as: OK / PARTIAL / MISSING.
执行以下检查,并将状态报告为:正常(OK)/ 部分缺失(PARTIAL)/ 完全缺失(MISSING)。

1) Are the hook scripts installed?

1) Hook脚本是否已安装?

bash
ls -la ~/.claude/skills/skill-evolution/hooks/ 2>/dev/null || true
Required files:
  • pre-tool.sh
  • post-bash.sh
  • post-tool.sh
  • session-end.sh
If missing: user must install/update the
skill-evolution
skill first.
bash
ls -la ~/.claude/skills/skill-evolution/hooks/ 2>/dev/null || true
必需文件:
  • pre-tool.sh
  • post-bash.sh
  • post-tool.sh
  • session-end.sh
如果缺失:用户必须先安装/更新
skill-evolution
技能。

2) Are hooks enabled in settings?

2) 是否在配置中启用了hooks?

Check both locations:
bash
test -f .claude/settings.json && echo "project settings: .claude/settings.json" || true
test -f ~/.claude/settings.json && echo "global settings: ~/.claude/settings.json" || true

grep -n "skill-evolution/hooks/pre-tool.sh" .claude/settings.json ~/.claude/settings.json 2>/dev/null || true
grep -n "skill-evolution/hooks/post-bash.sh" .claude/settings.json ~/.claude/settings.json 2>/dev/null || true
grep -n "skill-evolution/hooks/post-tool.sh" .claude/settings.json ~/.claude/settings.json 2>/dev/null || true
grep -n "skill-evolution/hooks/session-end.sh" .claude/settings.json ~/.claude/settings.json 2>/dev/null || true
Interpretation:
  • If none of the grep checks match: hooks are not enabled.
  • If only some match: configuration is PARTIAL and should be fixed.
检查以下两个位置:
bash
test -f .claude/settings.json && echo "project settings: .claude/settings.json" || true
test -f ~/.claude/settings.json && echo "global settings: ~/.claude/settings.json" || true

grep -n "skill-evolution/hooks/pre-tool.sh" .claude/settings.json ~/.claude/settings.json 2>/dev/null || true
grep -n "skill-evolution/hooks/post-bash.sh" .claude/settings.json ~/.claude/settings.json 2>/dev/null || true
grep -n "skill-evolution/hooks/post-tool.sh" .claude/settings.json ~/.claude/settings.json 2>/dev/null || true
grep -n "skill-evolution/hooks/session-end.sh" .claude/settings.json ~/.claude/settings.json 2>/dev/null || true
结果解读:
  • 如果所有grep检查都无匹配结果:hooks未启用。
  • 如果只有部分匹配:配置不完整,需要修复。

3) Quick runtime smoke test (optional)

3) 快速运行时冒烟测试(可选)

If user confirms, run a harmless bash command in the project and then check:
bash
ls -la runs/evolution 2>/dev/null || true
如果用户确认,在项目中运行一个无风险的bash命令,然后检查:
bash
ls -la runs/evolution 2>/dev/null || true

Fix (write; require confirmation)

修复步骤(写入操作;需用户确认)

If hooks are not enabled, recommend installing project-level hooks (safer than global).
Before applying, tell the user exactly which file will be written:
  • Project-level:
    <repo_root>/.claude/settings.json
    (recommended)
  • Global:
    ~/.claude/settings.json
Then wait for explicit confirmation.
如果hooks未启用,建议安装项目级hooks(比全局hooks更安全)。
在执行修复前,明确告知用户将写入哪个文件:
  • 项目级:
    <repo_root>/.claude/settings.json
    (推荐)
  • 全局:
    ~/.claude/settings.json
然后等待用户明确确认。

Install project-level hooks (recommended)

安装项目级hooks(推荐)

bash
python3 - <<'PY'
import json
from pathlib import Path

settings = Path('.claude') / 'settings.json'
settings.parent.mkdir(parents=True, exist_ok=True)

data = {}
if settings.exists():
    data = json.loads(settings.read_text() or '{}')
if not isinstance(data, dict):
    data = {}

hooks = data.get('hooks')
if not isinstance(hooks, dict):
    hooks = {}

desired = {
  'PreToolUse': [{
    'matcher': 'Bash|Write|Edit',
    'hooks': [{'type':'command','command':'bash ~/.claude/skills/skill-evolution/hooks/pre-tool.sh'}]
  }],
  'PostToolUse': [
    {
      'matcher': 'Bash',
      'hooks': [{'type':'command','command':'bash ~/.claude/skills/skill-evolution/hooks/post-bash.sh "$TOOL_OUTPUT" "$EXIT_CODE"'}]
    },
    {
      'matcher': 'Write|Edit',
      'hooks': [{'type':'command','command':'bash ~/.claude/skills/skill-evolution/hooks/post-tool.sh "$TOOL_OUTPUT" "$EXIT_CODE"'}]
    }
  ],
  'Stop': [{
    'matcher': '',
    'hooks': [{'type':'command','command':'bash ~/.claude/skills/skill-evolution/hooks/session-end.sh'}]
  }]
}

def has_command(arr, matcher, command):
    for item in arr:
        if not isinstance(item, dict):
            continue
        if item.get('matcher') != matcher:
            continue
        hs = item.get('hooks')
        if not isinstance(hs, list):
            continue
        for h in hs:
            if isinstance(h, dict) and h.get('command') == command:
                return True
    return False

for event, items in desired.items():
    arr = hooks.get(event)
    if not isinstance(arr, list):
        arr = []
    for it in items:
        cmd = it['hooks'][0]['command']
        if not has_command(arr, it['matcher'], cmd):
            arr.append(it)
    hooks[event] = arr

data['hooks'] = hooks

if settings.exists():
    backup = settings.with_suffix(settings.suffix + '.bak')
    backup.write_text(settings.read_text())

settings.write_text(json.dumps(data, indent=2, ensure_ascii=True) + '\n')
print('Installed hooks into:', settings)
PY
bash
python3 - <<'PY'
import json
from pathlib import Path

settings = Path('.claude') / 'settings.json'
settings.parent.mkdir(parents=True, exist_ok=True)

data = {}
if settings.exists():
    data = json.loads(settings.read_text() or '{}')
if not isinstance(data, dict):
    data = {}

hooks = data.get('hooks')
if not isinstance(hooks, dict):
    hooks = {}

desired = {
  'PreToolUse': [{
    'matcher': 'Bash|Write|Edit',
    'hooks': [{'type':'command','command':'bash ~/.claude/skills/skill-evolution/hooks/pre-tool.sh'}]
  }],
  'PostToolUse': [
    {
      'matcher': 'Bash',
      'hooks': [{'type':'command','command':'bash ~/.claude/skills/skill-evolution/hooks/post-bash.sh \"$TOOL_OUTPUT\" \"$EXIT_CODE\"'}]
    },
    {
      'matcher': 'Write|Edit',
      'hooks': [{'type':'command','command':'bash ~/.claude/skills/skill-evolution/hooks/post-tool.sh \"$TOOL_OUTPUT\" \"$EXIT_CODE\"'}]
    }
  ],
  'Stop': [{
    'matcher': '',
    'hooks': [{'type':'command','command':'bash ~/.claude/skills/skill-evolution/hooks/session-end.sh'}]
  }]
}

def has_command(arr, matcher, command):
    for item in arr:
        if not isinstance(item, dict):
            continue
        if item.get('matcher') != matcher:
            continue
        hs = item.get('hooks')
        if not isinstance(hs, list):
            continue
        for h in hs:
            if isinstance(h, dict) and h.get('command') == command:
                return True
    return False

for event, items in desired.items():
    arr = hooks.get(event)
    if not isinstance(arr, list):
        arr = []
    for it in items:
        cmd = it['hooks'][0]['command']
        if not has_command(arr, it['matcher'], cmd):
            arr.append(it)
    hooks[event] = arr

data['hooks'] = hooks

if settings.exists():
    backup = settings.with_suffix(settings.suffix + '.bak')
    backup.write_text(settings.read_text())

settings.write_text(json.dumps(data, indent=2, ensure_ascii=True) + '\n')
print('Installed hooks into:', settings)
PY

Install global hooks (optional)

安装全局hooks(可选)

Same as above, but write to
~/.claude/settings.json
.
与上述步骤相同,但写入到
~/.claude/settings.json