debug-hooks

parcadei's avatarfrom parcadei

Systematic hook debugging workflow. Use when hooks aren't firing, producing wrong output, or behaving unexpectedly.

⭐1.3kstars🔀76forks📁View on GitHub🕐Updated Jan 1, 2026

When & Why to Use This Skill

The 'debug-hooks' skill provides a systematic and comprehensive workflow for troubleshooting Claude Code hooks. It is designed to resolve critical issues such as hooks failing to fire, producing incorrect outputs, or silent execution failures. By offering a structured approach to inspecting caches, verifying registration, and manual testing, this skill ensures that agent automation triggers are reliable and correctly configured for both local and CI environments.

Use Cases

  • Troubleshooting non-responsive lifecycle events such as 'SessionEnd' or 'PostToolUse' to ensure automation triggers execute as expected.
  • Verifying hook registration and merging logic between global and project-specific settings.json files to resolve configuration conflicts.
  • Performing manual unit tests on shell wrappers and compiled TypeScript bundles by simulating JSON input streams.
  • Identifying and fixing silent failures in detached background processes by redirecting stdio to debug logs.
  • Ensuring development consistency by validating that TypeScript source edits are correctly rebuilt into the distribution folder before execution.
namedebug-hooks
descriptionSystematic hook debugging workflow. Use when hooks aren't firing, producing wrong output, or behaving unexpectedly.
allowed-tools[Bash, Read, Grep]

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

Source edits alone don't take effect - the shell wrapper runs the bundled .mjs.

Common Issues

Symptom Likely Cause Fix
Hook never runs Not registered in settings.json Add to correct event in settings
Hook runs but no output Detached spawn hiding errors Add logging, check manually
Wrong session ID Using "most recent" query Pass ID explicitly
Works locally, not in CI Missing dependencies Check npx/node availability
Runs twice Registered in both global + project Remove duplicate

Debug Checklist

  • Outputs exist? (ls -la .claude/cache/)
  • Registered? (grep -A10 '"hooks"' .claude/settings.json)
  • Files exist? (ls .claude/hooks/*.sh)
  • Bundle current? (ls -la .claude/hooks/dist/)
  • Manual test works? (echo '{}' | ./hook.sh)
  • No silent failures? (check for stdio: 'ignore')

Source Sessions

Derived from 10 sessions (83% of all learnings):

  • a541f08a, 1c21e6c8, 6a9f2d7a, a8bd5cea, 2ca1a178, 657ce0b2, 3998f3a2, 2a829f12, 0b46cfd7, 862f6e2c