Connect sessions to Git commits

Install Sher's Git and agent hooks to find the conversation behind a commit.

Sher can record commits and connect them to the conversation that produced a surviving file change. This runs entirely on your machine: no login, no upload, no server involved. GitHub PR tracking and comments are a later stage.

What it does

After you install Sher's Git hooks and an agent bridge, Sher watches commits made in a repository and matches them against tool edits it saw in your agent sessions. A match becomes a link between a session and a commit only when the evidence is strong enough — see What a link means. Everything indexed this way stays local; sher context reads it back without a network round trip.

Install

Run these from inside a Git working tree, using the Sher build you want linking to observe:

sher hooks install
sher hooks agents install claude
# Or: sher hooks agents install codex
# Or: sher hooks agents install omp

Restart the agent so it loads its bridge. Agent installation uses the agent's active configuration directory; --config-dir PATH selects an explicit one. Installing a hook does not bypass the agent's own hook trust or policy checks.

Git hooks apply to the repository, including any linked worktrees. Install the agent bridge once per configuration or profile you use — a second machine or a second agent account needs its own hooks agents install.

Keep the Sher daemon running so it can discover new transcripts and process receipts:

sher daemon install
sher daemon start

For a one-off local trial without the daemon, run sher daemon --once.

Commits without a hook receipt

Git runs Sher's hook for every ordinary git commit once the hooks are installed. The daemon also covers the commits the hook never saw:

  • commits made before you ran sher hooks install
  • commits made while another tool had replaced Sher's core.hooksPath setting

On each audit pass the daemon walks the local branches and the checked-out HEAD of every worktree in repositories it knows about. It does not walk remote-tracking branches or tags. It attributes a commit only when its branch is checked out in a worktree; a commit on a branch that no worktree has checked out is recorded as seen but never matched.

A commit found this way is held to the same proof as a hook receipt. It can link as a contribution, but never as an invocation, because nothing records which session ran Git.

Find the conversation

Once you or the agent has committed a captured change:

sher hooks status
sher context query --commit FULL_COMMIT_HASH
sher context get CONTEXT_ID

sher context query --commit returns accepted links only. Widen it with --decision any (or a specific decision such as --decision candidate) to see relationships Sher considered but did not accept automatically, or that you or the agent later rejected. Add --json for machine-readable output.

  • Contributed — a successful, identified tool edit has a uniquely verifiable change still present in the commit. A human can commit it after the agent exits.
  • Invoked — evidence identifies the session and tool call that ran Git. It does not mean that session wrote everything in the commit. Only a commit seen by Sher's Git hook can have an invoker.
  • Candidate — evidence is incomplete, or competing edits prevent confident attribution. A candidate is inspectable with --decision candidate, never returned as a link by default. Sher does not turn timing or a shared branch into authorship.

Sher's matcher is deliberately strict, because a wrong link is worse than a missing one:

  • Overlapping or ambiguous edits. If more than one tool call plausibly produced the same surviving change, both stay candidates.
  • Shell edits that are not proven. A shell command (for example sed -i through Bash) can produce a contribution only when all of these hold: the command text names the file, Sher's before/after snapshot shows a change that survives in the commit, no other recorded writer touched that file, and the transcript shows the call succeeded. Snapshots are bounded: a call that changes more than eight files, or whose snapshot fails or times out, gets no link. Today only Codex records whether a shell call succeeded, so shell-edit links appear for Codex. A shell edit made through Claude Code or OMP stays pending and is not linked.
  • A shell call that ended before the commit. It can be a contributor, but it is never the invoker, even in the session that later ran Git. A single call that both edits and commits can be both.
  • Rewrites. After an amend, rebase, or squash, Sher records which new commit replaced which old one, as Git reports it. Each new commit is checked again against its own parent, with the same proof as any other commit. A link is never copied from an old commit to a new one, and the old commit keeps its link. An amend or a clean rebase usually links again. A squash that merges two edits of the same lines does not. sher context query --commit shows rewritten_to on an old commit and rewritten_from on a new one. A rewrite Sher did not see, such as a force-push from another machine, is not linked again.
  • Large or binary files, and unsupported tool paths. These never receive an automatic contribution claim.

An automatically accepted link can still be weakened by later competing evidence. An explicit correction you make — accepting, rejecting, or reviewing a relationship — is preserved.

Status and removal

sher hooks status
sher hooks agents status claude

sher hooks status prints a plain-language report by default; add --json for a stable, machine-readable form that includes a state field. The state is one of:

  • not_installed — Sher's hooks are not set up here. The message names the install command.
  • installed — Git runs Sher's hooks for this repository.
  • displaced — Sher's hooks are installed, but another tool (for example husky) now sets core.hooksPath. The message names that value and how to fix it: sher hooks install --reinstall chains Sher back in ahead of it, or sher hooks remove --force removes only Sher's own files and leaves the other tool in place. The daemon still observes commits reachable from local branches even while displaced.
  • edited — a hook file no longer matches what Sher wrote. Sher will not overwrite it silently.
  • executable_missing — the Sher executable pinned at install time can no longer be found. Restore it, or reinstall from the executable you want to use.

It also reports how many receipts are pending, parked as invalid, or only partly written, whether any capture was lost, and what the local ledger has stored. A healthy status does not by itself promise complete capture: the daemon may need another pass when a transcript write lags its callback.

Sher owns a private hook directory and forwards to any existing hook, preserving its arguments, input, and exit status. It refuses to silently touch a hook it did not install or that was edited by something else. sher hooks remove restores the prior core.hooksPath; pass --force to remove only Sher's own hook files when another tool has since taken over core.hooksPath.

sher hooks agents remove claude
sher hooks remove

Choose the provider you installed. Removal preserves unrelated agent configuration and restores the Git hook setting that was in place before Sher installed its own. Historical receipts and relationships already recorded remain available through sher context.

Going back to Sher 0.1

Sher 0.1 has no hooks command, so it is still best to remove the hooks and agent bridges before you install it: run sher hooks agents remove <claude|codex|omp> for each agent you connected, and sher hooks remove in each connected repository. Your sessions and Sher's local data are not affected, and Sher 0.1 opens them as before.

Removal first is recommended, not required: the Claude Code and Codex bridges fail open, so if you go back without removing them, Sher just stops capturing and your tool calls keep working. Git still skips your own post-commit and post-rewrite hooks in connected repositories, though, because Sher's Git hook wrapper calls Sher before forwarding to yours, and Sher 0.1 does not understand the wrapper's request. Fix that (or an already-installed bridge you would rather remove by hand) like this:

  • Claude Code or Codex: in ~/.claude/settings.json or ~/.codex/hooks.json, delete every hook whose command ends in hooks agent claude or hooks agent codex.
  • OMP: delete ~/.omp/agent/extensions/sher.ts.
  • Each repository: run git config --unset core.hooksPath, or set it back to the value you had before installing Sher, then delete the sher-hooks directory inside .git.

Supported platforms and providers

Git-linking is qualified on Linux. Runtime checks used Claude Code, Codex, and OMP; all three produced accepted edit contributions, and Claude and Codex also produced separate invocation links. OMP invocation is currently unqualified. Shell-edit links are qualified for Codex only. A Codex shell-intercepted patch can omit a completion callback and fall back to transcript metadata instead of a shell-edit link.

macOS support for Git-linking is pending a maintainer check on a Mac; until then, treat it as Linux-only.

See docs/git-capture.md in the repository for the maintainer-facing reference, including exact provider versions and observed payloads.