Skip to content
luiseiman edited this page Apr 5, 2026 · 3 revisions

Hooks

Event-driven scripts that protect and validate your code automatically.

Hook Events

Claude Code exposes ~25 hook events (13 fully documented). Key events:

SessionStart, SessionEnd, PreToolUse, PostToolUse, UserPromptSubmit, Stop, SubagentStop, PreCompact, PostCompact, PermissionRequest, SubagentStart, CwdChanged, StopFailure

Hook Types

Type Description
command Bash script (most common)
http POST to a URL
prompt LLM decision
agent Spawn a subagent

In settings.json, hooks must be objects: {"type": "command", "command": "path/to/script.sh"} — never plain strings.

Available Hooks

Hook Event Matcher What it does
block-destructive.sh PreToolUse Bash Blocks dangerous commands (rm -rf, DROP TABLE, force push)
lint-on-save.sh PostToolUse Write|Edit Auto-lint after file save (ruff, eslint, swiftlint, shellcheck)
warn-missing-test.sh PostToolUse Write Warns when creating source files without tests
session-report.sh Stop Generates session metrics JSON
check-updates.sh SessionStart Checks for dotforge updates
detect-claude-changes.sh Stop Detects .claude/ changes, creates practice inbox entries

Hook Profiles (block-destructive)

Configure via FORGE_HOOK_PROFILE environment variable:

Profile Patterns blocked
minimal rm -rf /, rm -rf *, rm -rf ~, git push --force main/master
standard (default) + DROP TABLE, DROP DATABASE, TRUNCATE TABLE, git reset --hard, docker system prune -a, chmod -R 777
strict + curl | sh, wget | sh, eval, chmod 777, > /etc/, dd if=.* of=/dev/

Lint-on-save Stack Detection

File type Linter
.py ruff
.ts, .tsx, .js, .jsx eslint
.swift swiftlint
.sh shellcheck

Optional: TypeScript type-check with FORGE_TSC_CHECK=true.

Session Metrics

session-report.sh generates JSON metrics at ~/.claude/metrics/{project}/{date}.json:

{
  "sessions": 1,
  "errors_added": 0,
  "hook_blocks": 1,
  "lint_blocks": 3,
  "files_touched": 12,
  "rules_matched": 9,
  "rule_coverage": 0.75,
  "commits": 4
}

Hook block counters are accumulated from block-destructive.sh and lint-on-save.sh via temp files in /tmp/.

SessionEnd Timeout

SessionEnd hooks have a default timeout of 1.5 seconds. Override with:

CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000

Set in settings.json under env to prevent session-report.sh from being killed prematurely.

PostCompact / PreCompact

  • PostCompact receives: trigger ("auto"/"manual") + compact_summary (full generated text)
  • PreCompact is non-blocking — exit code is ignored; use for logging/capture only

Async Hooks

Hooks can run asynchronously (fire-and-forget):

{"type": "command", "command": "script.sh", "async": true}

Or stream {"async":true} as the first JSON line from stdout. The asyncRewake flag allows the hook to wake the agent after completion.

How Hooks Work

  • Exit 0 = allow (pass)
  • Exit 2 = block (prevent the action)
  • All hooks must be chmod +x
  • Hooks receive tool input as JSON via $TOOL_INPUT

Clone this wiki locally