Comparison · Hooks

Claude Code Hooks vs Git Hooks

Git hooks fire on repository operations; Claude Code hooks fire in the agent tool loop. They see different things and carry different trust guarantees — so robust AI repos layer both rather than picking one.

Key takeaways
  • A Git hook sees a repository event; an agent hook sees an agent event — each is structurally blind to the other's layer.
  • Layer them: agent hooks capture the prompt and tool trace while working; Git hooks and CI enforce at the commit and push boundary.
  • Trust differs — client-side Git hooks are local and bypassable with --no-verify, while a sandbox can pin an unkillable managed-settings hook.

AI agents now author a growing share of the commits in many repositories, which makes one question routine: what produced this change, and was it checked before it landed? The answer lives in two automation layers that look alike — both are hooks, both run your scripts at a defined moment — but fire on different lifecycles. Git hooks run when Git performs a repository operation: commit, merge, applypatch, push. Claude Code (and Codex) hooks run inside the agent's tool loop: a prompt arrives, a tool runs, a turn ends. Because they observe different lifecycles, they see different things, fail in different ways, and carry different trust guarantees. They are complementary, not interchangeable — and inside an auditable workspace you layer both: agent hooks capture the prompt and trace, Git hooks and CI enforce at the commit and push boundary.

Git hooks: repository lifecycle

The official Git hooks documentation says hooks are programs placed in a hooks directory, usually $GIT_DIR/hooks or a path configured with core.hooksPath. Git invokes them at specific moments in Git operations. A pre-commit hook can block a commit. A commit-msg hook can validate the message. Server-side hooks can inspect pushes.

Git hooks are excellent for repository policy: formatting, linting, secret checks, commit-message conventions, and push-time enforcement.

Claude Code hooks: agent lifecycle

Anthropic's Claude Code hooks reference describes hooks as user-defined commands, HTTP endpoints, or prompt hooks that execute at lifecycle events. Events include SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop, and others. Command hooks receive JSON context on stdin.

That makes Claude Code hooks useful for agent-aware automation: inject context at session start, inspect tool calls before execution, record edits after tool use, summarize state when a turn stops, or add deterministic feedback after a file change.

What each layer can — and can't — see

The two layers are blind to each other by construction. A Git hook is handed a tree and a set of refs; it never learns which prompt motivated the change or which files the agent read before writing one. An agent hook is handed the prompt and the tool call; it never sees a teammate who commits from a plain terminal, or a force-push from CI. The table maps the events most worth wiring to what each can observe and whether it can stop the operation.

Hook (layer)Fires atSeesBlind toCan block?
pre-commit (Git)start of git commitstaged tree, diffthe prompt; files only readyes — bypassable with --no-verify
prepare-commit-msg / commit-msg (Git)message built / validatedcommit message + sourceagent intent behind ityes (commit-msg) — bypassable
pre-push (Git)before refs leavelocal commits, target remotethe session that wrote themyes — bypassable
post-merge (Git)after a merge completesnew worktree statewho/what authored itno — informational
pre-receive (Git, server-side)on the remote, at push receiptproposed refs + objectslocal config, the promptyes — not client-bypassable
SessionStart (agent)session opensprior context, cwdrepo ops outside the sessionno — injects context
UserPromptSubmit (agent)human submits a promptthe verbatim promptany Git operationcan add context / block the prompt
PreToolUse (agent)before a tool runstool name + args (e.g. Bash, Edit)repo-wide stateyes — can deny the tool call
PostToolUse (agent)after a tool runsthe edit/read and its resultan out-of-band commitno — observe / give feedback
Stop (agent)turn endsthe full transcripta later push or CI runno — summarize / checkpoint

Read down the two halves and the asymmetry is the whole point. The Git rows are tree- and ref-centric and blind to intent; the agent rows are intent- and tool-centric and blind to any repository operation that happens outside the session. Use only Git hooks and you know a commit landed but not which prompt caused it or what the agent inspected first; use only agent hooks and you have rich session context but no gate at the final commit or push.

How h5i combines them

h5i's Claude Code hook commands are designed for agent context. h5i hook session-start prints prior context into the new session. h5i hook claude sync reads JSON from Claude Code as a PostToolUse handler and emits h5i context traces. h5i hook claude finish checkpoints recent context when a turn stops, and h5i hook claude prompt captures the verbatim prompt as a UserPromptSubmit handler.

You do not wire these handlers in by hand. h5i hook setup prints the copy-paste install instructions, and h5i hook setup --write merges the wiring straight into the agent config for you (idempotent — safe to run twice):

$ h5i hook setup                       # print the wiring; write nothing
$ h5i hook setup --write                # write Claude AND Codex config (project scope)
$ h5i hook setup --write --target claude  # write only .claude/settings.json
$ h5i hook setup --write --scope user   # write ~/.claude/settings.json (all projects)
$ h5i hook setup --write --wrap-bash    # also route every agent Bash through h5i capture run
$ h5i hook setup --write --team         # also register the team peer-review Stop hook

That can coexist with Git hooks for conventional repository policy. For example, Claude Code hooks can capture the prompt and tool trace, while a Git pre-commit hook runs tests or blocks secrets.

The h5i hook commands

The setup command is the entry point; everything else is a handler the agent runtime invokes at a lifecycle event — you never type those by hand. Start with these setup options:

OptionWhat it does
setup (no flag)Prints copy-paste install instructions. Writes nothing.
--writeMerges the hook wiring into the agent config. Idempotent.
--target claude|codexWhich config to write. Omit to write both.
--scope project|userDefault project (repo ./.claude/settings.json); user writes ~/.claude/settings.json for every project.
--wrap-bashOptional. Also registers the Bash capture-wrap PreToolUse hook that routes each agent Bash command through h5i capture run (token-reduced summary; full raw kept for h5i recall). Off by default — note that permission allowlists then match the rewritten h5i capture run … command, not the original.
--teamOptional. Also registers the team peer-review Stop hook (h5i team agent hook); a no-op outside a live h5i team round, safe to leave on.

For Claude, --write installs four handlers (plus the optional fifth with --wrap-bash):

HandlerEventWhat it does
h5i hook session-startSessionStartInjects prior context — goal, recent milestones, decisions/actions, open TODOs — into the new session. (Wired for Claude and Codex.)
h5i hook claude syncPostToolUse (Edit|Write|Read)Emits an OBSERVE trace for Read, an ACT trace for Edit/Write; on Read also injects prior reasoning about that file.
h5i hook claude finishStopMines THINK entries from the transcript plus NOTE entries for deferrals/placeholders/unfulfilled promises, and auto-checkpoints a context milestone.
h5i hook claude promptUserPromptSubmitRecords the verbatim human prompt; a later h5i capture commit uses it as the recorded prompt (it wins over an agent-authored --intent).
h5i hook wrap-bashPreToolUse (Bash)Optional (--wrap-bash). Rewrites the Bash command into h5i capture run …; skips h5i's own commands, top-level cd, and non-git dirs, and fails open so the original command still runs.

Codex has no PostToolUse or UserPromptSubmit, so it derives the same prompt and OBSERVE/ACT signals by parsing its session JSONL. --write wires SessionStarth5i hook session-start and Stoph5i hook codex finish --quiet; the matching h5i hook codex prelude and h5i hook codex sync handlers cover its tool loop. The optional --wrap-bash hook is not Claude-only: --target codex --wrap-bash writes the same PreToolUse / Bash handler into .codex/config.toml.

A worked layering: agent hooks and Git hooks together

Wiring both is mechanical. The agent hooks record intent and trace while the agent works; the Git hooks gate the result when it tries to land. Here is a minimal pairing. The agent side declares two handlers in .claude/settings.json:

{
  "hooks": {
    "UserPromptSubmit": [
      { "hooks": [{ "type": "command", "command": "h5i hook claude prompt" }] }
    ],
    "PostToolUse": [
      { "matcher": "Edit|Write|Read",
        "hooks": [{ "type": "command", "command": "h5i hook claude sync" }] }
    ]
  }
}

The Git side is two ordinary scripts. A .git/hooks/pre-commit gate runs before the snapshot is written:

#!/bin/sh
# block a failing or secret-bearing tree before it becomes a commit
cargo test --quiet || exit 1
if git diff --cached | grep -nE 'AKIA|BEGIN [A-Z ]*PRIVATE KEY'; then
  echo "secret-like content staged" >&2
  exit 1
fi
exit 0

…and a .git/hooks/pre-push gate enforces policy at the boundary refs cross:

#!/bin/sh
# refuse a direct push to the protected branch
if [ "$(git rev-parse --abbrev-ref HEAD)" = "main" ]; then
  echo "direct push to main blocked" >&2
  exit 1
fi
exit 0

What each catches that the other can't: the UserPromptSubmit handler records the prompt verbatim — the pre-commit hook will never see it. The pre-commit hook blocks a failing or secret-bearing tree for everyone, including a teammate who never opened an agent — and no agent hook can reach that path. Run them together and the final review has both halves: the agent trace (prompt → reads → edits) and the conventional Git verdict (tests passed, no secrets, branch policy held). In practice you don't hand-edit the agent side — h5i hook setup --write merges exactly these handlers (and three more) — but the Git side stays yours to own.

Trust and determinism: advisory vs enforced

The two layers do not offer the same guarantee, and conflating them is the costliest mistake. The right question for any hook is not "does it run?" but "who can turn it off?"

Git hooks are local and advisory. They live in $GIT_DIR/hooks (or a path set by core.hooksPath) and are not tracked in the repository by default, so a fresh clone has none of them until each contributor installs them. Any of them is bypassable in one flag: git commit --no-verify skips pre-commit and commit-msg; git push --no-verify skips pre-push. A client-side Git hook is therefore a convenience for the cooperative, never a control against the determined. The only Git hook that enforces for everyone is a server-side one — pre-receive / update on the remote, or an equivalent required CI check — because the client cannot opt out of it.

Agent hooks inherit the trust of the config that declares them. A .claude/settings.json or .codex/config.toml hook is exactly as binding as that file: whoever can edit the config — or pass disableAllHooks — can switch it off. That is fine for observability you control, and inadequate when the point is to observe code you don't trust.

h5i's sandbox closes that specific gap. When it runs an agent in a box, it writes a minimal managed-settings file carrying only the wrap-bash hook and bind-mounts it read-only at /etc/claude-code/managed-settings.json. Claude's managed scope sits above the normal precedence merge, so a higher-priority user setting can't remove it, and it survives disableAllHooks; the in-box agent can't write the root-owned path, so it cannot silence its own observation. That turns an advisory hook into an enforced one — the same shift you get by moving a Git check from pre-commit to pre-receive. (The managed-settings injection is Claude-specific; Codex hardening is handled separately.)

Failure modes worth knowing

Conclusion: layer, don't choose

Treat this as a layering decision, not an either/or. Use agent hooks — SessionStart, UserPromptSubmit, PreToolUse, PostToolUse, Stop — for intent and session memory while the agent works. Use Git hooks, with a server-side check or required CI behind them, to gate the tree and refs at the repository boundary where the rule has to hold for every contributor and tool. h5i wires the agent side for you and joins both records, so a later reviewer reads one provenance trail — prompt, reads, edits, and the Git verdict — instead of two disconnected halves.

FAQ

Can a Git hook capture Claude Code prompts?

Not reliably by itself. Git hooks run at Git operations and do not naturally receive Claude Code session context.

Can a Claude Code hook replace pre-commit?

It can run checks, but repository enforcement still belongs at Git boundaries if you want the policy to apply to all contributors and tools.

Which one should I install first?

For AI provenance, install Claude Code hooks first. For enforcement, keep or add Git hooks and CI.

Can an agent disable its own hooks?

If it can edit the config that declares them, yes — that includes passing disableAllHooks. h5i's sandbox prevents this for Claude by pinning the wrap-bash hook in a read-only managed-settings file the in-box agent can neither write nor override, so observation survives even a disableAllHooks.

Sources and verification

This article avoids vendor-specific claims that were not checked against primary docs or local h5i CLI behavior.

Try h5i on your next AI-assisted branch

Create a sandboxed workspace, capture the run, and post a review-ready PR brief. h5i records prompts, context, test evidence, review signals, and agent messages alongside normal Git history.

Star on GitHub Read the guides