Files
everything-claude-code/hooks
Affaan Mustafa 6686cb9bda fix: add try-catch to inline hooks, fix schema drift
- Wrap JSON.parse in try-catch for all 6 inline hooks in hooks.json
  (dev-server blocker, tmux reminder, git-push reminder, doc blocker,
  PR create logger, build analysis) — previously unguarded JSON.parse
  would crash on empty/malformed stdin, preventing data passthrough
- Add config parse error logging to evaluate-session.js
- Fix plugin.schema.json: author can be string or {name,url} object,
  add version (semver pattern), homepage, keywords, skills, agents
- Fix package-manager.schema.json: add setAt (date-time) field and
  make packageManager required to match actual code behavior
2026-02-12 14:38:00 -08:00
..

Hooks

Hooks are event-driven automations that fire before or after Claude Code tool executions. They enforce code quality, catch mistakes early, and automate repetitive checks.

How Hooks Work

User request → Claude picks a tool → PreToolUse hook runs → Tool executes → PostToolUse hook runs
  • PreToolUse hooks run before the tool executes. They can block (exit code 2) or warn (stderr without blocking).
  • PostToolUse hooks run after the tool completes. They can analyze output but cannot block.
  • Stop hooks run after each Claude response.
  • SessionStart/SessionEnd hooks run at session lifecycle boundaries.
  • PreCompact hooks run before context compaction, useful for saving state.

Hooks in This Plugin

PreToolUse Hooks

Hook Matcher Behavior Exit Code
Dev server blocker Bash Blocks npm run dev etc. outside tmux — ensures log access 2 (blocks)
Tmux reminder Bash Suggests tmux for long-running commands (npm test, cargo build, docker) 0 (warns)
Git push reminder Bash Reminds to review changes before git push 0 (warns)
Doc file blocker Write Blocks creation of random .md/.txt files (allows README, CLAUDE, CONTRIBUTING) 2 (blocks)
Strategic compact Edit|Write Suggests manual /compact at logical intervals (every ~50 tool calls) 0 (warns)

PostToolUse Hooks

Hook Matcher What It Does
PR logger Bash Logs PR URL and review command after gh pr create
Build analysis Bash Background analysis after build commands (async, non-blocking)
Prettier format Edit Auto-formats JS/TS files with Prettier after edits
TypeScript check Edit Runs tsc --noEmit after editing .ts/.tsx files
console.log warning Edit Warns about console.log statements in edited files

Lifecycle Hooks

Hook Event What It Does
Session start SessionStart Loads previous context and detects package manager
Pre-compact PreCompact Saves state before context compaction
Console.log audit Stop Checks all modified files for console.log after each response
Session end SessionEnd Persists session state for next session
Pattern extraction SessionEnd Evaluates session for extractable patterns (continuous learning)

Customizing Hooks

Disabling a Hook

Remove or comment out the hook entry in hooks.json. If installed as a plugin, override in your ~/.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": [],
        "description": "Override: allow all .md file creation"
      }
    ]
  }
}

Writing Your Own Hook

Hooks are shell commands that receive tool input as JSON on stdin and must output JSON on stdout.

Basic structure:

// my-hook.js
let data = '';
process.stdin.on('data', chunk => data += chunk);
process.stdin.on('end', () => {
  const input = JSON.parse(data);

  // Access tool info
  const toolName = input.tool_name;        // "Edit", "Bash", "Write", etc.
  const toolInput = input.tool_input;      // Tool-specific parameters
  const toolOutput = input.tool_output;    // Only available in PostToolUse

  // Warn (non-blocking): write to stderr
  console.error('[Hook] Warning message shown to Claude');

  // Block (PreToolUse only): exit with code 2
  // process.exit(2);

  // Always output the original data to stdout
  console.log(data);
});

Exit codes:

  • 0 — Success (continue execution)
  • 2 — Block the tool call (PreToolUse only)
  • Other non-zero — Error (logged but does not block)

Hook Input Schema

interface HookInput {
  tool_name: string;          // "Bash", "Edit", "Write", "Read", etc.
  tool_input: {
    command?: string;         // Bash: the command being run
    file_path?: string;       // Edit/Write/Read: target file
    old_string?: string;      // Edit: text being replaced
    new_string?: string;      // Edit: replacement text
    content?: string;         // Write: file content
  };
  tool_output?: {             // PostToolUse only
    output?: string;          // Command/tool output
  };
}

Async Hooks

For hooks that should not block the main flow (e.g., background analysis):

{
  "type": "command",
  "command": "node my-slow-hook.js",
  "async": true,
  "timeout": 30
}

Async hooks run in the background. They cannot block tool execution.

Common Hook Recipes

Warn about TODO comments

{
  "matcher": "Edit",
  "hooks": [{
    "type": "command",
    "command": "node -e \"let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const ns=i.tool_input?.new_string||'';if(/TODO|FIXME|HACK/.test(ns)){console.error('[Hook] New TODO/FIXME added - consider creating an issue')}console.log(d)})\""
  }],
  "description": "Warn when adding TODO/FIXME comments"
}

Block large file creation

{
  "matcher": "Write",
  "hooks": [{
    "type": "command",
    "command": "node -e \"let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const c=i.tool_input?.content||'';const lines=c.split('\\n').length;if(lines>800){console.error('[Hook] BLOCKED: File exceeds 800 lines ('+lines+' lines)');console.error('[Hook] Split into smaller, focused modules');process.exit(2)}console.log(d)})\""
  }],
  "description": "Block creation of files larger than 800 lines"
}

Auto-format Python files with ruff

{
  "matcher": "Edit",
  "hooks": [{
    "type": "command",
    "command": "node -e \"let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path||'';if(/\\.py$/.test(p)){const{execFileSync}=require('child_process');try{execFileSync('ruff',['format',p],{stdio:'pipe'})}catch(e){}}console.log(d)})\""
  }],
  "description": "Auto-format Python files with ruff after edits"
}

Require test files alongside new source files

{
  "matcher": "Write",
  "hooks": [{
    "type": "command",
    "command": "node -e \"const fs=require('fs');let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const i=JSON.parse(d);const p=i.tool_input?.file_path||'';if(/src\\/.*\\.(ts|js)$/.test(p)&&!/\\.test\\.|\\.spec\\./.test(p)){const testPath=p.replace(/\\.(ts|js)$/,'.test.$1');if(!fs.existsSync(testPath)){console.error('[Hook] No test file found for: '+p);console.error('[Hook] Expected: '+testPath);console.error('[Hook] Consider writing tests first (/tdd)')}}console.log(d)})\""
  }],
  "description": "Remind to create tests when adding new source files"
}

Cross-Platform Notes

All hooks in this plugin use Node.js (node -e or node script.js) for maximum compatibility across Windows, macOS, and Linux. Avoid bash-specific syntax in hooks.