gangline
Many harnesses. One line.
A harness harness. Gangline unifies any CLI coding agent it can get its hands on — Claude Code, Codex, opencode, Pi, whatever ships next — into a tmux-powered team, using each for its strengths, with minimal harness-specific integration.
A gangline is the single rope that hitches many dogs, each in its own harness, to one sled and one musher. The integration point is the attachment clip — never surgery inside the dog.
There is no server, no bus, and no database. tmux already holds the state, so
gang is a CLI you run and it exits.
One message to a Claude Code lead: it hires a Codex worker, waits for the work, then hires an opencode reviewer to check it. Real harnesses, real subscriptions, unedited except for cuts through the waiting.
$ cd ~/my/repo && gang up
$ gang spawn worker -p codex -r worker
spawned worker (codex) in ~/my/repo — watch: gang attach
briefed worker as worker
$ gang send worker --from lead "read the failing test in ci and fix it"
delivered to worker as [gang:lead] (verified in pane, submitted)
$ gang roster
lead claude-code idle (slack tug) 142k/1000k (14%)
worker codex busy (tight tug) 31k/272k (11%)
Install
$ curl -fsSL https://raw.githubusercontent.com/adambiggs/gangline/main/install.sh | sh
Needs git and tmux ≥ 3.2. Clones to ~/.local/share/gangline,
links gang into ~/.local/bin, re-run to update.
Piping a stranger's script into a shell is your call to make —
read it first,
or clone and symlink bin/gang yourself; the installer does nothing more.
How it works
- Agents are tmux windows.
- Spawning is
new-window, killing iskill-window, watching iscapture-pane, and the window name is the agent's identity. Attach and you are looking at your team — the pane is the transcript, and you can type into it yourself at any time. - Every message is attributed and verified.
- A send carries a
[gang:<sender>]prefix, is confirmed by capturing the pane, and only then submitted. No sender means no send. Unverified means a loud failure, never a shrug. - Agents manage their own context.
- A model cannot feel its own token count, so the substrate measures it and warns at configurable bands. The agent compacts itself at the next clean seam, queuing a resume directive behind the compaction so work continues unattended.
- A new harness costs a profile, not an integration.
- Launch command, busy marker, compact command — about ten lines.
Everything else is universal. Profiles scrape the stock TUI: a theme or
UI extension that repaints it takes a shadow profile of your own
(
GANG_PROFILES), re-verified against what you actually run. - Batteries included, every one replaceable.
- Agents spawn with a role brief — lead, worker, reviewer. Point
GANG_ROLESat a directory of your own and any brief becomes yours.
Delivery is measured, not assumed
A send is three reads of the target's input box, not a spray of keystrokes. gang reads the box, pastes, and reads again — the box must have changed. Enter goes in as its own keystroke, and a third read confirms the submit.
Matching the text somewhere on screen would verify against an identical
earlier send still sitting in the transcript; a box that changed is the
harness-independent fact underneath every TUI's rendering, whether it echoes a
paste literally or collapses it into [Pasted text #2]. And the
separate Enter matters: batch text and newline into one burst and a TUI reads
the newline as part of the paste, parking your message in the box as an unsent
draft that scrollback renders exactly like a sent one. If any read disagrees,
the send dies loudly — and nothing was submitted.
Context is measured, not felt
Measure, warn, act — the substrate does the first two, the agent does the third. Usage is read through each harness's own introspection: a statusline beacon, a painted readout, or the session file the harness itself writes. Two warning legs run the same band ladder over that one readout — an in-turn hook where the harness has hooks, an ambient cron patrol where it doesn't — and share one band memory, so an agent is warned once per band, not once per leg.
[gang:patrol] [context-usage] 187k/272k (69%) in context — crossed the
180000-token band. At the next checkpoint (arc boundary), run:
gang compact worker --from worker --resume '<where you are, what is next>'
— it queues behind the turn you are in, and the resume is delivered once
compaction settles.
The agent then compacts itself at the next clean seam — tests green, a
commit made — without being idle first and without anyone watching. The
--resume never rides the input queue behind its own compaction,
where the turn about to be compacted could swallow it; a detached waiter
delivers it at the first moment it cannot be overtaken, and the agent picks it
up the instant it has a context to pick it up into.
Loud refusals, never guesses
gangline's failure mode is a refusal you can read, not a guess you can't.
$ gang send reviewer --from lead "ship it"
gang: refusing to deliver 'ship it': reviewer is gated (hook set) — a
permission prompt only the operator can answer owns the screen (gang attach)
- Gated agents take no keystrokes from anyone. A harness stopped at a permission prompt is waiting on the operator, and a send would answer the dialog — so every delivery path refuses, and patrol names the gate loudly as what the team is waiting on.
- Nudges hold for churning panes and half-typed drafts. Patrol injects only into a pane that is byte-identical across two captures with an empty input box. Holding costs one sweep; interleaving costs the turn.
- An unread context window is admitted, not hidden. A
readout gang cannot reach shows as
-in the roster and is reported as not patrolled, never quietly skipped. - Scrape drift is diagnosed, not endured. Every scraped
marker is pinned to live-verified harness versions;
gang doctorexits nonzero on drift — and says so when a clean bill can't explain misbehavior, because version pins don't watch your UI mods.
The verbs
| command | what it does |
|---|---|
gang up |
the whole setup, no arguments: spawns lead in the current
repo and puts you in the session |
gang spawn <name> -p <profile> -r <role> |
start an agent — the name becomes its tmux window, its identity, and its handle |
gang send <name> --from <you> "…" |
attributed, pane-verified message; delivered mid-turn where the harness takes input during one |
gang roster |
everyone, with harness, state, and context usage |
gang status / context / capture |
one agent's state, window usage, or screen |
gang compact <name> --resume "…" |
trigger the harness's own compaction; the resume lands once it settles. Naming yourself is the ordinary case |
gang patrol |
ambient roster sweep from cron: band warnings in, gate reports out |
gang doctor |
strategy-rot check: installed harness versions against each profile's verified pins |
gang attach / kill / down |
watch the team live; end an agent; end the session |
Extend it
A profile says how to talk to a harness; a role says what an agent is for. They are the same kind of extension point, and neither is an integration.
- A profile is a few lines of shell declaring what gang cannot know generically: launch command, busy marker, compact command, how the permission prompt reads, optional hooks for the context readout and the input box. Markers nobody has watched live stay unset, and gang takes the slower, safer branch rather than guessing.
- A role is a markdown brief — lead splits work and guards its own context hardest, worker reports what changed and what proves it, reviewer verifies claims rather than reading diffs. The brief is pointed at, not pasted, so it survives compaction.
- Both are shadowed, not patched.
GANG_PROFILESandGANG_ROLESare searched before the shipped directories, so a new harness — or a fix for a TUI that moved under you — costs a file of the same name, never surgery on the installed tree.
The trail
The vocabulary is mushing, and every term names something real in the system — the musher's field guide maps all of it. A sample:
| on the trail | on the team |
|---|---|
| tight tug / slack tug | busy / idle — a pulling dog keeps its tugline taut |
| hook set | gated — the snow hook is dug in, and only the musher pulls it |
| checkpoint | the clean seam an arc ends on — tests green, a commit made — where compaction and releases happen |
| dropped | a released agent — deliberate, routine, the musher's call; health, not failure |
| dog in the basket | an idle-parked agent still burning a context window — the anti-pattern dropped exists to prevent |
| intelligent disobedience | a good lead refuses the command onto thin ice — once, short, specific; the musher's word is then final |
Of the 1925 serum run's lead dogs, Togo ran the long, dangerous leg while Balto got the statue — credit your working dogs.
One bash script. Profiles and roles are files you can replace. tmux is not a hack under it — tmux is the substrate, and when the metaphor stops carrying weight, every command still reads literally without it.