SKILL.md
Debug Hooks
Systematic workflow for debugging Claude Code hooks.
When to Use
- "Hook isn't firing"
- "Hook produces wrong output"
- "SessionEnd not working"
- "PostToolUse hook not triggering"
- "Why didn't my hook run?"
Workflow
1. Check Outputs First (Observe Before Editing)
# Check project cache
ls -la $CLAUDE_PROJECT_DIR/.claude/cache/
Check specific outputs
ls -la $CLAUDE_PROJECT_DIR/.claude/cache/learnings/
Check for debug logs
tail $CLAUDE_PROJECT_DIR/.claude/cache/*.log 2>/dev/null
Also check global (common mistake: wrong path)
ls -la ~/.claude/cache/ 2>/dev/null
### 2. Verify Hook Registration
Project settings
cat $CLAUDE_PROJECT_DIR/.claude/settings.json | grep -A 20 '"SessionEnd"\|"PostToolUse"\|"UserPromptSubmit"'
Global settings (hooks merge from both)
cat ~/.claude/settings.json | grep -A 20 '"SessionEnd"\|"PostToolUse"\|"UserPromptSubmit"'
### 3. Check Hook Files Exist
Shell wrappers
ls -la $CLAUDE_PROJECT_DIR/.claude/hooks/*.sh
Compiled bundles (if using TypeScript)
ls -la $CLAUDE_PROJECT_DIR/.claude/hooks/dist/*.mjs
### 4. Test Hook Manually
SessionEnd hook
echo '{"session_id": "test-123", "reason": "clear", "transcript_path": "/tmp/test"}' | \
$CLAUDE_PROJECT_DIR/.claude/hooks/session-end-cleanup.sh
PostToolUse hook (Write tool example)
echo '{"tool_name": "Write", "tool_input": {"file_path": "test.md"}, "session_id": "test-123"}' | \
$CLAUDE_PROJECT_DIR/.claude/hooks/handoff-index.sh
### 5. Check for Silent Failures
If using detached spawn with `stdio: 'ignore'`:
// This pattern hides errors!
spawn(cmd, args, { detached: true, stdio: 'ignore' })
**Fix:** Add temporary logging:
const logFile = fs.openSync('.claude/cache/debug.log', 'a');
spawn(cmd, args, {
detached: true,
stdio: ['ignore', logFile, logFile] // capture stdout/stderr
});
### 6. Rebuild After Edits
If you edited TypeScript source, you MUST rebuild:
cd $CLAUDE_PROJECT_DIR/.claude/hooks
npx esbuild src/session-end-cleanup.ts \
--bundle --platform=node --format=esm \
--outfile=dist/session-end-cleanup.mjs