Guide 05 · Harnesses

Hooks: which one, for what

A hook is a script that a tool runs at a set moment and that can stop or change the next step. Git, OMP and Claude Code each have their own.

Related exhibit:
03 · A hook that keeps a log
Checked against the source:
Nov 4, 2026
Tools:
Git 2.52 · OMP 18.14.1 · Claude Code 2.1.281

What it is for

A check can live in three places. Git runs hooks around commits and pushes in a repository. An agent harness such as OMP or Claude Code runs hooks around the agent's tool calls. Approval rules such as OMP's bash.patterns sit next to them: they are not hooks, but they often do the same job. This guide helps you pick the place for a check.

Pick a hook

I want to…UseBecause
Stop any commit that adds a marker, a secret or a failing checkGit pre-commitIt runs for every commit in the repo, from a person or an agent. Any non-zero exit stops it.
Enforce a commit message formatGit commit-msgIt gets the message file and can reject or rewrite it.
Refuse a push to a protected branchGit pre-pushIt sees every ref being pushed before anything is sent.
Have a second model review each commitA pre-commit hook that calls an agent CLISee Commit review by a second model.
Stop the agent from running a commandOMP tool_call hook, Claude Code PreToolUse hookIt sees the tool call before it runs, including commands that never reach git.
Forbid one command without writing codeOMP bash.patterns with denyOne line of config. It matches the command text and covers the bash tool only.
Add a step to a command before it runsOMP tool_call returning a new input, Claude Code updatedInputThe agent does not have to remember the step.
Tell the agent something after a tool ranOMP tool_result, Claude Code PostToolUse, with additionalContextThe note reaches the agent before its next step. The call itself cannot be undone.
Run something when a session endsOMP session_shutdown, Claude Code SessionEndFor logs and summaries. It cannot stop anything.

The kinds at a glance

KindRunsSeesStops it withLimits
Git pre-commitbefore git creates a committhe staged change (no arguments)any non-zero exitskipped by --no-verify; only active once core.hooksPath points at it
Git commit-msgafter the message is writtenthe path to the message fileany non-zero exitskipped by --no-verify
Git pre-pushbefore a push sends anythingremote name and URL; one line per ref on stdinany non-zero exitskipped by --no-verify; runs only on the pushing machine
OMP tool_callbefore an agent tool runs, before approvaltool name and input{ block: true, reason }, or a new input insteadtool calls only; loads at session start; 30 s, then the call is blocked
OMP bash.patternsbefore a bash tool commandthe command textdeny or promptliteral text with *; bash tool only; not a sandbox
Claude Code PreToolUsebefore a tool callJSON on stdin: tool_name, tool_input, cwd, session_idexit 2 (stderr goes to Claude) or JSON permissionDecision: "deny"any other exit code lets the call through
OMP tool_result, Claude Code PostToolUseafter the tool ranthe input and the resultfeedback to the agent, a replaced resultcannot undo the call

Git rows: Git 2.52, from its documentation and our tests. OMP rows: OMP 18.14.1 documentation. Claude Code rows: its hooks reference, checked Nov 4, 2026, against version 2.1.281.

Set them up

What hooks are not

A hook sees the text of a command or a tool call, not the intent behind it. A git hook can be skipped with --no-verify, and an agent once got past one of our agent hooks by building the blocked word from parts. Put the real boundary in permissions: what the process may delete and where it may write. A hook is a guardrail on top. Codex's documentation says the same: “Treat tool hooks as a useful guardrail, not a complete enforcement boundary.”

Sources

← All guides