Hooks and Automation

Deterministic lifecycle hooks for Claude Code and CI/CD integration.

Last updated

Workflow

1. Lifecycle Events

Claude Code exposes 26 lifecycle events that fire at specific points during a session. Hooks subscribe to these events and run deterministic logic -- shell scripts, HTTP calls, or LLM evaluations -- before the agent proceeds.

Key events

EventWhen it firesUse case
PreToolUseBefore a tool executesBlock or modify tool input
PostToolUseAfter a tool returnsValidate output, enforce invariants, keep feedback short
StopBefore final responseQuality gate on the answer
SessionStartSession beginsCheck prerequisites, inject only minimal context
PermissionRequestTool needs approvalAuto-approve or deny based on rules
SubagentStartSubagent spawnsLimit concurrency, log subagent work
SubagentStopSubagent finishesAggregate results, check quality
PreCompactBefore context compactionPreserve critical context
PostCompactAfter context compactionRe-inject lost context
CwdChangedWorking directory changesUpdate project-specific config
FileChangedFile is written/modifiedLint, format, or trigger rebuild

Other events include TaskCreated, TaskCompleted, ConfigChange, WorktreeCreate, WorktreeRemove, and Elicitation. Most projects only need PreToolUse, PostToolUse, and Stop to start.

Hook output is prompt surface: anything a hook prints back to the agent becomes part of the model's working context. Use hooks to block, validate, or report facts. Do not write hook feedback that tells the agent to offer choices, wait, stop, create profiles, or switch tasks. Sandbox recovery belongs in managed profiles and launcher docs, not injected into every transcript.

2. Handler Types

Each hook specifies one of five handler types. The type determines how the hook runs and what it can do.

TypeHow it runsBest for
commandShell script (deterministic)Blocking patterns, file checks, git guards
httpPOST to an endpointExternal services, logging, webhooks
mcp_toolCall an MCP server toolReuse existing MCP integrations
promptSingle-turn LLM eval (Haiku)Semantic judgment, fuzzy matching
agentMulti-turn subagentDeep verification, complex checks

Exit code protocol

Hooks communicate decisions through exit codes. Exit 0 means proceed normally. Exit 2 means block the action -- the hook's stderr becomes feedback the agent sees and can act on. Keep that feedback factual and non-procedural. Any other exit code is treated as an error.

For PreToolUse hooks, the handler can also return JSON on stdout with permissionDecision (to auto-approve or deny) and updatedInput (to modify tool arguments before execution).

Example: block force-push

# .claude/hooks/no-force-push.sh
# PreToolUse hook matching the Bash tool

INPUT=$(cat -)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')

if echo "$CMD" | grep -qE 'git\s+push\s+.*--force'; then
  echo "Blocked: force-push is not allowed." >&2
  exit 2
fi

exit 0

Register it in .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "type": "command",
        "command": ".claude/hooks/no-force-push.sh"
      }
    ]
  }
}

3. The Dispatcher Pattern

A single event can trigger multiple hooks. When a project grows past a handful, concurrent hooks reading from stdin can corrupt each other's JSON input. The dispatcher pattern solves this: one hook per event reads stdin once, caches it, then runs all handlers sequentially from the cached copy.

How it works

  1. A single command hook is registered for each event (e.g., pre-tool-dispatch.sh).
  2. The dispatcher reads the full JSON payload from stdin into a variable.
  3. It iterates through a config file listing all handlers for that event, piping the cached JSON to each one.
  4. If any handler exits 2, the dispatcher propagates the block immediately.

Config-driven thresholds

Store thresholds and feature flags in JSON config files, not hardcoded in scripts. This lets you adjust behavior (max file size, allowed commands, blocked patterns) without editing hook code.

Performance: A well-structured dispatcher adds roughly 200ms total overhead per event, even with 95+ hooks. The bottleneck is handler logic, not dispatch overhead.
Start small: Begin with 3 hooks (a force-push guard, a lint check, a Stop quality gate), not 25. Add hooks only when something goes wrong that a hook would have caught.

4. Skills and Slash Commands

Skills are Markdown files (.claude/skills/deploy/SKILL.md) that define reusable capabilities Claude can invoke. They supersede the older slash-command system and load on demand, keeping the context window lean.

Invocation control

Frontmatter fieldEffect
disable-model-invocation: trueUser-only -- Claude cannot trigger this skill autonomously
user-invocable: falseModel-only -- the user cannot call it directly

Dynamic context injection

Skills can inject live data into their prompt using !command syntax. When the skill loads, the command runs and its stdout replaces the directive inline. This keeps skills context-aware without hardcoding values.

Key frontmatter fields

  • name -- display name for the skill
  • description -- what triggers the skill (matched semantically)
  • allowed-tools -- restrict which tools the skill can use
  • model -- override the default model
  • effort -- reasoning effort level
  • context -- files or globs to auto-load
  • hooks -- lifecycle hooks specific to this skill

5. CI/CD Integration

Any CI pipeline that runs shell commands can run Claude Code as a headless agent using the -p flag (pipe mode). The agent reads a prompt from stdin or arguments, executes it non-interactively, and exits with a status code.

Trust spectrum

LevelWhat the agent doesHuman involvement
AssistComments on PRs with suggestionsReviewers apply manually
DraftOpens PRs with proposed changesHuman reviews and merges
Auto-mergeMerges if CI passesHuman sets merge criteria
AutonomousFull lifecycle: detect, fix, shipPost-hoc audit only

Start at Draft. Move to Auto-merge only for low-risk, high-frequency tasks (dependency bumps, doc regeneration) where the CI suite provides sufficient coverage.

Common patterns

  • Dependency updates -- bump, run tests, open PR
  • Doc generation -- regenerate API docs from code changes
  • Code review assistance -- post inline comments on PRs
  • Bug triage -- label and assign issues based on stack traces
Warning: Never commit AI-generated code without review. Agents write plausible but wrong code. The Draft level exists specifically to keep a human in the loop.
Event fires → Matcher (tool name + args) → Handler → Exit code → Proceed / Block 26 events Pipe-delimited + regex 5 types 0 / 2 With feedback