Skip to main content

Command overview

beacon git records which agent sessions produced a commit, as a git note on the commit.
Command syntax
beacon git setup installs hooks so every new commit in the repository is linked as it is made. beacon git link links one commit by hand, which is also how to link commits made before setup. A link names a session by its harness and session id, for example beacon:claude_code/0f8e2c1a-…. The note carries identifiers only, never prompts, output, or diffs: notes can be shared with a remote, and the session’s content stays in the local runtime log. beacon handoff export <session-id> reads a linked session back.

How sessions are matched to a commit

Beacon already records every file a supported runtime writes, so matching a commit to its sessions is a read of the local runtime log. No state file is kept and nothing touches the network.
  1. Window. Beacon reads the log from the parent commit’s time up to the commit. The window always covers at least --min-lookback (2 hours) and never more than --max-lookback (24 hours) before the commit. Events after the commit are ignored, because they cannot have produced it.
  2. Evidence. A session wrote a file when the log has, for that session, a file.created, file.modified or file.deleted event, a tool event whose file.operation is a write, a shell command whose target is plain to read (rm, mv, cp, install, ln, touch, truncate, tee, sed -i, perl -pi, git mv, git rm, output redirection), or an apply_patch envelope. Reads do not count, and neither does a path that depends on a variable, a glob or a command substitution.
  3. Overlap. Beacon compares those paths with the files the commit changes, counting both sides of a rename. A session that wrote at least one of them is linked. Sessions are ordered by how many of the commit’s files they wrote, then by how recently.
For a merge commit, only files where the result differs from every parent count: conflict resolutions and edits made while merging. The files a merge brings in were written by the other side’s commits, so a clean merge or a git pull links nothing. A link is an inference, not an observation. A session that edited a file a person then rewrote by hand is still linked, and a session that wrote nothing the commit kept is not.

beacon git setup

Installs Beacon’s git hooks in the current repository.
Example output
  • post-commit links the new commit, exactly as beacon git link would. It reads the local runtime log and writes a git note, typically in tens of milliseconds, and gives up after three seconds rather than hold a commit, recording in the runtime log that it did (see below). It never prints, never touches the network, and never fails a commit.
  • post-rewrite runs after git commit --amend and git rebase and tidies the note on each rewritten commit, folding the note git copied from the old commit into the one the post-commit hook just wrote.
  • notes.rewriteRef makes git copy a commit’s links to the commit that replaces it when you amend or rebase.
Rebases, cherry-picks and reverts are not attributed afresh: they replay existing commits, and their links, if any, travel with notes.rewriteRef. A hook already in place keeps working. Beacon writes its own beacon-post-commit and beacon-post-rewrite scripts and adds a short marked block to the top of the hook that calls them, so a hook ending in exit 0 still runs Beacon’s part. Only shell hooks are edited: if the hook is another language, setup stops and prints the line to add yourself. The scripts call Beacon by the absolute path it had at setup and fall back to beacon on PATH, so a GUI git client without Beacon on its PATH still links commits. Hooks live in the repository’s shared hooks directory, so one setup covers every linked worktree. Set BEACON_GIT_HOOKS=0 to skip the hooks for one command:

Hook managers and core.hooksPath

Tools such as husky and lefthook point core.hooksPath at a directory they own, often one tracked in the repository. setup refuses to write there by default and prints the line to add to that tool’s post-commit hook instead:
Pass --hooks-path to install into that directory anyway.

beacon git status

Shows whether each hook is installed, reports an install that is broken (a hook calling a script that is gone, or the reverse), and says whether notes.rewriteRef is set. The overruns line counts this repository’s hook runs that ran out of time in the last seven days, how many of them were cut short, and the most recent. --json prints the same as JSON, with a top-level installed field and the counts under budget_overruns. Links start out local. Nothing leaves the machine until you share them, and sharing is off unless you turn it on.
This does two things in the repository:
  • Fetching. Each remote gets a fetch refspec, +refs/notes/beacon:refs/notes/beacon-remotes/<remote>, so a plain git fetch or git pull brings teammates’ links into a read-only copy. beacon git notes reads that copy alongside your own links. Your refs/notes/beacon is never overwritten by a fetch, so links you have not pushed yet are safe. A fetch or pull that names a refspec on the command line, such as git pull origin main, skips configured refspecs; run beacon git notes fetch to fetch links then.
  • Pushing. A pre-push hook shares your links whenever you git push. It fetches the remote’s links, merges them with yours (every line of both is kept), and pushes only refs/notes/beacon to the same remote. It runs with --no-verify, so your other pre-push hooks are not run twice. It gives up after ten seconds and never fails your push: if sharing fails, it prints one line and your code is pushed as usual.
This is the one Beacon hook that reaches the network, and only during a push you started, to the remote you are pushing to. BEACON_GIT_HOOKS=0 git push skips it for one push, and beacon git setup --share-notes=false turns it off. To share by hand instead, without the hook:
A note holds only beacon:<harness>/<session-id> lines, optionally followed by the session’s share URL, so sharing a link shares that a session produced the commit (and, when the session was shared, where its trace is), not what happened in it. The session itself stays in the runtime log of the machine that ran it.

Share URLs in notes

A note line can also carry the share URL of a session that was shared to beacon.sh, after the session (beacon:<harness>/<session-id> <url>). Beacon takes these URLs only from local session.shared events in the runtime log, so writing them never touches the network, and it writes or shows only https URLs on beacon.sh or on the Beacon service the CLI is configured for: a note can come from anyone who pushes to the remote, so a URL on any other host is ignored and the link is shown without it. beacon git notes prints the URL after the session, and --json includes it as url. setup --share-sessions goes further and has the pre-push hook share the sessions linked to pushed commits (at most 100 commits and 10 sessions per push, within the hook’s ten seconds, never failing the push) and write their URLs into the notes. It is off by default, uses the same beacon login sign-in or BEACON_API_KEY as beacon share, shares nothing without one, and is stored in the clone’s own git config (beacon.shareSessions, beacon.shareVisibility). Session sharing is a beacon.sh feature, documented at docs.beacon.sh.

beacon git remove

Removes Beacon’s scripts, its blocks in the hooks, its notes.rewriteRef value, and the fetch refspecs --share-notes added, leaving everything else as it was. A hook that held nothing but Beacon’s block is deleted. Links already written stay in refs/notes/beacon. Links a commit (default HEAD) and writes the note.
Example output
Running link again adds only what is new. Links already on the commit, including a teammate’s, are kept, as is any other text in the note.

beacon git notes

Lists recent linked commits reachable from a revision (default HEAD).

Runtime log events

Each new link is also recorded in the runtime log as a session.commit_linked event in the linked session’s own timeline, so the dashboard, beacon endpoint traces, handoff briefs, rules and any forwarding destination see the commit alongside the work that produced it. link and the hook record an event only for a link they add: a dry run, or a commit already linked to the session, records nothing.
Example event (abridged)
  • event.fidelity is inferred: the link is Beacon’s conclusion from file overlap, not something the runtime reported.
  • vcs.ref.head.name is the branch HEAD was on at commit time. It is empty for a detached HEAD, or for an older commit linked by hand, whose branch git cannot say.
  • vcs.repository.url.full is the URL of the remote the branch tracks, or origin, with any user name, token, or query string removed.
  • The event carries identifiers and counts only: no file paths, diff, or commit message.
A rule can match on these fields, for example a commit to main that one session wrote in full:

Hook runs that run out of time

The hooks hold up a command a person is waiting on, so each gives up when its budget runs out: three seconds for post-commit and post-rewrite, ten for pre-push. Giving up is silent on the terminal, and a post-commit hook that gives up leaves the commit unlinked. So each run that runs out of time writes one git.hook_budget_exceeded event to the runtime log.
Example event (abridged)
  • vcs.hook.phase is the step the hook was in when its time ran out. Post-commit runs open_repository, attribute (reading the runtime log and writing the note) and record_link_event; post-rewrite runs open_repository and normalize_notes; pre-push runs open_repository, share_sessions (with --share-sessions) and push_notes.
  • vcs.hook.elapsed_ms is the whole run and vcs.hook.phase_elapsed_ms the part of it spent in that phase, so a slow earlier step shows as the difference. Pre-push holds its share phase to six of its ten seconds, so a share phase that runs out records an elapsed time under the budget.
  • vcs.hook.git_spawns is how many git processes the run started. Each costs a few milliseconds on macOS and Linux, and can cost far more on a Windows machine that scans every new process.
  • vcs.hook.cut_short is true when running out of time left work undone: a commit left unlinked, a link event, note or share URL left unrecorded, or the notes ref left unpushed. It is false for a run whose last step finished after the deadline, which is recorded as info rather than low.
  • repository is empty when the hook ran out before it could resolve the repository.
beacon git status counts these events for the repository, and beacon endpoint doctor for every repository. A commit left unlinked can be linked with beacon git link <commit>.

Note format

Links live under refs/notes/beacon, one per line:
<harness> is Beacon’s canonical harness name (claude_code, codex_cli, cursor, …). A session id has no whitespace, no control or non-ASCII characters, and at most 256 characters. The first field after whitespace is the session’s share URL when it is an allowed https URL (at most 512 characters); anything else after whitespace is reserved for later fields and ignored. Lines that do not start with beacon: are ignored. When the same link appears with and without a URL (two people’s notes merged), the line with the URL is kept. Beacon updates the note with a compare-and-swap on the notes ref, so two commits made at the same moment in two worktrees of one repository never lose each other’s links. To see links in git log, including those fetched from remotes: