ORCHESTRATION Jeecabs/gang ↗

gang

gang mission-control crew logo

A minimal, observable subagent primitive for Pi. Fire up subagents as visible tmux panes you watch live, let them talk over a cross-agent message bus, and observe everything three ways: live panes, a durable log, and a mission-control view — in your terminal (a Pi overlay) and in the browser.

Built on a vendored, fully-owned copy of pi-intercom v0.13.0 (MIT). No external pi-intercom runtime dependency; npm deps install with the package. A dev-only version pin lets Dependabot flag new Intercom releases, while CI requires the vendored version marker to match. Targets @earendil-works Pi 0.85.x (>=0.85.1 <0.86.0).

┌─ tmux panes ──────────────┐   raw per-agent view — watch each member's pi session
│  superintendent │ worker        │
│           │ reviewer      │
└───────────┴───────────────┘
        │ intercom (unix socket: ~/.pi/agent/intercom/broker.sock)
        ▼
┌─ owned broker (vendored) ────────────────────┐
│  • routes messages                            │
│  • TAP  → ~/.pi/agent/intercom/intercom.jsonl │  durable, greppable, replayable
│  • HTTP + SSE :7717  ─────────────────────────┼─► mission control:  /gang watch  (in Pi)
└───────────────────────────────────────────────┘                     127.0.0.1:7717 (browser)

Requirements

  • Pi (@earendil-works/pi-coding-agent) 0.85.x, ≥ 0.85.1 — pi --version
  • tmux — members run as visible panes (brew install tmux)
  • Node ≥ 22

Install

Option A — from npm (recommended)

pi install pi-gang
pi list                                           # verify: should list  gang

Option B — from GitHub

Pi packages run local extension code with your user permissions, so review the source before installing. Pi installs the package's runtime dependencies for you on a git install:

pi install https://github.com/Jeecabs/gang.git
pi list                                           # verify: should list  gang

SSH also works if your GitHub account is configured for it: pi install ssh://git@github.com/Jeecabs/gang.git

Option C — local clone (to hack on it)

git clone git@github.com:Jeecabs/gang.git
cd gang
npm install                                       # REQUIRED — the broker + GUI run from node_modules
pi install .                                       # persistent: registers it in your Pi settings
# …or load it for a single session without touching settings:
pi -e ./src/intercom/index.ts -e ./src/gang/index.ts

⚠️ A local install (pi install .) references the folder in place and does not run npm install for you — so run it yourself, and don't move or delete the folder (the broker launches from its node_modules). A git/npm install handles deps automatically.

Unsure about Pi extensions in general? Pi documents itself — just ask it: pi -p "how do I write and install a Pi extension?"

Use

In any Pi session, ask for a member — or call the tool yourself. The session adopts the superintendent identity the first time you spawn (not before), so sessions where you never use gang keep their natural title in pi -r:

/gang spawn list the repo's largest files
/gang spawn @reviewer -t high review the diff carefully

gang spawn returns immediately and opens a tmux pane. The member does the task in its own Pi session and sends the result back to superintendent as an intercom message (it does not return inline). Keep working; handle the result when it arrives.

Watch the gang three ways:

# 1. in Pi — live mission-control overlay (members + status + feed)
/gang watch            # or press  alt+g

# 2. in the browser — the same feed, richer
/gang url              # prints computed loopback URL from GANG_GUI_PORT
open http://127.0.0.1:7717

# 3. durable, greppable log of every cross-agent message
tail -f ~/.pi/agent/intercom/intercom.jsonl

Commands & tools

Surface What
gang tool {action:"spawn", task, role?, thinking?, deadlineSeconds?} · {action:"list"} · {action:"clean", force?, all?} · {action:"stop", role? finished?} · {action:"name", name} — model-callable
/gang show the roster
/gang spawn [@name] [-t <level>] <task> spawn a member by hand; name optional (auto m1, m2, …). Levels: off, minimal, low, medium, high, xhigh
/gang clean · /gang clean --force · /gang clean all clean reaps dead/missing panes and prunes stale roster entries; clean --force also kills members that already reported back; clean all stops this superintendent's members (detached mode kills the gang session; in-tmux mode kills direct member panes only)
/gang stop <member> · /gang stop --all-finished stop one member immediately, or bulk-stop any member already reapable (reported_done, pane_dead, pane_missing)
/gang url show the browser mission-control URL (http://127.0.0.1:<GANG_GUI_PORT>)
/gang name [name] show or set this agent/session's own name
/gang watch · alt+g open mission control (in-Pi overlay)
intercom tool list / list-cwd / send / ask / reply / pending / status / cancel

Env knobs: GANG_GUI_PORT (default 7717), GANG_TMUX_BIN (default Homebrew tmux), GANG_FEED_HOURS (mission-control feed recency window, default 6).

Set deadlineSeconds on a tool-driven spawn when a workflow needs a terminal failure path. If the member does not report in time, gang wakes the superintendent with its latest pane state. It does not kill the member automatically.

Finished members linger on purpose — their panes stay (remain-on-exit on) so you can read the final state. Reap them when you're done with /gang clean (or gang({ action: "clean" })). If a member has already reported back but its pane is still hanging around, use /gang clean --force or /gang stop --all-finished. /gang stop <member> is the first-class escape hatch when you know a specific pane should die. /gang clean all tears down the detached gang session outside tmux; inside tmux it kills this superintendent's direct member panes only.

Agent naming: an unnamed orchestrator session claims superintendent of <current-folder> (e.g. superintendent of private-evals) on its first spawn, not at startup — so plain Pi sessions stay unnamed in the pi -r resume list. Use /gang name <name> before spawning to pick a custom target; spawned members get that exact supervisor name. Member task prompts also tell agents to name themselves with gang({ action: "name", name: "<clear role/name>" }) before spinning up their own teammate.

Gang review skill

The package includes a parallel review skill. It starts visible gang reviewers and returns patch-anchored P0-P3 findings, confidence scores, and a ship verdict. The skill does not run for a general review request. Start it explicitly:

/skill:gang-review worktree
/skill:gang-review against main
/skill:gang-review commit abc123
/skill:gang-review https://github.com/owner/repo/pull/123

The skill supports worktree, base-branch, single-commit, and GitHub PR reviews. It filters lock files, generated output, build output, and binary files.

The skill assigns related files to the same reviewer and runs reviewers in parallel. Each reviewer is read-only and reports through intercom. The superintendent validates and deduplicates all findings before it gives the verdict.

Adversarial template skill

The package includes an explicit best-of-N contest skill. Each gang contestant receives the same task and scoring rubric. The superintendent evaluates every valid submission, promotes one winner, and fires the others from the simulated run.

/skill:adversarial-template 3 design a retry policy for this API client

Contestants return independent answers, plans, or proposed patches. They do not edit the shared worktree. The skill selects a recommended solution but does not apply it.

How it's wired

package.json → pi.extensions points Pi at two TypeScript entry files it loads on startup:

  • src/intercom/ — vendored pi-intercom: broker, client, extension bus, mailbox, and the intercom / contact_supervisor tools. Owned — adds gang lifecycle metadata, a broker message tap (durable log), compact tool results, and an embedded HTTP/SSE mission-control server.
  • src/gang/ — the gang tool (spawn members in tmux, list the roster), the /gang watch overlay, superintendent auto-naming, and the gang-review skill.

gang spawn <task> (optionally @name) launches pi --name <name> … @<taskfile> in a tmux pane with PI_SUBAGENT_* env vars. Pass --thinking <level> to add Pi's --thinking flag for that member. The vendored intercom extension reads the env vars at child startup to register the member on the bus and unlock its contact_supervisor tool — that's the whole contract:

Env var Set to Effect
PI_SUBAGENT_INTERCOM_SESSION_NAME <role> member's intercom identity
PI_SUBAGENT_ORCHESTRATOR_TARGET current supervisor name (superintendent of <folder> or custom /gang name) who it reports to
PI_SUBAGENT_ORCHESTRATOR_SESSION_ID supervisor's intercom session ID reports still arrive after /gang name or /alias renames the supervisor
PI_SUBAGENT_RUN_ID / _CHILD_AGENT / _CHILD_INDEX run metadata unlocks contact_supervisor

(The member's addressable name comes from Pi's --name flag; the supervisor self-names lazily to the computed or custom orchestratorName.)

Roadmap

  • Busy-aware messaging in the vendored intercom. Presence already carries each session's lifecycle status (idle, thinking, tool:<name>, see currentStatus() in src/intercom/index.ts), but only list shows it. Ideas to design and build in src/intercom/:

    • send / ask results say when the target is busy and with what, so a sender knows an ask may wait on a long tool call.
    • A timestamp for when the current status started, so peers can tell "busy" from "stuck".
    • A way to wait until a target goes idle, so senders do not poll list.

    Upstream pi-intercom (up to v0.13.0) does not provide this, so gang owns the design.

Develop

npm test             # vendored intercom suite + gang/tmux/tap/GUI/overlay tests
npm run typecheck    # strict TypeScript check

Credits

Vendors pi-intercom by Nico Bailon (MIT). See NOTICE. Built on Pi by the earendil-works team.