Hook System

Understanding Han's hook lifecycle for automated quality validation at every stage of your Claude Code workflow.

Han's hook system automatically validates your code at key moments during Claude Code sessions. No manual commands, no forgotten checks - quality gates that run themselves.

Why Hooks?

Traditional validation requires discipline: remember to lint, remember to test, remember to type-check. Claude doesn't forget, but without hooks, validation becomes an afterthought.

Hooks make validation automatic. Write code, finish your conversation, and hooks validate everything before you move on.

The Hook Lifecycle

Han hooks into Claude Code's execution at specific points:

HookWhen It FiresPurpose
SessionStartSession beginsInitialize state, capture checkpoints
SubagentStartSubagent spawnsCapture agent checkpoint
UserPromptSubmitBefore processing inputPre-process, inject context
PreToolUseBefore tool executionValidate tool calls
PostToolUseAfter tool executionProcess results
StopBefore responseMain validation point
SubagentStopSubagent completesValidate agent's work
SessionEndSession endsCleanup

Most validation happens at Stop and SubagentStop - the natural checkpoints after work is done.

How Hooks Run

Session Flow

SessionStart
  │
  ├─ [Your work happens]
  │
  ├─ SubagentStart (if agent spawned)
  │   ├─ [Subagent work]
  │   └─ SubagentStop (validates subagent changes)
  │
  └─ Stop (validates session changes)

Checkpoint Integration

  • SessionStart: Creates session checkpoint
  • SubagentStart: Creates agent checkpoint
  • Stop: Validates against session checkpoint
  • SubagentStop: Validates against agent checkpoint

This ensures hooks only check files that changed since the relevant checkpoint.

What Hooks Do

Han plugins define hooks for specific validations:

Jutsu Plugins (Techniques)

PluginHookValidates
jutsu-biomelintJavaScript/TypeScript linting
jutsu-typescripttypecheckType errors
jutsu-buntestTest failures
jutsu-markdownlintMarkdown formatting

Example Hook Configuration

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "han hook run jutsu-biome lint" },
          { "type": "command", "command": "han hook run jutsu-typescript typecheck" }
        ]
      }
    ],
    "SubagentStop": [
      {
        "hooks": [
          { "type": "command", "command": "han hook run jutsu-biome lint" }
        ]
      }
    ]
  }
}

Hook Types

Han supports two types of hooks:

Command Hooks

Execute shell commands:

{
  "type": "command",
  "command": "han hook run jutsu-biome lint",
  "timeout": 120
}

Prompt Hooks

Inject context or guidance for Claude:

{
  "type": "prompt",
  "prompt": "Review all changes for security vulnerabilities...",
  "timeout": 300
}

Prompt hooks are powerful for review workflows and quality gates.

Smart Behaviors

Han hooks are intelligent by default (as of v2.0.0):

Caching

Skip hooks when files haven't changed:

  • Compares file hashes to previous run
  • Only re-validates modified files
  • Dramatically speeds up repeated runs

Fail-Fast

Stop on first failure:

  • Don't waste time running all hooks
  • Get feedback immediately
  • Continue with --no-fail-fast when needed

Checkpoint Filtering

Only validate your work:

  • Session hooks filter to session changes
  • Agent hooks filter to agent changes
  • Pre-existing issues are out of scope

All three are enabled by default. Disable with --no-cache, --no-fail-fast, or --no-checkpoints.

Configuration

Global Settings

In han.yml:

hooks:
  enabled: true       # Master switch
  cache: true         # Smart caching (default: true)
  fail_fast: true     # Stop on first failure (default: true)
  checkpoints: true   # Session-scoped filtering (default: true)

Per-Plugin Settings

plugins:
  jutsu-biome:
    hooks:
      lint:
        enabled: true
        command: npx biome check --write .
        cache: true
        if_changed:
          - "**/*.ts"
          - "**/*.tsx"

Conditional Execution

Only run in directories with specific files:

plugins:
  jutsu-typescript:
    hooks:
      typecheck:
        dirs_with:
          - tsconfig.json

Only run when specific patterns changed:

plugins:
  jutsu-bun:
    hooks:
      test:
        if_changed:
          - "**/*.ts"
          - "**/*.test.ts"

Hook Priority

Settings cascade with later overriding earlier:

  1. Built-in defaults: All features enabled
  2. han.yml: Your configuration
  3. CLI flags: --no-cache, --no-fail-fast
  4. Environment variables: HAN_NO_CACHE=1

Running Hooks Manually

While hooks run automatically, you can trigger them manually:

# Run a specific plugin hook
han hook run jutsu-biome lint

# Run with options
han hook run jutsu-typescript typecheck --verbose

# Disable caching for this run
han hook run jutsu-bun test --no-cache

See CLI Hook Commands for full reference.

Creating Custom Hooks

Any command can be a hook. Create project-specific validation:

# han.yml
plugins:
  my-project:
    hooks:
      validate-schema:
        command: ./scripts/validate-schema.sh
        if_changed:
          - "**/*.graphql"

Hook into the lifecycle:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "han hook run my-project validate-schema" }
        ]
      }
    ]
  }
}

Debugging Hooks

Verbose Output

See what's happening:

han hook run jutsu-biome lint --verbose

Check Hook Status

View hook configuration:

han hook info jutsu-biome lint

Force Re-run

Bypass cache:

han hook run jutsu-biome lint --no-cache

Best Practices

Keep Hooks Fast

  • Use caching to skip unchanged files
  • Run expensive checks (tests) less frequently
  • Use if_changed to limit scope

Layer Your Validation

SubagentStop: Quick checks (lint, typecheck)
Stop: Full validation (lint, typecheck, tests)

Trust the System

Let hooks run automatically. Don't disable them because they found issues - fix the issues.

Next Steps