ConsLabs
Claude Code
Advanced Features3 min read

Hooks

Running your own shell commands automatically around Claude Code's lifecycle events, for formatting, enforcement, and logging.

A hook is a shell command you configure to run automatically when something specific happens during a session — before a tool executes, after a response, when a session starts or ends. Hooks are how you attach your own deterministic automation to an otherwise non-deterministic agent loop.

What hooks are for

The agent decides what to do; a hook reacts to that decision with guaranteed, scriptable behavior that doesn't depend on the model remembering to do it. Common uses:

  • Auto-formatting every edited file immediately after it's written, so formatting is never a matter of whether the agent remembered to run the formatter.
  • Blocking specific actions outright — a hook that inspects a proposed command before it runs and refuses to let it execute if it matches a pattern you've decided is never acceptable, regardless of context or how the request was phrased.
  • Logging and notifications — recording every file change for an audit trail, or sending a notification when a long session finishes or needs input.
  • Enforcing policy mechanically — a hook can require a specific check to pass before allowing a commit, independent of whether the agent thought to run that check itself.

A representative example

A hook that runs a formatter after every file edit, configured roughly like this:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "prettier --write \"$CLAUDE_FILE_PATH\""
      }
    ]
  }
}

The exact event names and available variables depend on your version, but the shape is consistent: match an event (and optionally which tool triggered it), then run a command, with relevant context passed in.

Why hooks matter even with good prompting

You could ask Claude, in CLAUDE.md, to always format files after editing them — and most of the time it would. A hook makes that guaranteed rather than likely, because it runs as deterministic code outside the model's reasoning entirely. The distinction matters most for anything where "almost always" isn't good enough: a security check that must run before every commit is exactly the kind of thing that belongs in a hook rather than a polite request in a memory file.

Treat hooks like any other automation with real privileges

A hook runs with whatever permissions the shell it executes in has — the same caution that applies to running shell commands generally applies here, just running automatically instead of through the agent's own decision-making. Review what a hook actually does before relying on it, the same way you would for any script you'd let run unattended.

Next: connecting Claude Code to external tools and data through MCP.