Skip to main content

tmux-backed mission control for parallel AI coding agents

Project description

regatta

Mission control for AI coding agents.

regatta runs many Codex (or Claude) workers in parallel — each on its own task, in its own git worktree, in its own tmux window — and gives you one terminal cockpit to steer them. A foreman agent does the planning, the workers do the coding, and you do the steering.

freebird   5 active  ·  2 blocked  ·  1 stale  ·  3 ready   last 8s                       progress
                                                                ▰▰▰▰▰▰▰▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱▱  47%

activity ─────────────────────────────────────────────────────────────────────────────────────────
▁▁▂▃▆█▇▅▃▂▂▁▁▂▃▄▆▇█▇▅▄▃▂▁▁▂▃▄▅▆
events · last 30m · 412

lanes ─────────────────────────────────────────────────────────────────────────────────────────────
 READY 3       │ ACTIVE 5         │ BLOCKED 2        │ CHANGED 4        │ DONE 11
 ─ csv-import  │ ─ backend-api    │ ─ frontend-up…   │ ─ migration-fix  │ ─ schema-v2
 ─ retry-flow  │ ─ test-harden    │ ─ s3-creds       │ ─ docs-rewrite   │ ─ ci-cache
 ─ docs-bulk   │ ─ rate-limiter   │                  │ ─ rbac-models    │ +9 more
               │ ─ webhook-sign   │                  │ ─ logging-cleanup│
               │ ─ idle-worker  ●STALE              │                  │

This is what a regatta session looks like with five things happening at once.


Why use it

  • Real parallelism. Each task gets its own worktree and its own agent. No more sharing one repo with one conversation. No more rebasing manually.
  • You stay in the loop. The mission control TUI shows what every worker is doing right now — heartbeat, terminal tail, file changes, blockers — without context-switching across tmux windows.
  • Steer from anywhere. Same control surface from the TUI, the CLI (regatta spawn, regatta retask, regatta pause), or Discord (!regatta retask backend-api ...). Pick whichever is handy.
  • Boring under the hood. Plain tmux, plain SQLite, plain Python. No daemons, no servers, no cloud. If you can read a .regatta/runs/<id>/state.db file you can debug anything.

Install

pip install regatta          # once published
# or, from a checkout:
pip install -e .

Requires tmux, git, Python 3.10+, and a working codex CLI on your $PATH. Optional: claude CLI for tasks you want Claude to run.

Run regatta doctor to check your environment in one shot.


30-second quickstart

cd ~/Code                 # or any directory containing repos you want to work on
regatta start                # creates a tmux session called `regatta`, opens mission control

That's it. You're now in a tmux session with:

  • a foreman pane running Codex — talk to it like a colleague who can dispatch work
  • a mission window — the cockpit pictured above
  • compact summary / focus panes that stay glanceable
  • an ops shell for whatever you'd usually do in a terminal
  • one worker window per task once you spawn them

To switch to mission control: Ctrl-b then 2 (or tmux select-window -t regatta:mission).

To add work, just tell the foreman in plain English:

"Three things to do: add CSV bulk import to the backend, harden upload validation, document the new flow. Spawn workers for the first two and leave the docs as ready."

The foreman files the tasks into SQLite, spins up worktrees, opens worker windows, and the mission view updates live.


Mission control

The mission window is the operator surface. It updates live — the activity sparkline ticks, heartbeats decay, lane cards move between columns as workers progress.

Keys

key action
move selection between tasks
f share selection as the run-wide focus
s spawn the selected task
p pause the selected task
e resume the selected task
w jump to the selected worker's tmux window
t toggle between lane board (default) and table view
d toggle the dependency tree overlay
/ focus the filter input
; focus the command input
c clear filter
r force refresh
17 bucket filters: all / ready / active / blocked / paused / todo / done
q quit

Command line (;)

Anything you can do by hotkey, you can do with arguments. Some things only work here.

spawn  backend-api
pause  backend-api  waiting on API contract
resume backend-api  contract is stable now
retask backend-api  defer caching, focus on validation + tests
focus  test-hardening
worker frontend-upload
filter csv
bucket blocked

If you omit the task id, the currently-selected row is the target.


From the CLI

You don't have to be in the TUI to drive a run. Every action has a CLI form:

regatta status                                  # one-frame overview, pretty in any pane
regatta tasks                                   # list tasks
regatta spawn  backend-api                      # start a worker
regatta pause  backend-api  "waiting on contract"
regatta resume backend-api  "contract is stable"
regatta retask backend-api  "tighten scope, add tests"
regatta done   backend-api  "shipped"
regatta inspect backend-api                     # full focused view of one task
regatta events --limit 30                       # raw event stream
regatta where                                   # which run/workspace am I in?
regatta runs                                    # list recent runs
regatta logs   backend-api --follow             # tail a worker's output
regatta note   add "API contract is frozen"     # append to the shared knowledge base
regatta ship   backend-api                      # open a PR from the task's branch
regatta export <run-id>                         # archive a run (state.db + logs) for sharing

For the deterministic low-level surface (adding tasks programmatically, etc.):

regatta ctl add-task \
  --task-id docs \
  --title  "Update docs" \
  --repo-root ~/Code/my-repo \
  --prompt "Document the new flow."

Workspace config

For a workspace you orchestrate repeatedly, drop a regatta.yaml at the workspace root:

regatta init-config --objective "Coordinate active work across my local repos"
workspace_root: .
run_root: .regatta/runs
session_name: regatta
objective: Coordinate active work across my local repos
mission_ui: auto         # auto | textual | rich
pane_refresh: 5          # seconds, compact panes
mission_refresh: 1       # seconds, mission window
max_active_workers: 8    # cap on simultaneously-active workers; 0/negative = unlimited
discord: false

max_active_workers caps how many workers can be active at once. When you are at the cap, regatta spawn <task> refuses with an actionable error; pass --force to bypass it once, or --max-active-workers N to raise the cap and spawn in one command. The status/mission header shows the current count as active N/M.

regatta searches upward for regatta.yaml, regatta.yml, .regatta.yaml, or .regatta.yml. Paths inside the config resolve relative to the config file.


Discord bridge (optional)

Steer a run from your phone. The bridge is a small bot that reads one Discord channel and calls the same control actions the TUI uses.

export DISCORD_BOT_TOKEN='...'                    # required (env only; never argv)
export DISCORD_CHANNEL_ID='123456789012345678'    # required: scope the bot to one channel
export DISCORD_ALLOWED_USER_IDS='111,222'         # required: only these user IDs may issue commands
export DISCORD_COMMAND_PREFIX='!regatta'             # optional

regatta start --discord

See the Security and trust model section for why every one of those should be set, not just DISCORD_BOT_TOKEN.

!regatta status
!regatta tasks [all|ready|active|blocked|paused|todo|done|failed]
!regatta inspect [task_id]
!regatta events  [limit]
!regatta focus  <task_id>
!regatta spawn  <task_id> [--force]
!regatta pause  <task_id> [message]
!regatta resume <task_id> [message] [--force]
!regatta retask <task_id> <message>

Enable the bot's Message Content Intent in the Discord developer portal first.


Task plans (optional)

Preload a task board from YAML — useful when you know up-front what work needs to ship:

regatta start \
  --plan plans/example_plan.yaml \
  --objective "Ship CSV bulk import safely with parallel workers"

See plans/ for examples.


What's where

The package lives under src/regatta/:

module role
regatta.regatta_cli the regatta CLI front door
regatta.orchestrator session bootstrap (tmux windows, panes, foreman)
regatta.tmux tmux helpers (windows, panes, send-keys)
regatta.git_ops git / worktree helpers
regatta.plans YAML plan loader
regatta.actions spawn / retask / pause / resume / focus business logic
regatta.cli deterministic CLI surface (used by regatta ctl …)
regatta.regattactl aggregated re-exports of the split control modules
regatta.state SQLite schema, migrations, and queries
regatta.doctor environment health checks for regatta doctor
regatta.mission_textual the Textual mission control app
regatta.mission_widgets native Textual widgets (lane board, heartbeat, etc.)
regatta.mission_cache TTL cache for git / tmux IO
regatta.ui_render Rich rendering — shared by mission view and dashboard panes
regatta.ui_snapshot builds the unified snapshot every UI consumes
regatta.dashboard one-frame and --watch Rich views, plus mission full-screen mode
regatta.control_room thin deprecation shim — folded into dashboard
regatta.discord_bridge Discord bot
regatta.hooks user-defined lifecycle event hooks (task.spawned / done / failed / worker.stale)
regatta.plans/ (package data) starter plans for preloading a board
regatta.prompts/ (package data) foreman/system prompts

State lives under .regatta/runs/<run-id>/. Tasks, focus, events, and worker metadata are all in state.db. Worker code lives in per-task worktrees alongside.


Design notes

  • The foreman is for planning, the mission view is for piloting. The foreman pane is where you discuss what work to do and why. The mission view is the fast lane for spawning / pausing / retasking once the work exists.
  • Selection is local; focus is shared. Browsing tasks in the mission view doesn't disturb the rest of the control room. Press f (or Enter) when you want to commit a selection as the run-wide focus.
  • The lane board is the primary view. It shows movement — who's moving, who's stuck, who's queued — at a glance. The table is still there (t) for when you want sortable density.
  • Visualisations are live. The activity sparkline shows events-per-minute over the last 30 minutes. Heartbeat decay bars per active worker turn magenta when stale (no heartbeat in 10 min). Dependency overlay (d) shows what's blocking what.
  • No daemon. SQLite is the source of truth. The TUI, the CLI, and the Discord bridge all read and write the same store.

Requirements

  • Python 3.10+
  • tmux
  • git
  • codex CLI on $PATH (and optionally claude)
  • Python deps: textual, rich, PyYAML, discord.py (auto-installed by pip install -e .)

Security and trust model

regatta is a developer tool, not a sandbox. Before pointing it at anything sensitive, read this section.

Privacy

Every task prompt is sent to whichever model your provider CLI is configured for (codex, claude, …). If you are running on a private repository, assume the contents of your task prompt and any code the worker reads have left your machine. If your provider relationship doesn't permit that, do not run regatta against that repo.

.regatta/runs/<run-id>/ contains task prompts, worker transcripts, logs, and any output the agents produced. It is gitignored — do not commit it. The directory may contain secrets the agents surfaced; treat it the way you would treat a shell history file.

Discord bridge

The Discord bridge is a remote-control surface. Anyone able to post in the configured channel can spawn or retask workers, which means they can run arbitrary AI agents inside your worktrees. Configure it defensively:

  • DISCORD_ALLOWED_USER_IDS (comma-separated numeric user IDs) is required: the bridge fails closed and refuses to start unless at least one trusted operator id is set, so an empty allowlist can never leave commands open to everyone.
  • Always set DISCORD_CHANNEL_ID so the bot also ignores messages from other channels.
  • DISCORD_BOT_TOKEN is read from the environment only. It is never accepted on the command line; that would leak it via ps.

Plans

PlanLoader trusts the YAML author. A plan file prescribes prompts, repos, and metadata that drive what workers do. Only load plans you wrote or fully reviewed. Loading an untrusted plan is the rough equivalent of running someone else's shell script — the prompts may be hostile, and the metadata can route writes into repos you didn't expect.

.regatta/runs/ is gitignored on purpose

Run directories accumulate task prompts, model output, log files, and SQLite databases. They are large, low-signal, and may contain secrets that agents accidentally echoed. The default .gitignore excludes them; please keep it that way.


License

MIT. See also SECURITY.md for reporting vulnerabilities and CONTRIBUTING.md for how to help.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

regatta-0.1.0.tar.gz (126.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

regatta-0.1.0-py3-none-any.whl (108.8 kB view details)

Uploaded Python 3

File details

Details for the file regatta-0.1.0.tar.gz.

File metadata

  • Download URL: regatta-0.1.0.tar.gz
  • Upload date:
  • Size: 126.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for regatta-0.1.0.tar.gz
Algorithm Hash digest
SHA256 f24031b131838532577458b68a6d462230c27430616cd4acb0413e607ffa920c
MD5 b862180978aefb80fda322b6be645275
BLAKE2b-256 4314807b3b8ad5dee8bdb269088f29ff0bff9d60df7b44d3f939c32f1694efc5

See more details on using hashes here.

Provenance

The following attestation bundles were made for regatta-0.1.0.tar.gz:

Publisher: release.yml on Someblueman/regatta

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file regatta-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: regatta-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 108.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for regatta-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ff045a5c5d586afdfb26b27110de440d2a86077265a7676eb9653f4577a5d342
MD5 c665fdb45f2e835673d269edac0a23a5
BLAKE2b-256 49728b2069bbdc6828883eeaf38282b7d6a140e1c3388c0afda24fac6e102122

See more details on using hashes here.

Provenance

The following attestation bundles were made for regatta-0.1.0-py3-none-any.whl:

Publisher: release.yml on Someblueman/regatta

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page