Skip to main content

Multi-LLM planning & orchestration that routes work across the terminal AI CLIs you already have: plan -> phases -> tasks, model choosable per role and per task.

Project description

Roundtable

CI License: MIT Python 3.11+

Build reliable, resumable AI coding workflows without blowing your context window or getting pinned to one vendor's usage limits.

Roundtable is a small, from-scratch multi-LLM planning and orchestration tool that drives other LLMs through their terminal CLIs (Claude Code, Codex, Gemini CLI, aider, llm, Ollama, …) — routing each part of the work to the model you choose, so no single vendor's limit stalls the run.

One LLM plans the work and breaks it into phases → tasks. You approve the plan. Then a Main Orchestrator drives execution: for each phase it runs a fresh Phase Orchestrator that defines and dispatches Task Agents, collects their work, summarizes the phase, and hands the summary back to Main — which keeps the project docs up to date. Every role's model/agent is choosable, per role and per task. It runs inside your existing project: agents execute with the repo as their working directory, and all roundtable artifacts stay under .roundtable/.

graph TD
  P[Planner agent] -->|plan.json| GATE{Approve?}
  GATE -->|yes| MO[Main Orchestrator]
  MO -->|OVERVIEW.md| DOCS[(.roundtable/docs)]
  MO -->|per phase, fresh context| PO[Phase Orchestrator]
  PO -->|TASK.md| A1[Task Agent A]
  PO -->|TASK.md| A2[Task Agent B]
  A1 -->|result.md| PO
  A2 -->|result.md| PO
  PO -->|phase-summary.md only| MO
  MO -->|PROGRESS.md / FINAL.md| DOCS
  PO -. context discarded .-> X(( ))

Key features

  • Multi-LLM orchestration: route planner, main, phase, and task roles to different models/providers.
  • Human approval gate: inspect the generated plan before any agent changes files.
  • Context isolation: phase/task contexts are discarded after each phase; Main only receives structured summaries.
  • Resumable runs: completed work is reused, and failed/skipped parts can be retried.
  • Validation gates: per-task and per-phase commands decide whether work actually passed.
  • Live local dashboard: watch progress, approve tasks, stop runs, and inspect usage without a cloud service.
  • Full MCP Server Support: expose init, map, plan, approve, run, stop, status, and usage to Claude Desktop, Claude Code, and other MCP clients.

Install

Prerequisites

  • Python 3.11+
  • macOS or Linux
  • Windows via WSL only: native Windows is not supported because Roundtable uses POSIX process control (SIGTERM, process groups) and Unix pseudo-terminals for pty: true agents.
  • At least one supported AI CLI on PATH for real runs, such as Claude Code, Codex, Gemini CLI, aider, llm, Ollama, pi, or omp.
  • uv or pipx for global installation.

Install it as a global tool so roundtable works in any project.

From GitHub (recommended):

uv tool install git+https://github.com/arfuhad/roundtable.git
# or with pipx:
pipx install git+https://github.com/arfuhad/roundtable.git

From source (to hack on Roundtable itself):

git clone https://github.com/arfuhad/roundtable.git && cd roundtable
uv tool install --editable .        # or: pipx install --editable .

The base install pulls only pydantic + pyyaml — the default cli backend shells out to LLM CLIs you already have, so no API SDKs are added. Optional extras append the same @ git+… reference to the package name:

# direct API calls via LiteLLM:
uv tool install "roundtable-cli[litellm] @ git+https://github.com/arfuhad/roundtable.git"
# expose Roundtable to MCP clients:
uv tool install "roundtable-cli[mcp] @ git+https://github.com/arfuhad/roundtable.git"

Not published to PyPI yet, so install from the repo as above.

Quick start (in an existing project)

cd my-existing-project
roundtable init                       # writes config + lists installed CLIs & their models
$EDITOR roundtable.config.yaml        # pick which CLI agent + model runs each role

roundtable plan --goal "Add retry with backoff to the HTTP client"
#   -> .roundtable/plan/plan.json + PLAN.md   (status: needs approval)

roundtable approve                    # the human gate
roundtable run                        # autonomous: agents work phase by phase
roundtable status                     # progress

run refuses until approve. Re-running resumes where it stopped.

Proof of life

Roundtable includes both a web dashboard and a terminal watch view, so a run is not a black box.

Web dashboard Terminal watch
Roundtable web dashboard showing phase/task progress, active agents, and event timeline Roundtable terminal watch view showing progress, active tasks, usage, and recent events

Recommended backend: pi

Roundtable works best with a pi-family coding agent — either upstream pi or the batteries-included fork oh-my-pi (omp) (LSP/DAP/subagents). Both speak ~40 model providers and share the same CLI contract. With provider: pi, the tool handles all LLM connectivity, auth and model routing — roundtable just orchestrates.

If pi (or omp) is on your PATH, roundtable init scaffolds this backend for you:

npm install -g @earendil-works/pi-coding-agent    # install pi   (or: @oh-my-pi/pi-coding-agent for omp)
pi-ai login anthropic                             # connect an LLM (or export ANTHROPIC_API_KEY / OPENAI_API_KEY / …)
cd my-existing-project
roundtable init                                   # detects pi/omp -> writes the pi backend
roundtable plan --goal "Add retry with backoff to the HTTP client"
roundtable approve && roundtable run

See Backend configuration for the full pi/omp config, flavor differences, and direct CLI/LiteLLM alternatives.

Don't have pi or omp? You have two options — roundtable prints both at init:

  1. Install pi (or omp) and connect it to your LLMs (recommended).
  2. Use what you already have: provider: cli drives the terminal CLIs you've installed (claude, codex, gemini, …), or provider: litellm for direct API calls. These backends are unchanged; see below.

Note: pi has no per-action permission gate and task agents share the project directory, so keep max_concurrency: 1 (the default) unless you isolate tasks.

Planning inputs

roundtable plan --goal "..."          # from a one-line goal
roundtable plan --prd docs/PRD.md     # from a PRD / requirements file
roundtable plan --plan old-plan.json  # ingest an existing plan (JSON in our schema -> loaded as-is)
roundtable plan --plan ROADMAP.md     # free-form plan/PRD -> structured into our schema by the planner

Mapping an existing project

Don't have a PRD? Point roundtable map at an unfamiliar (brownfield) codebase and it scans the project into two docs under .roundtable/docs/: an ARCHITECTURE.md outline (purpose, stack, module map, data flow, how to run) and a reverse-engineered PRD.md. You confirm the PRD by reviewing/editing it, then feed it straight into planning:

roundtable map                                  # scan -> .roundtable/docs/ARCHITECTURE.md + PRD.md
$EDITOR .roundtable/docs/PRD.md                 # the human confirmation: fix what's wrong
roundtable plan --prd .roundtable/docs/PRD.md      # the confirmed PRD drives the plan
roundtable approve && roundtable run

map builds a compact, provider-agnostic digest of the codebase (pruned file tree + key file contents), so it works on every backend; with provider: cli the analyst agent also reads the real files directly. Flags: --target DIR to scan a different directory than the project root, --model agent:model to override the analyst (default is the main role), and --max-files / --max-bytes to size the digest.

Watching a run live

roundtable run is live by default: it prints a web dashboard link up front and renders the terminal watch view inline as it works — no second terminal needed.

roundtable run                  # runs + shows progress; dashboard link printed at the top
roundtable run --approve        # auto-approve the plan before running
roundtable run --no-watch       # run without the inline terminal view
roundtable run --no-dashboard   # run without serving the web dashboard
roundtable run --port 9000      # pin the run's dashboard port (default: a free port)

A run streams structured events to .roundtable/runs/run.log, and plan.json is the live source of truth — so a viewer just polls the files and stays decoupled from the engine. You can also open the standalone local surfaces, both zero-dependency (stdlib only):

roundtable dashboard            # web UI at http://127.0.0.1:8787 (--open to launch a browser)
roundtable dashboard --port 9000   # pin the local dashboard port
roundtable watch                # live dashboard right in the terminal
roundtable stop                 # SIGTERM the in-progress run/process group

These show overall progress, what each agent is doing right now (task, agent:model, elapsed), per-phase/task status, per-agent task counts + time, task durations (avg / slowest), live token usage (estimated on the CLI backend), and a rolling event timeline. The web dashboard is also interactive: approve the plan, start/stop a run, and approve waiting HITL tasks right from the page. No build step, no JS framework, no API keys.

REST control API (roundtable serve)

The dashboard server doubles as a local JSON control API. roundtable serve starts it headlessly and prints a machine-readable JSON line first ({"event": "serving", "url": ...}) so tools can parse the picked port (--port 0 = a free one):

roundtable serve --project . --port 0
endpoint what it does
GET /api/state live run state snapshot (same data as watch)
GET /api/project project root, plan/config presence, run pid, waiting tasks
GET /api/plan · PUT /api/plan read / save the plan (validated; editing resets approval)
POST /api/plan/generate · GET spawn a detached roundtable plan (goal/prd/plan_file) · poll it
POST /api/approve validate runners against the config, then approve
POST /api/run · POST /api/stop spawn a detached run (guarded by run.pid) · stop its process group
POST /api/resume approve a waiting HITL task {"task": "p1-t2"}
POST /api/init scaffold .roundtable/ + default config
GET /api/config · PUT /api/config read / save roundtable.config.yaml (schema-validated)
GET /api/agents probe installed CLIs + their models (?timeout=s)
GET /api/usage provider usage snapshots (calls, tokens, duration)

The dashboard/API binds only to loopback hosts (127.0.0.1, localhost, or ::1). State-changing browser requests are limited to localhost/Tauri origins, and plan/config mutations are rejected while a run is live.

With provider: cli a CLI returns only stdout, so token counts are estimated from text length (~4 chars/token) and flagged with "estimated": true in the usage snapshot; provider: litellm reports exact counts from the API.

How "terminal access to other LLMs" works

With provider: cli, each role is a {agent, model} pair: agent names an entry in the agents map (a terminal command run in your project directory), and model is substituted into that command's {model} token. So one agent command serves many models, and you can mix CLIs and models freely per role — e.g. the Main Orchestrator on Claude/Opus, Phase Orchestrators on Antigravity/Gemini, Task Agents on OpenCode/Mimo. stdout is the response.

provider: cli

models:
  planner: { agent: claude,      model: opus-4.8 }
  main:    { agent: claude,      model: opus-4.8 }
  phase:   { agent: antigravity, model: gemini-3.5-flash }
  task:    { agent: opencode,    model: mimo-v2.5-pro }   # different CLI + model per role/task

agents:
  claude:
    command: ["claude", "-p", "{prompt}", "--model", "{model}"]
  codex:
    command: ["codex", "exec", "--model", "{model}", "{prompt}"]
  antigravity:                                          # Antigravity CLI, binary `agy`
    command: ["agy", "-p", "{prompt}", "--model", "{model}"]
  opencode:
    command: ["opencode", "run", "--model", "{model}", "{prompt}"]
  ollama:
    command: ["ollama", "run", "{model}"]
    stdin: true        # pipe the prompt on stdin instead of via {prompt}
  • command is an argv list (no shell, so no injection). Tokens may contain {prompt}, {system} and {model}. If {system} is absent, the system text is prepended to the prompt. With stdin: true, the prompt is piped on stdin.

  • pty: true allocates a real pseudo-terminal for the subprocess — use this when a CLI detects whether it's connected to a TTY and refuses to run (or switches to a degraded mode) without one.

  • The flags above are illustrative — check each tool's own docs for the exact model-selection and non-interactive flags.

  • A role value also accepts the shorthand string agent:model (e.g. opencode:mimo-v2.5-pro) or a bare agent (no model). CLI-specific extras like a reasoning level go in the model token or as a flag in the agent command.

  • Per-phase / per-task runner objects live in plan.json and override the role defaults — edit them before approve to assign a different agent/model to a specific task:

    "runner": { "agent": "opencode", "model": "mimo-v2.5-pro" }
    

Set the top-level project_context key (free text) to inject stack, conventions, and working-directory notes into every task/phase prompt. Pass -v/--verbose to any command for debug logging.

Picking models per role (roundtable models)

Instead of hand-editing models:, run roundtable models to choose a model for each role from the ones your backend is actually connected to — a two-step provider → model prompt in the terminal that writes your picks back into the config (leaving the rest of the file untouched). roundtable init offers the same picker interactively when run in a TTY.

roundtable models              # interactive: pick planner / main / phase / task
roundtable models --list       # just print the connected models, grouped by provider
roundtable models --json       # machine-readable
roundtable models --verify     # after picking, send one tiny call to each model to confirm it works

It works for both backends: on provider: pi the list comes from the tool itself (omp models --json, or pi --list-models) and "provider" is the model's provider (anthropic, opencode-go, …); on provider: cli it comes from each agent's models_command and "provider" is the agent (claude, opencode, …). Because the list is the tool's real catalog, you can't fat-finger an unsupported id — and --verify catches models that are listed but error at call time.

Seeing what you can assign

roundtable init (and roundtable agents) probes the configured CLIs and shows which are installed and which models they offer, so you know what to put in models::

$ roundtable agents
  [x] antigravity  (agy) — 8 model(s):
          Gemini 3.5 Flash (High)
          ...
  [x] claude  (claude) — models: n/a (no models_command)
  [x] opencode  (opencode) — 358 model(s):
          opencode-go/mimo-v2.5-pro
          ...
  [ ] cursor-agent  (cursor-agent) — not on PATH

An agent reports its models when its spec has an optional models_command:

agents:
  opencode:
    command:        ["opencode", "run", "--model", "{model}", "{prompt}"]
    models_command: ["opencode", "models"]

These commands often hit the network/auth and can be slow, so each runs with a bounded timeout (--models-timeout on init, --timeout on agents) and any failure degrades to a note — it never blocks. Tools without an enumeration command (claude, codex) just show n/a; pass the model to --model yourself. roundtable init --no-models skips probing; roundtable agents --json is machine-readable.

Letting agents edit files

Task Agents run with your project as their working directory, so they can read and modify real files — if their CLI is allowed to. Read-only/"print" modes only return text. Configure the command with the flags your tool needs to apply edits, e.g.:

agents:
  claude:
    command: ["claude", "-p", "{prompt}", "--model", "{model}", "--permission-mode", "acceptEdits"]
  codex:
    command: ["codex", "exec", "--full-auto", "--model", "{model}", "{prompt}"]

(Check each tool's own docs for the exact non-interactive / auto-edit flags.)

Per-task and per-phase controls in plan.json

Two optional fields can be added to any task object in plan.json before running roundtable approve:

{
  "id": "p1-t2",
  "title": "Deploy to staging",
  "runner": { "agent": "claude", "model": "opus-4.8" },
  "validate_command": ["pytest", "tests/smoke/", "-q"],
  "requires_approval": true
}
  • validate_command — an argv list that runs in the project root after the task agent finishes. A non-zero exit marks the task as failed (feeding into the retry loop and re-planning); exit 0 means success.
  • requires_approval — when true, the run pauses before executing this task and waits for a human to type:
    roundtable resume --task p1-t2
    
    The engine prints the exact command. Other concurrent tasks in the same wave are unaffected (the semaphore slot is not held while waiting).

A phase object accepts validate_command too — the phase completion gate. It runs in the project root after all of the phase's tasks succeed (e.g. the phase's test suite); a non-zero exit marks the phase failed even though its tasks are done. On re-run the completed tasks are reused and only the gate re-runs, so fixing the project and re-running heals the phase. Validation commands time out after defaults.validate_timeout (120s default).

Other backends

provider: pi           # recommended: drive the pi coding agent (see "Recommended backend: pi")
provider: cli          # reach other LLMs through their terminal CLIs (default without pi)
provider: litellm      # direct API calls, needs API keys
provider: scripted     # deterministic offline backend (demo/tests, no network)

With provider: litellm there is no terminal command, so a role's model is the litellm model string (openai/gpt-4o, anthropic/claude-3-5-sonnet-latest, ollama/llama3, …) and agent is ignored — e.g. main: { model: openai/gpt-4o } (a bare string openai/gpt-4o works too). litellm needs the litellm extra — see Install — and the usual provider API keys in your environment (OPENAI_API_KEY, ANTHROPIC_API_KEY, …).

Driving it from an MCP client

Expose the whole workflow to an MCP client (Claude Code, Claude Desktop) so an agent can plan and run the roundtable as tool calls:

roundtable mcp        # stdio MCP server  (needs the mcp extra — see Install)

Tools: roundtable_init, roundtable_map, roundtable_plan, roundtable_approve, roundtable_run (non-blocking; guarded against duplicate launches via a run.pid), roundtable_stop, roundtable_status, and roundtable_usage (token/cost tally); plus read-only roundtable://plan, roundtable://state, and roundtable://logs resources. Register it in Claude Code's .claude/settings.json:

{ "mcpServers": { "roundtable": { "command": "roundtable-mcp" } } }

Project layout produced by a run

my-project/
  roundtable.config.yaml              # provider + choosable {agent, model} per role
  .roundtable/
    plan/{BRIEF.md, PLAN.md, plan.json}     # plan.json = source-of-truth manifest
    phases/
      phase-01-<slug>/
        PHASE.md
        phase-summary.md           # Phase Orchestrator -> Main report
        tasks/task-01-<slug>/
          TASK.md                  # the task's work definition
          result.md                # the agent's completed work
          output/                  # artifacts the agent produced
    docs/{OVERVIEW.md, PROGRESS.md, FINAL.md}   # maintained by the Main Orchestrator
    runs/run.log                   # append-only structured JSONL events (feeds the dashboard)

Your own source files are never touched by the roundtable itself — only by the agents you point at them.

Design notes

  • Deterministic control flow, LLM cognition. The engine (which agent runs when, dependency scheduling, file layout, status) is plain code. Each role uses its agent only for its own thinking.
  • Context cleaning is a hard invariant. A Phase Orchestrator and its Task Agents are created per phase and dropped afterward; the Main Orchestrator only ever receives the phase summary, never task transcripts — so its context stays small across long runs. Enforced by the engine, covered by tests.
  • Dependency-aware execution. depends_on forms a DAG; tasks run in dependency-ordered concurrent waves (max_concurrency) and dependents receive upstream results as context. A dep may reference a task in the same phase or any earlier phase; forward references, cycles, and unknown/duplicate ids are rejected at plan time. (Phases still run in order — cross-phase deps flow results, not cross-phase parallelism.)
  • Failure isolation. A task that exhausts its retries — or fails its validate_command — is marked failed; every dependent (same or a later phase) is skipped; its phase and the overall run end failed, and no FINAL.md is written. A phase whose own validate_command fails is marked failed even when all its tasks completed. Provider exit codes are the failure signal, not output heuristics.
  • Resumable. Completed phases/tasks are skipped and their on-disk results reused as dependency context; re-running retries failed/skipped work.
  • Dynamic re-planning. After each dependency wave, if any task failed and tasks remain in the phase, the Phase Orchestrator is asked to adapt the remaining tasks' descriptions before they run — without touching the overall plan structure.

Architecture (modules)

module responsibility
roundtable/models.py AgentRef + Plan/Phase/Task models + graph validation (intra- & cross-phase deps)
roundtable/errors.py RoundtableError (user-facing) + TaskFailed (per-task failure signal)
roundtable/config.py roundtable.config.yaml: provider, role {agent, model} runners, agents map, project_context
roundtable/store.py .roundtable/ layout, manifest IO, structured event log, all writers
roundtable/llm.py LLMProvider protocol; PiProvider / CLIProvider / LiteLLMProvider / ScriptedProvider; JSON extraction; RunStats (tokens + cost)
roundtable/discovery.py detect installed CLIs + list their models (init / agents)
roundtable/modelpick.py list connected models + interactive per-role picker (models / init)
roundtable/insights.py build_state analytics over plan.json + events; terminal rendering
roundtable/dashboard.py zero-dep web dashboard + REST control API (stdlib http.server)
roundtable/runctl.py run.pid protocol: detached run/plan launches, stop, HITL approve
roundtable/scan.py stdlib codebase digest (pruned tree + key files) for map
roundtable/prompts.py per-role system prompts
roundtable/agents.py Planner, Analyst, MainOrchestrator, PhaseOrchestrator, TaskAgent
roundtable/engine.py dependency scheduler + run loop + context-clean boundary + failure/HITL handling
roundtable/cli.py init / agents / models / map / plan / approve / run / resume / stop / status / dashboard / serve / watch / mcp
roundtable/mcp.py MCP server (roundtable mcp / roundtable-mcp) exposing the workflow as tools + resources

Tests

pip install -e ".[dev]"
pytest -q

Covers model validation (incl. cross-phase deps, forward-ref/duplicate-id rejection), the .roundtable/ store, JSON extraction (objects, arrays, double-fenced blocks), the CLIProvider against real subprocesses (streaming output, cwd, missing-command and nonzero-exit handling), existing-plan ingestion + non-pollution layout, and full offline engine runs asserting the orchestration contract — dependency flow, wave ordering, cross-phase result flow, the context-clean invariant, the approval gate (HITL waitingresume), failure propagation (failed task → skipped dependents → failed run), the phase completion gate (phase validate_command failing/healing across re-runs), and resumability.

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

roundtable_cli-0.4.0.tar.gz (248.5 kB view details)

Uploaded Source

Built Distribution

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

roundtable_cli-0.4.0-py3-none-any.whl (83.3 kB view details)

Uploaded Python 3

File details

Details for the file roundtable_cli-0.4.0.tar.gz.

File metadata

  • Download URL: roundtable_cli-0.4.0.tar.gz
  • Upload date:
  • Size: 248.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for roundtable_cli-0.4.0.tar.gz
Algorithm Hash digest
SHA256 4d62c8cae5b3b538dae27dd09e3ea69beb514ec12e4d6d4a09ef9150538aa6ba
MD5 519f8db6dd256b566013992784d89d92
BLAKE2b-256 9af44daeca9573f6a4e76b71156ff963610cf5b098866240dd50efc844ac7325

See more details on using hashes here.

File details

Details for the file roundtable_cli-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: roundtable_cli-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 83.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for roundtable_cli-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8b8497273f8beef3ca30c8bbe07e803ac8dc6d84f27e00d14dde88343a561de7
MD5 55731fda0a349d4038e0bda1fd8eb792
BLAKE2b-256 2dd20ab9256509df0cb8f6fcd1b8ed34cce94e49dcf0984b52baf0100dea4c89

See more details on using hashes here.

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