Docs
Everything Baya does, and how
From install to a recovered run. This site is the place for the full picture; the wiki stays the technical source of truth for contributors.
Overview
Baya is a zero-config command-line orchestrator for the AI coding agents you already have installed and authenticated. Write the actions in plain text and run one command: Baya turns them into a dependency graph, routes each task to the provider and model that fit it, runs independent work in parallel, and carries the run through to a report.
There is no config format, no DSL, and no separate API key. It works with codex, claude, copilot, and opencode — use one default model, or name a different model or provider for a specific task.
--on-error stop, a parallel-aware status line, and the recovery prompt.Install & first run
Requires Node 24+ and at least one supported CLI on your machine.
npm install -g baya-cliThen check what Baya can see. baya doctor resolves every provider — path, version, and capabilities — and is worth running first on any new machine, since provider binaries are frequently off $PATH.
baya doctorOn the first real run, Baya asks once which provider and model to default to, stores the answer in ~/.config/baya/config.json, and never asks again. Change it later with baya config.
Run baya upgrade any time to update every installed provider CLI to its latest version; baya upgrade <provider> narrows to one.
baya upgradeQuick start
Point Baya at any plain-text list of tasks. It plans, shows you the graph, and waits for your approval before running anything.
baya ./tasks.mdTo see the plan without running it, use baya plan or --dry-run. To run unattended, review a captured manifest first, then execute it:
baya plan tasks.md --plan-out plan.jsonbaya run tasks.md --plan-in plan.json --yesWriting task lists
Any UTF-8 text file works. Baya never parses these structurally — the planner reads every format for intent, so depends_on: and plain prose like “once the schema and UI are done” get you the same graph.
Markdown
A heading for the goal, one bullet per task — each bullet as long as the task needs. Wrapped continuation lines are indented under the bullet.
# Ship the orders endpoint
- Design the REST API for orders — list, get, create, and cancel.
Define pagination, the error response shapes, and an idempotency
key on create. Write it up as an OpenAPI document. Use Sonnet.
- Generate the Postgres schema and migrations from that design.
- Build the React table that consumes the list endpoint: sortable
columns, a status filter, and empty and loading states. Run with codex.
- Once the schema and UI are done, write integration tests that
exercise every endpoint against a throwaway database.A bare TODO.txt
One task per line, numbered or not. The line itself can be as detailed as you like.
1 Design the REST API for orders (list, get, create, cancel) with pagination, error shapes, and an idempotency key on create. Use Sonnet.
2 Generate the Postgres schema and migrations from that design.
3 Build the sortable, filterable React table that consumes the list endpoint. Run with codex.
4 Once the schema and UI are done, write integration tests for every endpoint.YAML
The same intent, with explicit depends_on if you think better that way. Use a | block scalar for a multi-line task:.
- id: design-api
task: |
Design the REST API for orders — list, get, create, cancel.
Define pagination, error response shapes, and an idempotency
key on create. Write it up as an OpenAPI document. Use Sonnet.
- id: gen-schema
task: Generate the Postgres schema and migrations from that design.
depends_on: [design-api]
- id: build-ui
task: Build the React table that consumes the list endpoint. Run with codex.
depends_on: [gen-schema]
- id: tests
task: Write integration tests for every endpoint.
depends_on: [gen-schema, build-ui]If the planner can’t produce a graph, a deterministic splitter falls back to a linear chain in the order you wrote the tasks.
Model routing
Name a model in the task text — Use Sonnet., run this with codex, Use luna. — and Baya resolves that name against the catalog to a real provider and model id at the model gate. Tasks with no stated model use your configured default; an unset model means the provider’s own default.
Model ids churn faster than the tool ships, so nothing is hard-coded. When a provider’s catalog is missing a model or the installed CLI rejects a built-in slug, add an override to ~/.config/baya/config.json:
{
"modelAliases": {
"cheap": "gpt-5.6-luna"
},
"modelCatalog": {
"copilot": [
{
"id": "claude-sonnet-4.5",
"aliases": ["sonnet45"],
"description": "Anthropic Claude Sonnet 4.5"
}
]
}
}modelAliases maps a nickname to a real id; modelCatalog adds or replaces a catalog entry, keyed by provider and model id. Set an alias from the CLI with baya config set modelAliases.cheap gpt-5.6-luna, and inspect the effective catalog with baya models.
Run
baya run takes a plain-text list of coding tasks and carries it through to a report. A model turns the list into a dependency graph; a scheduler walks the graph, routing each task to the provider and model that fit it and running independent work in parallel; each result feeds the tasks that depend on it.
baya <file> is the default form — the same as baya run <file>. baya plan <file> stops at the preview, and --plan-in executes a manifest you captured earlier.
baya ./tasks.mdbaya run tasks.md --plan-in plan.json --yes- The planner builds the graph. A model turns your list into a DAG of tasks and dependencies; if it cannot produce a valid one, a deterministic splitter falls back to a linear chain in the order you wrote them.
- Models are named in the task text —
Use Sonnet.,run with codex. Each name resolves at the model gate to a real id and the provider that serves it; an unstated model uses your configured default. - A preview gate comes first (
--dry-runorbaya planstops there). It shows every task, its resolved model, what waits for what, and what shares a process, before anything runs. - One process, many tasks. Tasks that share a provider, model, permission level, and directory are worked through in order in a single agent process, so the repo is read once. Decided separately from the graph — a six-step chain can still be one process.
--group-sizedefaults to 3. - Parallel, but write-safe. Independent
read-onlytasks run concurrently (--max-parallel, plus a per-provider cap); everyread-writetask takes the single writer lock and runs alone. - Nothing paid-for is redone. A task already ticked off (
[x],✅) is read for context, never re-run; what earlier tasks discovered is handed forward to the tasks that could not share their process (--no-memoryto disable). - Checkpointed. State is written before every transition. Run out of credits mid-graph and
baya resume <runId>picks up the unfinished work, optionally on a different provider;baya runslists what is resumable.
Everything lands in .baya/runs/<runId>/ — the manifest, each task’s request and result, the providers’ event streams — and state.json is written before every transition, so a crash or Ctrl+C is a pause, not a restart. The run ends with a report: task outcomes, flagged notes, token spend, and the exact command to resume what is left.
Scheduling, grouping, memory, locks, and interrupts in full: wiki-llm/execution.md. The stage-by-stage flow is the diagram further down.
AI Consensus
baya consensus puts one artifact — a spec, a diff, or a plain question — in front of several models at once and reports where they land. Each provider CLI reviews it independently; a moderator reconciles their findings into a new draft; the loop repeats until nothing blocking is left or the round ceiling is hit.
It is a separate command, not a mode of baya run: no dependency graph, no task list, no lock, and baya runs and baya resume never see a consensus run.
baya consensus ./spec.md --providers luna,sonnet,opencode/mimo-v2.5-freebaya consensus "should we split this module?" --providers luna,sonnet --kind idea --rounds 3- Reviewers are named by model —
--providers luna,sonnet. A bare provider id means that CLI’s own default model; an unstated reviewer uses your configured default, and the moderator defaults to your planner model. - The moderator never picks the answer. It writes the criteria, reconciles each round, and reports whether the reviewers agree — nothing it believes about the subject reaches the output. A plain question gets no criteria at all: agreement prints the first reviewer’s answer, disagreement prints every answer side by side.
- Reviewers work blind. No reviewer sees another’s findings in the same round; rivals reach it under stable pseudonyms, so a finding stands on its evidence, not a brand name.
--roundsis a ceiling, not a count. The debate stops early when no reviewer raised ablockerormajorfinding, or when a round breaks no new ground the last one already fixed.- Access is the moderator’s call, not a flag. For a pure question the reviewers get no tools; for anything touching the tree they get the full tool set in your working directory and can run the suite to ground a finding.
- A confirm gate comes first (skip with
--yes). When the reviewers have write access it warns that agents run unsupervised, several at once, with nothing isolating them — commit or stash before you start. Consensus is for reviewing, not developing.
Everything lands in .baya/consensus/<runId>/ as each round settles — every critique, each reviewer’s append-only ledger, and the reconciled drafts — so Ctrl+C on round 3 leaves rounds 1–2 intact. The report adds per-provider token spend, agreement counts, and the disagreements that never resolved.
Every flag — --providers, --moderator, --rounds, --kind, --ledger-budget, --output, --no-diff — is in the CLI reference. Round loop, ledgers, compaction, and the access postures in full: wiki-llm/consensus.md. The round loop is the diagram further down.
Design principles
Five ideas do most of the work. Full detail lives in the architecture and protocol wiki pages.
- JSON on the wire, both directions
- Every exchange with a provider is a validated envelope, never prose. codex and claude enforce the result schema natively. A question from an agent is a status: "needs_input" field — not a question mark spotted in a stream.
- The planner picks a provider, never a command
- Manifests carry a provider name from a closed enum; adapters alone build argv. shell: true is banned repo-wide and lint-enforced.
- A process is the unit, not a task
- Tasks that share a provider, model, permission level, and directory go into one agent process and are worked through in order. Grouping is decided separately from the DAG shape, so a six-stage chain can still be one process.
- Nothing paid-for is ever redone
- Progress is checkpointed before each transition. Within a run, commands that worked, commands that failed, and files already touched are derived from the providers’ own logs and handed to every later task.
- Providers are watched, not trusted
- Their flag surfaces are live-probed and contract-tested, their output is ANSI-stripped and schema-validated before it is read or persisted.
CLI reference
A bare path argument is treated as baya run <file>. The full flag surface is in the CLI wiki page.
Commands
| Command | Purpose |
|---|---|
| baya <file> | Default form. Alias for run. |
| baya run <file> | Plan, resolve models, confirm, execute. |
| baya plan <file> | Plan and render the DAG; never executes. Same as run --dry-run. |
| baya doctor | Resolve every provider: path, version, capabilities. Reap stray process groups. |
| baya config | Re-run the setup wizard. Subactions: --show, path, set <key> <value>, refresh-models. |
| baya models [id] | Print the effective model catalog grouped by provider, each row tagged built-in or user. |
| baya upgrade [id] | Run each resolved provider's self-update argv; optional provider filter. |
| baya resume <runId> | Re-execute a run’s unfinished tasks; succeeded tasks are kept as context. --provider <id> re-runs elsewhere. |
| baya runs | List resumable runs — running, paused, failed, interrupted — newest first. |
| baya consensus <file|"prompt"> | Several provider CLIs debate one artifact until they agree. Alias con. Not a run — invisible to runs and resume. |
Frequently used run flags
| Flag | Meaning |
|---|---|
| --dry-run | Render the DAG with resolved models and exit. Nothing runs. |
| --yes | Auto-confirm the plan gate; at the model gate take a best match ≥ 0.85. Never answers a task question. |
| --max-parallel <n> | Global concurrency budget, default min(4, cpus). Per-provider caps apply on top. |
| --group-size <n> | Max tasks per provider process, default 3. 1 gives every task its own process. |
| --no-memory | Do not pass what earlier tasks learned. Every task starts blind — the A/B control for measuring memory. |
| --default-provider <id> | Fallback provider for tasks with no stated provider. Bypasses the first-run wizard. |
| --planner-provider <id> | Provider that parses the task list into a manifest. |
| --json | Machine-readable run report, models catalog, or runs list to stdout — always ANSI-free. |
Consensus flags
baya consensus takes its own flags below, plus the shared --json, --verbose, --quiet, --log-level, --no-color, and --no-progress. Full surface: wiki-llm/cli.md.
| Flag | Meaning |
|---|---|
| --providers <models> | Reviewers, named by model or alias — luna,sonnet. A bare provider id means that CLI’s own default model. Defaults to your configured default model; unset with no default exits 2. |
| --moderator <model> | The reconciler, named the same way. Defaults to your planner model. May also appear in --providers, in which case it reviews blind before reconciling. --moderator-model is a synonym. |
| --rounds <n> | Round ceiling, not a count — the debate stops earlier when nothing blocker or major is left. Default 5. |
| --kind <k> | question | plan | spec | review | idea | prompt. Skips the moderator’s classification call and fixes the access posture: plan, idea, prompt, and question run tool-less; spec and review get the workspace. question answers the artifact; prompt rewords it. |
| --ledger-budget <n> | Characters of its own prior rounds carried into each reviewer’s next prompt before compaction. Default 8000. |
| --output <f> | Write the final document to a file instead of stdout. The banner stays on stderr, so a piped stdout stays clean. |
| --no-diff | Suppress the change summary in the report. |
| --yes | Auto-confirm the cost gate. A non-TTY without it exits 2 after spending only pass 0. |
| --tools all | Force the full tool set regardless of the posture the moderator picked. |
Exit codes
| Code | Meaning |
|---|---|
| 0 | All tasks succeeded, or --dry-run completed. Consensus: the debate converged, or the ceiling was reached with a document. |
| 1 | A run had a task fail, skip, or park, or an uncaught exception (teardown still runs). Consensus: a reviewer or the moderator failed and no document survived. |
| 2 | A run hit a planner, manifest-validation, or model-gate error. Consensus: bad input, unknown provider, or an unresolved moderator. Nothing was executed either way. |
| 130 | SIGINT; children torn down. |
| 143 | SIGTERM; same teardown. |
Providers
Verified by live invocation, not from documentation. “Verified” means a task was run end to end and returned a valid task_result — a probed flag surface is not enough.
| Provider | Non-interactive | Schema enforcement | Status |
|---|---|---|---|
| codex | codex exec | file in / file out | Verified 2026-08-28 |
| claude | claude -p | inline --json-schema | Verified 2026-08-28 |
| opencode | opencode run | None | Verified 2026-08-31 |
| copilot | copilot -p | None | Partial |
| gemini | gemini -p | None | Deferred to v1.1 |
| grok | — | — | Planned, unprobed |
A task’s permission level is what it is allowed to do, not what it edits: a read-write task can write, run commands, and reach the network; a read-only task does none of the three. codex is the only provider that enforces this with an OS sandbox — a task that must not touch the tree belongs there.
Full flag surfaces, event shapes, and the capability matrix: wiki-llm/providers.md.
Configuration
Config is layered: built-in defaults, then ~/.config/baya/config.json (written by the first-run wizard), then per-directory .baya/config.json, then command-line flags — each overriding the one before.
baya config --showprints every effective value and the layer it came from.baya config pathprints the config file location.baya config set <key> <value>writes a single value.baya config refresh-modelsre-fetches theopencodemodel list and prunes catalog entries identical to a built-in one.
Full precedence rules and the first-run flow: wiki-llm/config.md.
Recovery & resume
The run is checkpointed to state.json before every transition — a crash never loses a step. If a provider fails, runs out of quota, or you press Ctrl+C, the unfinished work stays resumable.
baya runslists resumable runs — running, paused, failed, or interrupted — newest first.baya resume <runId>re-runs only the unfinished tasks in the run’s own directory; succeeded tasks are kept as context.baya resume <runId> --provider claudepicks the work up on a different provider — the answer to exhausted credits.
An unfinished run ends by printing that exact resume command, what it will re-run, and what to fix first. A quota failure halts the run cleanly rather than feeding the wall every remaining task.
Failure taxonomy and the full resume contract: wiki-llm/recovery.md.
How it works
Two commands, two shapes. A run turns a task list into a dependency graph and executes it; a consensus debate puts one artifact in front of several models and reports where they land.
A run
Freeform list to report, with the preview gate the only stop. Grouping is decided separately from the graph, so a chain can still be one process, and what each task discovers flows back to the scheduler for the tasks that follow. Detail: run and design principles.
flowchart TB
MD["tasks.md: freeform text"] --> P["Planner: an LLM CLI"]
P -->|JSON manifest| V{"Validate: schema, cycles, deps"}
V -->|invalid| R["Repair once, then linear fallback"]
R --> V
V -->|valid| G["DAG: topological layers"]
G --> GATE{"Preview and confirm"}
GATE -->|approved| S["Scheduler: budgets, single write-lock"]
S --> GRP["Group: same provider, model, access, cwd"]
GRP --> PROC["Agent processes: several tasks each, in order"]
PROC --> AD["Provider adapters: argv, prompt delivery, event parsing"]
AD --> CLIS["codex / claude / opencode / copilot"]
CLIS --> RES{"task_result JSON"}
RES -->|ok| BUS["Context bus: feeds dependents and later tasks"]
RES -->|needs_input| ASK["Bubble the question"]
RES -->|failed| REC["Classify failure, mark resumable"]
BUS --> S
BUS --> OUT["Report: outcomes, flagged notes, resume command"]
ASK --> OUT
REC --> OUTA consensus debate
The moderator writes the criteria and reconciles each round but never picks the answer; reviewers critique in parallel and never see each other’s findings in the same round. The loop stops when nothing blocking is left or --rounds is hit. For a plain question there are no criteria and the moderator only reports agreement. Detail: consensus and wiki-llm/consensus.md.
flowchart TB
ART["artifact: a file or a prompt"] --> P0["Moderator pass 0: classify, write criteria, pick access posture"]
P0 --> ROUND{"Round N: fan out to reviewers, in parallel and blind"}
ROUND --> RA["Reviewer A"]
ROUND --> RB["Reviewer B"]
ROUND --> RC["Reviewer C"]
RA --> MERGE["Moderator: reconcile findings into a new draft"]
RB --> MERGE
RC --> MERGE
MERGE --> CONV{"any blocker or major finding, and rounds left?"}
CONV -->|yes| ROUND
CONV -->|no| OUT["Final document, rejected findings, unresolved disagreements"]Contributing
Contributions are welcome. The work is broken into sequenced tasks in specs/001/02-plan.md, each with its own done-criteria, so there is plenty to pick up independently.
Before opening a PR:
npm run typecheck && npm run lint && npm testA few rules are load-bearing rather than stylistic — the full list is in wiki-llm/conventions.md:
- No
shell: true, ever. Spawns takeargv: string[]. - Never document a provider flag you have not actually run.
- Never regex a model’s prose for meaning — semantics come from validated JSON.
- Update the affected
wiki-llm/page in the same commit as the change. - Read provider event shapes out of a recorded run in
.baya/runs/, not out of a provider’s docs. - Tests never touch the network; the contract tier is opt-in via
BAYA_CONTRACT=1.
Adding a provider is deliberately small: one adapter, one capability block, one section in providers.md, one contract test. New contributors should start with conventions.md, then the plan.
.baya/runs/<runId>/ behind — real provider event streams on a real repository, for free. That corpus is the best fixture set the project has. Mine it before inventing an input, then pin what you find with a committed test.Still have a question? The FAQ covers why Baya sits alongside your existing CLIs, what it does for your bill, and whether parallel runs are safe.