-
Notifications
You must be signed in to change notification settings - Fork 0
Hooks
Event-driven scripts that protect and validate your code automatically.
Claude Code exposes ~25 hook events (13 fully documented). Key events:
SessionStart, SessionEnd, PreToolUse, PostToolUse, UserPromptSubmit, Stop, SubagentStop, PreCompact, PostCompact, PermissionRequest, SubagentStart, CwdChanged, StopFailure
| 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.
| 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 |
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/
|
| File type | Linter |
|---|---|
.py |
ruff |
.ts, .tsx, .js, .jsx
|
eslint |
.swift |
swiftlint |
.sh |
shellcheck |
Optional: TypeScript type-check with FORGE_TSC_CHECK=true.
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 hooks have a default timeout of 1.5 seconds. Override with:
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000Set in settings.json under env to prevent session-report.sh from being killed prematurely.
-
PostCompact receives:
trigger("auto"/"manual") +compact_summary(full generated text) - PreCompact is non-blocking — exit code is ignored; use for logging/capture only
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.
- 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