frit is a command-line tool for developers who run coding agents: it finds every plan across many repositories, branches and machines, claims one with a single atomic git push, and hands it to an agent. The name is a glassmaking term for material prepared before the melt; the naming note explains the choice.
Work on a developer's machine is scattered: many repositories, many branches in each, worktrees for some, agents running in a few, and a plan file that may exist on one branch only. No single view shows all of that, and nothing stops two agents from starting the same plan; why frit exists records the gap.
frit is that single view. It does three things:
- It reads every plan file on every branch of every repository under one root directory, without checking anything out.
- It answers which plans are ready: their dependencies are done and nobody holds them.
- It claims a plan by pushing a branch to the git remote, then hands the plan to an agent in a worktree of its own. That claim is the one mutation frit owns on the shared refs: it never edits a plan, never writes a prompt of its own, and never reads an agent's conversation. architecture.md calls this the one-mutation rule.
mdsmith parses the markdown. herdr runs the panes and worktrees that agents work in. frit joins both with what git holds; how frit and mdsmith fit together draws the line.
flowchart LR
subgraph origin["git origin, shared by every machine"]
plans["plan files on every branch"]
claims["claim refs: plan/<id>"]
end
subgraph host["one machine"]
frit["frit"]
herdr["herdr: panes, worktrees, agents"]
mdsmith["mdsmith: markdown parser"]
end
plans -->|read| frit
claims -->|read| frit
frit -->|"the claim, one push"| claims
herdr -->|"which lane has a live agent"| frit
frit -->|"the worktree and the pane"| herdr
mdsmith -->|library| frit
Everything the machines must agree on lives on the git remote. A ref
push is the one operation every machine can race on safely. A fact
that never reaches the remote, such as a running pane or an open pull
request, stays on the machine that has it. frit reads that fact from
herdr on that machine, or asks the agent. It never guesses it from
the refs. CLAUDE.md states this rule and
architecture.md explains it. --hosts a,b
reads other machines' herdr over ssh; a host that does not answer is
reported as a problem and its lanes show their last cached presence,
while ? in the board's agent column means the local herdr socket
did not answer. When two machines claim one plan at once, the remote
accepts the first push and the second reports who won.
A plan moves through a small set of states; the status glyph lives in the plan file's front matter, the hold on the remote.
stateDiagram-v2
direction LR
[*] --> NotStarted: plan file lands, status 🔲
NotStarted --> Held: frit claim pushes plan/id
Held --> NotStarted: frit release
Held --> InProgress: first work commit, status 🔳
InProgress --> Done: last phase closes, status ✅
Done --> [*]: branch merges, frit reap deletes it
How claiming works has the rules for each edge, including takeover of a stale lease and the rescue ref that parks unmerged commits.
| Word | Meaning | Defined in |
|---|---|---|
| fleet, root | every git repository under one directory, the root | claiming.md |
| plan | a markdown file with id, title, status and, optionally, model up front | plan/proto.md |
| hold, lease | the branch plan/<id> on the remote; its tip says who holds it |
claiming.md |
| lane | one worktree on one host, working one plan; an agent's pane rides it | lease-protocol.md |
| rescue ref | where unmerged commits are parked before a lane is torn down | claiming.md |
| tier | the model named in a plan's front matter, from the vocabulary plan/proto.md declares | plan/proto.md |
frit needs Go 1.25 or newer to build from source:
go install github.com/jeduden/frit/cmd/frit@latestOr download a binary from the releases page and check its provenance before you run it:
gh attestation verify frit-linux-amd64 -R jeduden/fritfrit version prints the tag a release was built from, and dev for
a source build. Three other tools matter:
- git must be on
PATH. frit runs it as a subprocess for every read and for the claim, as CLAUDE.md requires. - herdr must be running for any verb that touches a lane:
claim,start,open,nudge,messageandyield. The survey verbs work without it:boardthen shows?in the agent column, andwhoprintsno live agentsand reports the socket as a problem. - mdsmith lints plans and regenerates the plan index. Install it
with
go install github.com/jeduden/mdsmith/cmd/mdsmith@latest.
export FRIT_ROOT=~/git
cd ~/git/myrepo && frit init --mdsmith .Getting started carries the rest: writing a plan, checking it, claiming it, and watching it run.
frit --help lists every verb, and frit <verb> --help its flags.
The full reference — every verb grouped as survey, discover, lease,
drive, clean and setup, with the conventions that hold across them —
is in docs/commands.md.
Every verb takes --json; the table and the document come from one
model, so they never disagree.
frit orphans --json | jq '.repos[] | select(.unstaffed | length > 0)'UX principles has the rules that make the document safe to write against, and the golden files that pin them.
.frit.yml holds per-repository settings; frit's own settings, such
as --root, resolve from the command line, the environment, a config
file and the user config, most specific first.
docs/configuration.md has the keys and the
exact order.
frit ships the instructions an agent needs to drive it. frit skills
writes seven Claude Code skills into a repository's .claude/skills:
| Skill | What the agent does with it |
|---|---|
plan-pick |
find the next unheld plan, claim it, start its lane |
plan-phase |
execute one phase of a plan, test first, and close it |
plan-handoff |
close a phase: write the handoff, flip its status |
plan-new |
write a plan that passes the schema on the first try |
plan-sync |
reconcile plan statuses against what drift found |
plan-tidy |
read orphans and stale, then act with yield, release, reap |
plan-drive |
survey the board and drive a lane up the ladder: open, nudge, message, start |
The skills are embedded in the binary, and --via "go run ./cmd/frit"
changes how they invoke frit. The prompt frit composes for a pane is
/plan-phase <id> [phase], from
internal/dispatch; start --note
and --edit amend it, and message sends whatever text you give it.
Development has the rest.
Every release is on the
GitHub releases page, or
from a terminal with gh release list -R jeduden/frit. It carries
notes from the merged pull requests, five platform binaries, a
checksum file and a provenance attestation. A release is made from
the Actions "Run workflow" button on
release.yml with a version such as
v0.11.0; the workflow checks the version, runs the suite, builds the
binaries, and creates the tag only once they exist.
Development has the details.
| Page | Answers |
|---|---|
| CLAUDE.md | the rules the code and its agents follow; the current record |
| PLAN.md | what is planned, in progress and done |
| docs/getting-started.md | init to first claimed lane, one command at a time |
| docs/configuration.md | the .frit.yml keys, with defaults and comments |
| docs/architecture.md | what frit, mdsmith and herdr each own |
| docs/claiming.md | how a lease is made, kept, taken over, yielded and scavenged |
| docs/commands.md | every verb, grouped, and the conventions across them |
| docs/reaping.md | the orphan categories and what reap may delete |
| docs/ux-principles.md | why the verbs, flags and JSON behave as they do |
| docs/development.md | build, test, lint, the scenario matrix, skills, CI and release |
| docs/research | dated notes on how each decision was reached |
To contribute, follow CLAUDE.md: a failing test, the code
that passes it, one small commit, with mdsmith check . clean.