Skip to content

What are agent hooks (Stop hooks)?

Agent hooks are scripts a coding agent runs at set points in its loop, e.g. a Stop hook that runs the tests before the agent can end its turn.

Last updated , 9 min read

What are agent hooks (Stop hooks)?

An agent hook is a script that a coding agent's harness runs at a fixed point in the agent's loop. A Stop hook is an agent hook that runs when the agent tries to end its turn. A hook can let the step go ahead, block it, or send the agent a message to act on.

Hooks belong to the harness, not to the model, so each coding agent defines its own set of hook events. The Claude Code hooks reference describes hooks as handlers that "execute automatically at specific points in Claude Code's lifecycle." Each of these points is a hook event, e.g. Stop.

Hooks put checks inside the loop, before control returns to the developer. A pre-commit hook runs only if the agent commits, and a continuous integration and delivery (CI/CD) pipeline usually runs after a push. In Claude Code, a Stop hook can run the tests each time the agent tries to stop. When a test fails, the hook exits with code 2, and the agent receives the failure and keeps working.

How do agent hooks work?

Each hook pairs an event with a handler, which is usually a shell command. A command hook works in six steps:

  1. A developer adds the hook to a settings file with an event, an optional matcher, and the command. The matcher limits the hook to some tools, e.g. Bash.
  2. The harness reaches the event, e.g. the model requests a tool call that edits a file.
  3. The harness starts the command and passes it a JSON description of the event on standard input.
  4. The command runs its check and exits with a code. It can also print JSON with a decision.
  5. The harness reads the result. In Claude Code, exit code 2 blocks a tool call or a stop, and the model receives the command's error output.
  6. The model reads that message as its next input and continues from it.
Where hooks run in the agent loop Prompt Model tool call PreToolUse hook can block the call Tool runs PostToolUse hook checks the result result final answer block Stop hook can send it back allow Turn ends
The PreToolUse and PostToolUse hooks run on each matching tool call. The Stop hook runs once per final answer, so a slow check there adds less wait to the loop.

Claude Code and Codex share the names of these four events:

  • Before a tool call. A PreToolUse hook reads the tool's input and can block the call.
  • After a tool call. A PostToolUse hook runs after the tool runs, e.g. to format an edited file. It cannot undo the call. Claude Code runs it only after the tool succeeds.
  • When a prompt arrives. A UserPromptSubmit hook runs before the model reads the prompt and can add context or block it.
  • When the agent stops. A Stop hook runs when the agent finishes its response and can send it back to work.

Besides shell commands, Claude Code can use an HTTP endpoint, a Model Context Protocol (MCP) tool, a model prompt, or a subagent as a handler. Its documentation calls the subagent type an "agent hook," a narrower meaning than this page's, and marks it experimental. A hook in a user's own settings applies to every project. A project's settings file can be committed and shared with the team.

What is an example of an agent hook?

Here is an illustrative example. Acme Co. sells furniture online. A developer at Acme asks a coding agent, e.g. Claude Code, to "Let customers edit their delivery address during checkout." The web store's repository has this Stop hook in .claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/run-tests.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

The hook runs this script, which is marked executable with chmod +x so the harness can start it. It checks for uncommitted changes and then runs the unit tests:

#!/bin/bash
# .claude/hooks/run-tests.sh
if [ -z "$(git status --porcelain)" ]; then
  exit 0  # no uncommitted changes, so the agent may stop
fi
if ! npm test > /tmp/acme-tests.log 2>&1; then
  echo "Unit tests failed. Read /tmp/acme-tests.log and fix the code." >&2
  exit 2  # block the stop and send this line to the agent
fi
exit 0

The turn then runs in six steps:

  1. The agent edits checkout.js and checkout.test.js, and ends its final message with "Done."
  2. The harness fires the Stop event and runs run-tests.sh.
  3. The script finds 2 changed files with git status --porcelain, so it runs npm test.
  4. One cart test fails. The address saved, but the cart emptied.
  5. The script exits with code 2. The harness keeps the turn open and sends the script's error line to the model.
  6. The agent reads /tmp/acme-tests.log, fixes checkout.js, and tries to stop again. The tests pass, the script exits 0, and the turn ends.

This example is simplified. A real hook would also test commits the agent made during the turn, which git status --porcelain does not list.

What is a Stop hook?

A Stop hook is an agent hook that runs when the agent finishes its response, before control returns to the developer. In Claude Code, the hook blocks the stop by exiting with code 2, or by printing JSON with "decision": "block" and a reason. The model receives the reason and keeps working. Codex accepts the same exit code and JSON, and sends the reason to the model as the next prompt.

In Claude Code, a Stop hook runs after each response, not only at the end of the task, e.g. when the agent stops to ask a question. It does not run when the developer interrupts the agent. A PostToolUse hook can run the tests after each file edit instead, which reports a failure sooner but makes each edit wait for a test run. A hook that runs in the background avoids that wait, but it cannot block.

A Stop hook turns part of a team's definition of done into a check that runs, e.g. "the unit tests pass." It answers a "done" claim with a test run, not with the agent's own summary.

A Stop hook that blocks on a condition the agent cannot meet keeps the turn running. Claude Code sets a stop_hook_active field to true when a Stop hook already continued the turn, so a hook can read it and stop blocking. Claude Code also ends the turn after 8 blocks in a row by default. Cursor gives each Stop hook a loop limit for the same reason.

What are the limits of agent hooks?

Agent hooks have these limits:

  • A hook checks only what its command checks. A passing Stop hook shows that the suite passed, not that the feature works.
  • The agent can change what the hook runs. The agent can edit the tests so they pass, which is test tampering. It can also edit the hook's settings, but in Claude Code a write to the .claude settings folder needs approval unless permission checks are turned off.
  • A broken hook usually lets the step through. In Claude Code, only exit code 2 blocks on its own for most events. A script that exits 1 without a JSON decision does not block, and neither does a mistyped script path.
  • Hooks add time to the loop. The harness waits for a hook to finish. A slow check on each tool call repeats that wait many times per turn.
  • A hook that blocks commands is not a hard rule. A PreToolUse hook that matches command text can miss a command written another way. Claude Code's documentation recommends permission rules, not hooks, for a hard allow or deny. A sandbox limits what a command can reach when a hook misses it.

How are agent hooks different from Git hooks?

Both are hooks, which are scripts that a tool runs at set points in its workflow. A Git hook runs when someone runs a Git command, e.g. git commit. An agent hook runs inside the agent's loop, even before any commit exists, and no flag the agent adds to a command, e.g. --no-verify, turns it off. When an agent runs git commit, the Git hook still runs, and a PreToolUse hook can block a git commit --no-verify that would skip it. Outside the agent's loop, the choice between a pre-commit hook, a pre-push hook, and CI depends on how long each check takes and whether it can be skipped.

FAQs

What are Claude Code hooks?

Claude Code hooks are the agent hooks that Claude Code runs at named events in its loop, e.g. when the agent tries to stop. Each hook runs a handler, usually a shell command. A command hook can block a tool call or a stop by exiting with code 2.

Where are agent hooks configured?

Agent hooks are configured in the coding agent's settings files. In Claude Code, a hook in a user's own settings applies to every project. A hook in a project's settings file can be committed and shared with the whole team.

Can hooks slow down a coding agent?

Hooks can slow down a coding agent, because the harness waits for each hook to finish unless it runs in the background. A slow check on each tool call repeats that wait many times per turn. A matcher that limits the hook to the tools it needs keeps the wait short.

Can an agent skip its own hooks?

An agent cannot turn off its own hooks with a flag on a command it runs, because the harness runs them, not the model. An agent can still weaken a hook by editing the tests it runs or the hook's settings. In Claude Code, a write to the settings folder needs approval unless permission checks are turned off.