CLI reference
After this page you can invoke any bullswarm verb with the flags the running binary actually accepts, and look up a default without guessing.
There are exactly two ways to start work: bullswarm run dispatches one bounded task, and bullswarm workflow goal executes a program you author. Reach for a specific command's --help when this page and the binary might have drifted after an upgrade.
# Print the top-level command list, then one command's full help.
bullswarm --help
bullswarm run --help--help / -h / a trailing help never reads or writes state, calls a network endpoint, or spawns a process. Every other command self-initializes ~/.bullswarm/state.json (or $BULLSWARM_HOME) on first use. An unrecognized option prints unknown flag --name plus that command's synopsis and exits 2, before routing or spawning anything. Malformed values (a missing --lane, a --limit that is not a positive integer) are rejected at the same boundary with exit 2.
Bare bullswarm opens setup until the installation is configured, then opens the dashboard. It accepts every option that setup documents. --yes on the bare command skips the interactive wizard and auto-initializes with discovered defaults (default: prompts on a TTY; auto-initializes for a non-TTY caller).
The root dashboard
Once setup has completed, bare bullswarm opens the full-screen dashboard on Home. bullswarm --setup or bullswarm setup opens the setup control centre; bullswarm workflow tui is the explicit dashboard form. The dashboard has Home, Runs, Run, Step, Budget, Stats, Fleet, and Help pages, and a five-tab row — Home, Runs, Budget, Stats, Fleet. Home answers what happened today and what is active; Runs is the active block over the day-grouped history table, plus the product commands; Run and Step explain a workflow and one action; Budget shows quota, measured worker-time share, and labelled money; Stats shows period trends and pool/model/project breakdowns; Fleet shows lane/provider rungs; Help names the controls. A sticky header and bottom nav keep the page and run controls visible. The shared keys are h (Home), r (Runs), b (Budget), s (Stats), y (the first day header of the Runs history table), f (Fleet), ? (Help), 1–9 (open a run), Tab (sub-tabs), Shift+Tab (workflows), p (period), Esc/← (back), arrows (one line), PgUp/PgDn (one screen), Home/End (top/bottom), ctrl+s (copy), and q (quit). Enter/→/l opens the selection. In 0.33.0, r no longer refreshes, b no longer moves out, Tab no longer cycles workflows, and h opens Home rather than Help; the view refreshes itself, Esc/left moves out, and Shift+Tab still cycles workflows. Mouse reporting lets you click any tab, tile, bar, run, step, date, or control, and the wheel scrolls the body. The Claude Mod is this dashboard's read-only counterpart: its pane keeps only Run, Step, and its Usage/Pools view; it does not expose the dashboard's Home, Runs, Budget, Stats, Fleet, or Help pages, nor edit/install actions.
# main screen after setup
bullswarm
# open setup explicitly
bullswarm --setup
bullswarm setup
# explicit dashboard entry point
bullswarm workflow tuisetup
Discover installed agent CLIs and initialize local routing state. Bare setup on a TTY opens the provider/model control center; agents and CI retain deterministic non-interactive setup.
# Non-interactive initialization plus skill install for Claude and Codex.
bullswarm setup --yes --integrate --agents claude,codex| Flag | Meaning | Default |
|---|---|---|
--wizard | open the question-based wizard for worktree, reasoning-depth, and integration settings | off; bare setup opens the provider/model control center |
--yes | skip interactive setup and initialize with discovered defaults | interactive control center on a TTY |
--strategy | discover models, set each pool's recommended model per effort tier (its rungs; no tier is pinned), and enable strategy autopilot; requires --yes | off |
--integrate | also install agent integration (skill symlink + awareness block); requires --yes | off |
--agents <list> | comma-separated agent list for --integrate (codex, claude, grok) | all three |
--json | machine-readable result | human summary |
Writes state.json and routing.json. With --integrate --yes, also writes under ~/.codex, ~/.claude, ~/.grok. A non-TTY caller auto-applies discovered defaults without prompting, even without --yes.
home
Create a compact, dashboard-readable copy of a Bullswarm home without touching the source. home snapshot copies routing, provider and meter state (including meter history), connectors, calibration, the single-task ledger, and only the selected workflow run directories. It then rebuilds each copied rollup and history/runs.jsonl, so Home, Runs, and Stats describe the same trimmed set.
# Keep the three newest workflow runs and omit large per-attempt streams.
bullswarm home snapshot /tmp/bsw-snapshot --recent 3 --no-streams --json
# Select runs by their short ids instead.
bullswarm home snapshot /tmp/bsw-snapshot --runs ab12cd,ef34gh| Flag | Meaning | Default |
|---|---|---|
--runs <shortId,...> | comma-separated short ids or full wf-... run ids to copy | three newest workflow runs |
--recent <n> | copy the newest n workflow runs | 3 |
--since <date> | keep only runs started at or after an ISO/date-only bound; relative bounds such as 7d are accepted | unbounded |
--no-streams | omit stream-*.jsonl and stdout-*.log from copied workflow directories | keep them |
--json | print destination, byte size, and the selected run list as JSON | human summary |
The destination must be new or empty and outside both the source home and the live ~/.bullswarm. Single-task records remain available in the copied state.json, assignments/, and runs/ surfaces; only workflow directories are trimmed. Point inspection commands at the copy with BULLSWARM_HOME:
BULLSWARM_HOME=/tmp/bsw-snapshot bullswarm workflow tui --json --allSnapshot destinations carry a small marker so workflow tui --json includes the complete copied catalogue by default; ordinary homes retain the usual ongoing-only default and need --all for historical runs.
home prune
List or remove retained isolated-workspace copies from old terminal runs. Bare home prune and --dry-run are read-only; deletion requires --yes.
bullswarm home prune --dry-run
bullswarm home prune --yes --days 7| Flag | Meaning | Default |
|---|---|---|
--dry-run | list eligible workspaces and bytes without removing them | on unless --yes is passed |
--yes | remove the listed workspace copies | off |
--days <n> | override state.json.retention.workspacesDays for this command | 7 |
--json | machine-readable plan/result | human rows and summary |
Internal --auto and --trigger flags run the same guarded sweep from the kernel, watch completion, or dashboard. Records, reports, streams, task/output files, running/interrupted runs, and live kernel workspaces are never removed.
home status
Print the effective retention policy, bytes currently held under workflow workspaces/, and the last recorded prune and reprice maintenance results. --json emits the same facts as a machine-readable object.
integrate
Manage the packaged Bullswarm skill and a short recursion-safe awareness rule inside each installed coding agent's global configuration.
# Check which agents already have the skill and awareness block.
bullswarm integrate status| Flag | Meaning | Default |
|---|---|---|
--agents codex,claude,grok | restrict the action to specific agents | all three |
--json | machine-readable output | human summary |
Install refuses to replace a non-Bullswarm file at the same path. retire-legacy renames, never deletes, the legacy skill directory.
status
Report, per agent, whether the packaged skill symlink and the awareness block are installed, and whether a legacy pre-Bullswarm offload skill needs retiring.
# Machine-readable install report for every discovered agent.
bullswarm integrate status --json| Flag | Meaning | Default |
|---|---|---|
--agents codex,claude,grok | restrict the report | all three |
--json | machine-readable output | human summary lines |
Does not change agent integration files.
install
Symlink the packaged skill and append the awareness block marker into each selected agent's global instructions file.
# Install for Codex and Claude. --yes is required.
bullswarm integrate install --agents codex,claude --yes| Flag | Meaning | Default |
|---|---|---|
--agents codex,claude,grok | restrict install | all three |
--yes | required — approves writing global agent configuration | none; the command refuses without it |
--json | machine-readable output | human summary |
Idempotent. Refuses to replace a non-Bullswarm skill path.
remove
Remove only the Bullswarm-managed skill symlink and awareness marker block, leaving any other content in the agent's configuration untouched.
# Remove the managed symlink and awareness block for every agent.
bullswarm integrate remove --yes| Flag | Meaning | Default |
|---|---|---|
--agents codex,claude,grok | restrict removal | all three |
--yes | required — approves editing global agent configuration | none; the command refuses without it |
--json | machine-readable output | human summary |
A conflicting non-symlink path is left untouched, not deleted.
retire-legacy
Recoverably archive the retired pre-Bullswarm ~/.claude/skills/offload skill so it stops shadowing Bullswarm guidance for Claude.
# Move the legacy offload skill into ~/.claude/skills-archive/.
bullswarm integrate retire-legacy --yes| Flag | Meaning | Default |
|---|---|---|
--yes | required — approves moving a user skill directory | none; the command refuses without it |
--json | machine-readable output | human summary |
Renames into ~/.claude/skills-archive/. A no-op if the legacy skill is not installed.
run
Dispatch one bounded task to the best-available delegate pool (or keep it on the calling agent when nothing suitable is eligible), then verify the saved output before reporting a verdict.
# Route one analysis task and print the verdict.
bullswarm run --lane analyze --add-dir . "List every TODO comment in src/ with file:line"Trailing <task text...> is mutually exclusive with --prompt and --task-file.
| Flag | Meaning | Default |
|---|---|---|
--lane <analyze|build|chore> | routing lane: analyze (exploratory/large), build (implementation), chore (small/cheap) | required — omitting it, or passing anything else, exits 2 |
--add-dir <dir> | working directory the delegate operates in | current directory |
--task-file <file> | read the task text from a file instead of trailing words | unset |
--prompt <text> | pass the task text inline as one flag value | unset |
--effort <high|medium|low> | override the effort tier used for model-tier routing | derived from --lane (analyze→medium, build→medium, chore→low) |
--reasoning <low|medium|high|xhigh|max|default> | run-wide thinking-level override, clamped to what the picked pool's connector accepts; default passes nothing | strategy reasoning setting for the effort tier, else the connector default |
--timeout <seconds> | hard wall-clock kill timer for the delegate process | none — the delegate is allowed to run to completion |
--heartbeat <seconds> | print one compact progress heartbeat to stderr per interval without streaming delegate output | off |
--answer-schema <file> | a JSON schema for a typed answer; the verdict carries answer and answerCheck, and a mismatch is failure kind schema | none |
--no-retry | one attempt only | off — one retry |
--avoid-pool <pool,...> | never route this task to these pools | unset |
--use-provider <provider,...> | route only to pools of these providers | unset |
--avoid-provider <provider,...> | never route to pools of these providers | unset |
--dry-run | print the kernel's routing pick, the forecast, and the exact command that would be spawned, without spawning, recording a run, registering an assignment, or writing the decision log | off (dispatches for real) |
--no-caller | accepted and ignored for one release: the calling agent is never a pool of its own run | removed in 0.37.0 |
--json | print the compact machine-readable verdict; top-level pool and model name who ran the last attempt (null when nothing was dispatched; pick keeps the same two beside the command); its last field, details, is the command for the full record and per-attempt cost (bullswarm workflow runs result <shortId> --json) | human-readable summary ending with the run id and that command |
A run is a one-step workflow, recorded under workflows/<id>/ (bullswarm workflow runs --all). It gets the step's one automatic retry unless --no-retry; a usage limit exits 1 with no retry, and the pool's meter is read again at once, so a window it shows at 100% keeps the pool out of later picks until that window resets. Nothing else about a failed pool is remembered. A build or chore run must change a file (else failure kind not-produced). The JSON shape is in Result envelope.
# Show the exact argv, including the clamped reasoning flag, without dispatching.
bullswarm run --lane build --add-dir . --reasoning max --dry-run --json "Refactor the loader"What a run records
A real dispatch (not --dry-run) writes two durable records, which is what makes a single task visible on the dashboard alongside workflow runs:
- While it runs, an assignment in
~/.bullswarm/assignments/withsource: "run", plusproject,cwd,taskFile,outFileandstartedAt. This is the row theRunspage shows in itsactiveblock and the Claude mod shows on its pane. - When it finishes, a decision-log entry in
state.jsonwithkind: "run". Alongside the pool, model, verdict and route it always recorded, the entry now carrieslane,taskFileandoutFile, plusid,project,cwd,startedAt,endedAtanddurationMs. Those are the fields theRunshistory day table and its task detail read; an entry written before them shows—for what it does not have rather than a guess.
Free-model liveness probe
Before a real dispatch to a pool whose rung for this effort tier names a free model, run sends the one-word prompt PONG through that pool's own CLI and waits at most 30 seconds. The answer is cached per pool and model for 15 minutes in $BULLSWARM_HOME/cache/free-model-probes.json.
A pool that fails the probe is dropped from this pick with the reason probe: 404, probe: provider error or probe: timeout, and routing falls through to the next eligible pool — the reason appears in the run's routeWhy. A rung left on the connector's CLI default is never probed, a paid model is never probed, no model catalogue is scanned, and a replacement model is never chosen. --dry-run never probes: a preview does not call a provider.
health
Re-judge every saved delegate output against the real verify gate and report where a logged FAIL verdict re-judges as a pass (the "gate ate real work" signal).
# Re-judge saved outputs. Exit 1 means unhealthy, not a crash.
bullswarm health --json| Flag | Meaning | Default |
|---|---|---|
--json | print the machine-readable health report | human-readable summary of the same facts |
Reads every out-* file under ~/.bullswarm/runs/ and state.json, and never changes state.json.
pools
Show every configured pool: cost rank, lanes, live meter usage/elapsed percentage, pace surplus, in-flight assignment count, projected 5-hour utilization, and whether a window is spent.
The 5-hour column reads 5h=<reading>% alone when nothing is in flight and 5h=<reading>%-><projected>% when in-flight work is expected to push the window further; routing decides on the right-hand number. A trailing (<n>% elapsed) is how much of that 5-hour window has already run. A pool whose weekly window resets within 24 hours, or whose monthly window resets within 3 days, ends its line with resets in <Nd Nh|Nh Nm|Nm> EXPIRING-SOON urgency=<n>.
The pool's status comes after the columns: disabled, or ready followed by BURST-GATED when the 5-hour window is at 100% and NEAR-5H-LIMIT when it is near its line. A weekly or monthly window at 100% keeps the pool out of routing until that window resets; the meter column shows the pacing window's reading (weekly used 100%). After a usage limit whose forced meter read failed, the meter source reads [blocked · refused <age>]: the pool counts as full until the reset the provider named or an earlier reading gave. When nobody knows that reset it reads [refused <age> · reset unknown] instead (with used ?%) and does not keep the pool out. Nothing pauses or benches a pool, so there is nothing to lift: a spent window comes back by itself when it resets.
A limit notice that says a usage window, a quota or a balance is spent is a usage limit, with or without a reset named. In a workflow started by this version it ends the step and comes back to you at once; its reset, when known, is the back at time. A single bullswarm run makes one attempt and exits 1. Either way the pool's meter is read again at once, and a window it shows at 100% keeps the pool out of later picks until that window resets.
Any other rate limit — Error: Rate limit exceeded. Please wait a moment and try again., 429 Too Many Requests, an overload — backs off on the same pool at most twice (20 s, then 60 s, or the wait it names when that is at most 2 minutes) and then comes back to you; one that names a longer wait comes back to you at once, with back at at the end of that wait. Nothing moves to another pool, and a single bullswarm run exits 1 on it. In a workflow started by an earlier version the attempt backs off and retries on the same pool, then moves to another pool for that attempt only.
Quota and auth signatures are matched against the provider's own error channel: its stderr, the events it flags as errors, and its terminal result record. Never an assistant's reply or a tool result — a report that quotes a limit phrase is not evidence about the pool.
# Bypass the meter cache and re-read live usage for every pool.
bullswarm pools --force| Flag | Meaning | Default |
|---|---|---|
--force | bypass the meter cache and re-read live usage for every pool | off (cached meter readings reused within their TTL) |
--json | machine-readable { pools }; each pool carries inflight, spend rates, pacingWindow, paceResetsAt, resetSource (provider | declared | null), and projected percentages | human-readable aligned table |
--json is also where the per-window numbers behind the Budget page live. Each entry's meterSnapshot is the provider's own reply: captured_at (how fresh the reading is), and one block per window it reported — five_hour, seven_day, monthly, each with utilization and resets_at — plus the plan fields Budget detects a price from (plan_type, subscription_type, rate_limit_tier). A window the provider did not report is null, never zero.
{ "captured_at": "2026-09-18T10:08:18.767Z", "pool": "claude-code:acme",
"five_hour": { "utilization": 61, "resets_at": "2026-09-18T11:00:00.643684+00:00" },
"seven_day": { "utilization": 84, "resets_at": "2026-09-18T18:00:00.643713+00:00" },
"monthly": null, "plan_type": "team", "rate_limit_tier": "default_claude_max_5x" }Never changes state.json. Reading the in-flight ledger prunes entries left behind by crashed processes (dead pids, or older than 12 hours).
assignments
List the work in flight right now across every Bullswarm process — one line per live assignment with its pool, lane/effort, source, run/action, age in minutes, expected duration, and worker pid. This is the shared ledger bullswarm pools counts as inflight=<n>.
# One line per live assignment, including runs started by another terminal.
bullswarm assignments| Flag | Meaning | Default |
|---|---|---|
--json | machine-readable array of live assignment records (pool, source, runId, actionId, kernelPid, workerPid, startedAt, elapsedMinutes, expectedMinutes, remainingMinutes) | one human-readable line per assignment |
Reads ~/.bullswarm/assignments/ only — no meters, no network, no pool build.
strategy
Open the provider/model strategy control center on a terminal, or run a subcommand non-interactively. Toggle whole providers, assign each detected model to any combination of high/medium/low effort, and preview the live route.
# Inspect providers, models, selections, meters, and effective routes.
bullswarm strategy inventory --json--json is accepted on subcommands that support it. refresh / apply / assign / clear-assignment / exclude-model / include-model / set-subscription / set-reasoning / reset-reasoning / set-rung all mutate state.json. refresh (and a cold show) perform live discovery against every installed agent CLI and the public OpenRouter model API.
Concepts (rungs, allow-lists, autopilot) are in Configuration.
tui
Open the full-screen provider and model strategy control center. Analysis previews one OpenRouter-backed default per provider/tier before Y applies it. Enter on a provider opens its card: one row per effort tier with its reasoning level, an indented row for each model selected on that tier, then the model matrix. ←/→ on a tier row steps that provider's reasoning through auto, the levels its CLI accepts, and "CLI decides" (default), the same write as strategy set-reasoning --pool; on a model row it sets a level for that model only (--pool --model), which beats the tier row. / searches the models. Provider Space toggles, reasoning changes, and model-tier Enter toggles persist immediately; select Finish setup or press F to exit.
# Browse providers, models, effort tiers, meters, and effective routes.
bullswarm strategy tuiNo flags. Bare bullswarm strategy on a TTY is the same control center.
inventory
Return detected provider pools, models, selections, live meters, and effective routes for an agentic caller. The output is always JSON.
# Rerun model discovery and meters, then dump the inventory.
bullswarm strategy inventory --json --refresh| Flag | Meaning | Default |
|---|---|---|
--json | accepted so agents can pass it uniformly; it selects nothing | output is always JSON, with or without the flag |
--refresh | rerun model discovery and meters | off |
Read-only apart from refreshing the cached discovery report.
routes
Show the live effective choice for high/analyze, medium/build, and low/chore. The output is always JSON.
# Print the currently effective high/medium/low choices.
bullswarm strategy routes --json| Flag | Meaning | Default |
|---|---|---|
--json | accepted so agents can pass it uniformly; it selects nothing | output is always JSON, with or without the flag |
--refresh | refresh meters first | off |
rungs
List every rung: one pool's model plus its reasoning level for one effort tier, with the dated benchmark evidence for that exact pair and what this machine actually recorded for it.
# Machine-readable rungs for one pool.
bullswarm strategy rungs --json --pool codex| Flag | Meaning | Default |
|---|---|---|
--json | machine-readable rows (the same rows strategy inventory --json carries under rungs) | human table |
--pool <name> | limit the table to one provider pool | every enabled pool |
Read-only: writes no state, spawns no model discovery, and downloads no meter or datapack. A rung with no benchmark row prints no evidence; one with no matching attempt prints no dispatches. Neither is ever estimated.
set-provider
Enable or disable one detected provider/account as a whole.
# Take relay:b out of the routing set.
bullswarm strategy set-provider relay:b off --yes| Flag | Meaning | Default |
|---|---|---|
--yes | required approval | none |
Arguments: <pool> (exact name from strategy inventory) and <on|off>. Changes routing immediately for new dispatches.
set-model
Assign one model to one or more effort tiers.
# Put one model on every effort tier for the relay pool.
bullswarm strategy set-model relay a/gpt-5.6-sol --tiers high,medium,low --yes| Flag | Meaning | Default |
|---|---|---|
--tiers <list|off> | comma-separated multi-selection or off | required |
--yes | required approval | none |
A configured tier becomes an allow-list; leaving it with no enabled models makes that tier unavailable.
set-rung
Set one rung — the model and the reasoning level a pool uses for one effort tier — in a single atomic state save.
# Move the codex high rung onto gpt-5.6-sol thinking at xhigh.
bullswarm strategy set-rung codex high --model gpt-5.6-sol --reasoning xhighArguments: <pool> and <tier> (high, medium, or low).
| Flag | Meaning | Default |
|---|---|---|
--model <model> | model to run on that tier; must appear in the pool's cached discovery unless --force | required |
--reasoning <level> | low, medium, high, xhigh, max, or default | leaves the configured level untouched |
--force | accept a model the cached discovery has not seen | off |
A rung is singular per pool and tier: the tier moves off whichever model held it, and that model keeps its other tiers. Never spawns model discovery — an unknown pool, tier, or model exits 2 instead.
reset-tier
Remove the explicit model allow-list for one effort tier and return it to automatic routing.
# Restore low to automatic routing.
bullswarm strategy reset-tier low --yes| Flag | Meaning | Default |
|---|---|---|
--yes | required approval | none |
Argument: <high|medium|low>. Removes that tier from every model selection and clears its legacy pin.
set-reasoning
Set how hard one effort tier thinks. This is a separate dimension from the model.
# Every pool's high tier thinks at xhigh, clamped per connector.
bullswarm strategy set-reasoning --tier high --level xhigh --yes| Flag | Meaning | Default |
|---|---|---|
--tier <high|medium|low> | effort tier to configure | required |
--level <level> | low, medium, high, xhigh, max, or default | required |
--pool <name> | apply to one provider pool only | every pool |
--yes | required approval | none |
A level a CLI cannot express is clamped down to the strongest level it accepts, never up.
reset-reasoning
Remove configured reasoning levels so the affected tiers fall back to each connector's own defaults.
# Clear the low-tier reasoning override everywhere.
bullswarm strategy reset-reasoning --tier low --yes| Flag | Meaning | Default |
|---|---|---|
--tier <high|medium|low> | clear one effort tier everywhere | every tier |
--pool <name> | clear one provider pool only | the global tier defaults too |
--yes | required approval | none |
With no --tier and no --pool this clears every configured reasoning level at once. --tier alone also removes that tier from every per-pool override.
configure
Atomically apply provider toggles, model tier combinations, and reasoning depth from an agent-authored JSON file.
# Validate and save a complete strategy document.
bullswarm strategy configure --file strategy.json --yes| Flag | Meaning | Default |
|---|---|---|
--file <json> | object with optional providers, models, and reasoning maps. reasoning is { "tiers": { "high": "xhigh" }, "pools": { "codex": { "high": "high" } } } where each level is low, medium, high, xhigh, max, default, or null to remove it | required |
--yes | required approval | none |
Validates the complete document before saving. An invalid reasoning section rejects the whole document; nothing is written.
refresh
Run live model discovery against every installed agent CLI and recompute high/medium/low tier recommendations from capability and cost. strategy recommend is an alias with identical options and behavior.
# Discover models and print the report without changing routing.
bullswarm strategy refresh --json| Flag | Meaning | Default |
|---|---|---|
--json | print the full report as JSON | human-readable summary |
--apply | also approve and persist the resulting recommendations; requires --yes | off (discovery only) |
--yes | required alongside --apply — approves changing routing | none; --apply refuses without it |
--refresh-hours <n> | auto-refresh cadence to record when combined with --apply | 24 |
Writes the report to state.json; with --apply --yes also writes each pool's recommended rungs, the reasoning level a suggestion carries (never over one you set), and enables the auto-refresh policy. It pins no tier: see apply.
apply
Approve and persist the most recently discovered recommendations (from the last refresh or show) without running discovery again.
# Persist the last refresh and enable autopilot at the default cadence.
bullswarm strategy apply --yes| Flag | Meaning | Default |
|---|---|---|
--yes | required — approves changing routing | none; the command refuses without it |
--refresh-hours <n> | auto-refresh cadence to record | 24 |
Writes each pool's recommended model per tier (state.strategy.modelTiers, the pool's rungs) and the auto-refresh policy. It never pins a tier, so every dispatch still picks its pool by spare quota and runs that pool's rung model and reasoning. strategy rungs shows the rungs; strategy routes shows what routing picks now.
Pins are explicit only: assign makes one and clear-assignment removes it. Apply keeps every pin you set (keptPins in its JSON). Versions up to 0.35.4 pinned every tier on apply and recorded that pin in lastReport.suggestions[tier].assignment. Apply and the auto-refresh remove such a pin when it still has the recorded pool and model (unpinned); a pin that differs from the record, or one from a home where apply never ran, is treated as yours and kept. The JSON also carries rungs (pool → tier → model) and bestNow (the tier-wide pick, for display only).
A suggestion that carries a reasoning level (a newest-generation fallback, such as medium: gpt-6-luna · max reasoning — no gpt-6 terra yet, newest generation preferred, or Grok's low: grok-4.7 · low reasoning — one Grok line, at each tier's own reasoning) also has that level written into its pool+tier rung, marked as the recommendation's. A level you set per pool or per tier is never overwritten.
show
Print the last captured strategy report (subscriptions, tier suggestions, exclusions). Runs a first discovery pass automatically if none is cached yet.
# Print the cached strategy report as JSON.
bullswarm strategy show --json| Flag | Meaning | Default |
|---|---|---|
--json | print the full report as JSON | human-readable summary |
Does not change routing. A tier line such as high: codex/gpt-6-astra (best now; routing picks by spare quota) names the best pick right now; it is not a pin. A pinned tier adds a line such as pinned to claude-code/claude-opus-5 by you · high dispatches go there while it is available · strategy clear-assignment high removes it. Models no family rule ranks are counted per pool on one line, unranked: 312 models (command-code 180, opencode 130, grok 2) · never recommended · strategy show --json lists them; --json keeps the full unranked list. Two lines read the home's current state rather than the cached report: free models: never (all pools) · openrouter: allow (see set-free), and, when a provider refused a model because a pool's plan does not include it, not in plan: command-code: gpt-6-astra (strategy include-model <model> clears it). That pool stays offered with its other models; --json has the same facts under notInPlan.
assign
Pin one pool/model for an effort tier: dispatches of that tier go to that pool, running that model, while the pool is available, instead of the pool with the most spare quota.
# Pin high to a specific Claude model.
bullswarm strategy assign high --pool claude-code --model claude-opus-5| Flag | Meaning | Default |
|---|---|---|
--pool <pool> | connector/pool name to assign | required; no default |
--model <model> | exact model identifier to assign | required; no default |
Argument: <high|medium|low>. Writes state.strategy.assignments[tier] as { pool, model, source: "user" }, makes the model that pool's rung for the tier when the tier has rungs, and invalidates the cached report. A pin you set is never removed by apply or the auto-refresh; only clear-assignment (or set-model, reset-tier, configure for that tier) removes it.
clear-assignment
Remove a tier pin, so dispatches of that tier go back to the pool with the most spare quota, running that pool's rung model.
# Release the high-tier pin.
bullswarm strategy clear-assignment highNo flags. Argument: <high|medium|low>.
exclude-model
Persistently prevent this exact model from orchestration and worker dispatch, even if it would otherwise be recommended or assigned.
# Block gpt-5.4-mini from every dispatch.
bullswarm strategy exclude-model gpt-5.4-miniNo flags. Argument: exact model identifier. Reverse with include-model.
include-model
Remove a previously persisted model exclusion, and forget every pool's record that its plan does not include the model (failure kind model-not-in-plan).
# Unblock a previously excluded model.
bullswarm strategy include-model gpt-5.4-miniNo flags. Argument: exact model identifier.
set-subscription
Record known subscription pricing for a pool so refresh's value-multiple math is accurate, choose the quota window routing paces this pool by, and declare when that window ends for a provider that reports usage but no reset date.
# Record plan economics, then declare a refill date a wallet does not report.
bullswarm strategy set-subscription claude --plan max --monthly-usd 200 --included-usd 1000
bullswarm strategy set-subscription opencode --resets-at 2026-09-17T01:46:01Z| Flag | Meaning | Default |
|---|---|---|
--plan <name> | plan label to record | unchanged |
--monthly-usd <n|unknown> | monthly subscription price | unchanged |
--included-usd <n|unknown> | estimated included usage value | unchanged |
--quota-window <weekly|monthly> | the subscription window that paces routing for this pool (used% vs elapsed% of it); unknown clears it back to the connector default | unchanged |
--resets-at <iso|unknown> | the date-time this pool's quota window next ends, used only when the provider reports usage but no reset; a provider-reported reset always wins; unknown clears it | unchanged |
Writes state.strategy.subscriptions[pool] and invalidates the cached report. A changed subscription also retires the pool's not in plan records made under the old one.
set-free
Choose whether strategy may suggest, apply or dispatch a free model (a connector model declared free, or an id such as openrouter/x:free). The default is allow.
# No free model on any pool; then let one pool use them again.
bullswarm strategy set-free never --yes
bullswarm strategy set-free allow --pool openrouter --yes
bullswarm strategy set-free reset --pool openrouter| Flag | Meaning | Default |
|---|---|---|
--pool <name> | set it for this pool only; a pool's own setting wins over the every-pool one | every pool |
--yes | required for allow and never, which change routing | none; exits 2 without it |
Argument: allow, never, or reset (drops one pool's own setting; needs --pool, no --yes). Under never a pool whose tier has a non-free model switches to it; a pool left with only free models for a tier is out of that tier, with the reason free models are off for <pool> (a step no pool can take names it in its why and routeCandidates, and strategy show prints it under the tier). A model you name yourself (a step's model, --worker-model) is still allowed. Writes state.strategy.freeModels / freeModelsByPool and invalidates the cached report; with autopilot on, its next check re-picks every rung. A usage mistake exits 2 and writes nothing.
auto
Inspect or disable the policy that re-applies discovery recommendations automatically on a cadence, set by a prior apply or refresh --apply.
# Show whether auto-apply-on-refresh is enabled and its cadence.
bullswarm strategy auto status| Flag | Meaning | Default |
|---|---|---|
--yes | required for off — approves changing routing policy | not needed for status |
auto status is read-only. auto off --yes writes state.strategy.policy and keeps the last-applied rungs, and any pins you set, as-is.
provider
Manage providers, the plugins that define pools. A provider is a directory with a connector.json and/or a provider.mjs. Bare bullswarm provider runs list. Loading is not routing: whether a loaded pool gets work stays bullswarm strategy set-provider <pool> on|off --yes.
# See what loaded and what failed.
bullswarm provider listThe contract is Providers.
list
Show every provider — first-class, contrib, and local — with its tier, whether it is loaded, the pools it returned, the pools the loader skipped, its load error, and whether it exports readUsage.
# Machine-readable provider table.
bullswarm provider list --json| Flag | Meaning | Default |
|---|---|---|
--json | print the report as JSON | human table |
Read-only; loads every provider module and calls connectors() for the loaded ones.
enable
Load a contrib provider: add its directory name to ~/.bullswarm/providers.json. Its pools appear on the next bullswarm start.
# Load the shipped command-code contrib provider.
bullswarm provider enable command-codeNo flags. Argument: a directory name under providers/contrib/. Refuses a name with no contrib directory, and a name a local provider already claims. Writes providers.json only; state.json is never touched.
disable
Stop loading a contrib provider: remove its name from ~/.bullswarm/providers.json.
# Unload a contrib provider. A name that is not listed writes nothing.
bullswarm provider disable command-codeNo flags. Argument: a contrib provider name listed in providers.json.
validate
Import a provider directory, check name and the export types, call connectors() with the real shipped templates, and check the prefix rule and every returned pool against src/providers/_schema.json.
# Check a local provider directory against the contract.
bullswarm provider validate ~/.bullswarm/providers/relay| Flag | Meaning | Default |
|---|---|---|
--json | print the report as JSON | human report |
Argument: a provider directory path, or a provider name looked up in the local, contrib, then first-class directories. Runs the module and connectors(); never spawns the CLI or reads usage. Exits 2 on any failure; warnings alone exit 0.
scaffold
Write a commented provider directory: a provider.mjs skeleton showing every export and, with --from, a connector.json copied from that shipped template and renamed. The result passes validate unchanged.
# Scaffold a reseller that runs through OpenCode.
bullswarm provider scaffold relay --from opencode| Flag | Meaning | Default |
|---|---|---|
--from <template> | copy this shipped provider's connector.json (a reader meter is set to none until you export readUsage) | no connector.json; the pool is written inline |
--dir <path> | the provider directory to create | ~/.bullswarm/providers/<name>/ |
Argument: provider name (lowercase letters, digits, and dashes). Writes only inside the target directory, and refuses one that already exists and is not empty.
probe
Spawn one pool's CLI directly with the task "Reply with the single word PONG and nothing else" through the dispatcher's own runner, then, when its provider exports readUsage, read usage once with the pool's declared subscription.
# Acceptance step: spawn the CLI once and print argv, output, elapsed time, snapshot.
bullswarm provider probe relay:b --json| Flag | Meaning | Default |
|---|---|---|
--json | print the result as JSON | human report |
--timeout <sec> | kill the CLI after this many seconds | 300 |
Argument: a pool name; contrib providers are probed even when not enabled. Spawns in a throwaway directory with no routing, quota gate, or assignment ledger, and ignores BULLSWARM_DEPTH. Values from the pool's env are redacted. Exits 1 when the reply does not contain PONG or readUsage threw.
doctor
Report installation readiness — config present, at least one agent CLI discovered, meters reachable, at least one delegate pool enabled — with the exact fix command for anything failing. The connector-copies check warns (!, never a failure) about an edited copy of a packaged connector that an install older than 0.29.0 left in <home>/connectors/, naming its stale fields and whether it is read at all.
# Machine-readable { version, configured, ok, checks[], nextActions[] }.
bullswarm doctor --json| Flag | Meaning | Default |
|---|---|---|
--json | machine-readable { version, configured, ok, checks[], nextActions[] } | human-readable checklist with ✓/!/✗ per check |
Self-heals: if ~/.bullswarm is not yet configured, runs the same auto-setup as any other verb before reporting. Exit code is 0 when every check passes, 1 if any check fails.
workflow
Plan, execute, observe, and audit durable multi-agent workflows of steps, phases, gates and loops. With no command on a TTY, opens the unified full-screen workflow home. Non-interactive callers receive this help text instead.
# Author plan.json from `workflow plan contract`, then launch.
bullswarm workflow goal "1. Fix the parser. 2. Verify it." --cwd . --program plan.jsonOnly a v3 run is driven. A run an earlier Bullswarm started (v2, earlier formats, legacy) is view-only: runs, show, result, watch and the dashboard keep reading it, and resume, goal --resume, pause, steer and step rerun/accept/restart exit 2 with run <id> was started by an earlier Bullswarm and is view-only; start a new run: bullswarm workflow goal "<goal>" --cwd <run folder> --program <file.json>. Goal dispatches real coding-agent CLI processes and writes durable state under ~/.bullswarm/workflows/<runId>/. How to author a program is in Workflows; the JSON fields are in Workflow program.
goal
Run a program you wrote, end to end. Write it from workflow plan contract (a v3 program: steps, phases, gates and loops) and check it with workflow plan validate; goal validates it again (exit 2 with the issues and nothing launched when invalid), launches it detached and prints its short id. Steps start when their dependencies finish, up to --concurrency at once, in one shared folder (file lists guide scheduling; exact-file enforcement and worktree copies need --isolation). A step passes by facts: a clean exit, its deliverable produced, its evidence passed and its answer matching its schema. A failed step gets one automatic retry, then comes back to you; a usage limit comes back at once. A gate stops the steps behind it until you run workflow continue; a loop repeats its steps until its condition holds, and waits for you like a gate when its rounds run out. A dry loop (until: {"step": "review", "dry": 1}) stops after a review round that finds nothing new: each later review is told what is already known, the loop's other steps run only when there is something to fix, and a review with areas runs one reviewer per area each round (at most 3 unless areasPerRound says more; more areas take turns). Add steps at any time with workflow add. When nothing more can run the run finishes, and its result hands back every unfinished step and unread steering with your options; a completed v3 run hands nothing back.
A bullswarm.workflow.program.v2 is refused before any run folder exists (exit 2): bullswarm.workflow.program.v2 is no longer accepted for a new run; write a program.v3 (bullswarm workflow plan contract) and check it (bullswarm workflow plan validate --program <file.json>). Without a program the command refuses the same way and prints the next commands; to survey first, make the survey your first step.
The flags 0.38.0 removed (--scout, --no-scout, --orchestrator, --orchestrator-model, --orchestrator-strict, --strict-orchestrator, --suggested-plan, --planner-reasoning) exit 2 with one sentence, for example --scout was removed in 0.38.0: make the survey the first step of your program and have the steps that need it depend on it. The planner flags name their replacement: write the program yourself, or add a step whose answer is a list of steps and append them with workflow add <runId> --from-answer <step>.
Launches independently by default. --resume <shortId|runId> resumes a v3 run and is mutually exclusive with new goal text; a run an earlier Bullswarm started is view-only and exits 2 before dispatch.
A duplicate launch is refused before anything is validated or started: when an ongoing run in the same --cwd already has this goal text, the command exits 2 with that run's shortId, its age, and the command to watch it. --json prints {"error":"duplicate-goal","shortId":…,"runId":…,"startedAt":…,"next":{"watch":…,"again":…}}. Pass --again to start the second copy anyway.
# Caller-planned launch that follows progress until terminal.
bullswarm workflow goal "1. Fix src/parser.js. 2. Update docs." --cwd . --program plan.json --watch| Flag | Meaning | Default |
|---|---|---|
--cwd <dir> | working directory the goal executes in | current directory |
--isolation | opt into per-worker worktrees and strict exact-file ownership checks; saved runs retain their original workspace policy on resume | off (shared workspace, advisory territories) |
--watch | immediately follow low-noise progress until terminal; only valid for a new human-readable independent launch — cannot combine with --detach, --foreground, --json, --resume, or --request | off |
--foreground | keep execution attached to this terminal instead of detaching | off (detaches into a background process) |
--json | print the launch/report document as JSON; advisories are in it as advisories (the detached launch document always, a foreground result when there are any) and are not printed as text | human-readable launch instructions, with each advisory once on stderr |
--program <file.json> | your program: a bare bullswarm.workflow.program.v3 document, or one inside a bullswarm.workflow.planner-response.v2 envelope; a bullswarm.workflow.program.v2 is refused; validated before anything launches (exit 2 with the issues and nothing launched when invalid) | required |
--summary <text> | one-line summary recorded for a bare --program document | derived from the action purposes |
--worker-pool <pool|auto> | pin every dispatch, work and evidence steps alike, to one pool | auto (normal routing) |
--worker-model <model|auto> | pin the exact model for every dispatch; only pools that can guarantee it remain eligible | auto |
--worker-reasoning <level> | run-wide reasoning depth for every dispatch: low|medium|high|xhigh|max, or default; a per-action reasoning field outranks this; clamped to the picked connector | the configured strategy level, else the connector default |
--max-agents <n> | soft planning target for total work, evidence, and correction dispatches; essential work may exceed it | 30 |
--max-expansion-rounds <n> | legacy planning target retained for compatibility; new programs do not generate gap rounds | 2 |
--max-actions <n> | soft planning target for total steps, added steps included; essential steps may exceed it | 100 |
--concurrency <n> | max parallel dispatches; dependency-ready file-disjoint actions run concurrently up to this cap | 4 |
--retry-attempts <0..3> | automatic retries per step before it comes back to you; a process failure retries elsewhere, a gate failure retries on the same pool with its failure attached | 1 |
--resume <shortId|runId> | resume a v3 run; a run an earlier Bullswarm started is view-only and exits 2 before dispatch | starts a new goal |
--detach | rarely needed — explicitly requests the default independent-launch behavior; cannot combine with --watch | the default launch already detaches |
--again | start another copy even when an ongoing run already has the same goal text in the same --cwd; only a new launch is checked, never --resume or the internal --request relaunch | off (a duplicate of an ongoing goal is refused) |
Workers keep their edits even when their action fails. Failed dependencies skip downstream actions and independent branches finish. A resumed v3 run keeps its original completion and isolation policy.
plan
You write the program. contract prints the v3 format before any run exists; validate checks a program without launching. Launch it with workflow goal --program and extend the run with workflow add.
# Print the contract, then dry-run a program, then launch with the same goal text.
bullswarm workflow plan contract "1. Fix the parser. 2. Update the docs." --cwd . --json
bullswarm workflow plan validate "1. Fix the parser. 2. Update the docs." --cwd . --program plan.json --jsonplan show, plan submit, plan export and plan revise were removed in 0.38.0. For one release each exits 2 with one sentence: plan show/plan submit say runs no longer wait for a caller program and point to workflow add; plan export/plan revise say a v3 run's steps are never edited and point to workflow add and workflow step rerun. On a run an earlier Bullswarm started they print the view-only sentence instead.
plan contract
Print everything you need to write a v3 program (bullswarm.workflow.program.v3) for a goal: the step, gate and loop fields, the condition forms (a gate's when takes the field or evidence form; a loop's until takes those or the dry form, which is for a loop only), the evidence checks Bullswarm runs after a step, the rules the kernel keeps, two worked examples that validate (example: draft, critique, approve, publish; dryLoopExample: build, then review from three areas and fix until a review round finds nothing new), the findings answer a dry loop's review declares, and the validate and launch commands.
The goal is optional: bullswarm workflow plan contract alone prints the contract with goal: null and '<goal>' in the validate and launch lines. --v2 was removed in 0.38.0 and exits 2 with --v2 was removed in 0.38.0: bullswarm workflow plan contract prints the v3 format; bullswarm.workflow.program.v2 is no longer accepted for a new run.
# The v3 format; no goal needed.
bullswarm workflow plan contract
# The same contract with the goal and --cwd filled into its commands.
bullswarm workflow plan contract "1. Fix the parser. 2. Update the docs." --cwd . --json| Flag | Meaning | Default |
|---|---|---|
--cwd <dir> | working directory the goal will execute in | current directory |
--isolation | describe strict per-worker worktree isolation and retain the flag in launch guidance | off (shared workspace) |
--json | accepted for consistency; the contract is always printed as JSON | JSON |
--max-agents <n> | advisory dispatch target recorded in the contract settings | 30 |
--max-actions <n> | advisory action target recorded in the contract settings | 100 |
--max-expansion-rounds <n> | advisory gap-round target recorded in the contract settings | 2 |
--concurrency <n> | execution concurrency recorded in the contract settings | 4 |
--retry-attempts <0..3> | automatic retries per step recorded in the contract settings | 1 |
--worker-pool <pool|auto> | pin the worker pool the contract echoes back | auto |
--worker-model <model|auto> | pin the worker model the contract echoes back | auto |
--worker-reasoning <level> | run-wide worker thinking level the contract echoes back | the strategy setting for the action effort tier |
Read-only. Rejects the launch-only --program so the contract cannot silently describe a different run.
plan validate
Check a program you authored against the exact contract a launch would enforce, without creating a run. Exit 0 prints the accepted steps and the launch line; exit 2 prints every validator issue. A bullswarm.workflow.program.v2 is refused (exit 2), as a launch refuses it.
# Same validator as workflow goal --program; nothing is launched.
bullswarm workflow plan validate "1. Fix the parser. 2. Update the docs." --cwd . --program plan.json --json| Flag | Meaning | Default |
|---|---|---|
--program <file.json> | a bare bullswarm.workflow.program.v3 document, or one inside a planner response envelope | required |
--cwd <dir> | working directory the goal will execute in (must exist) | current directory |
--summary <text> | one-line summary recorded for a bare program document | derived from the action purposes |
--json | print the acceptance document ({action: "plan-valid", requirements, program, next}) or the refusal ({error: "program-invalid", issues, next}) | human summary |
--isolation | validate against strict per-worker worktree isolation | off (shared workspace) |
--worker-pool <pool|auto> | pin the worker pool the preview routes with | auto |
--worker-model <model|auto> | pin the worker model the preview routes with | auto |
--worker-reasoning <level> | run-wide worker thinking level for the preview | the strategy setting for the action effort tier |
--max-agents <n> | advisory dispatch target for the previewed run | 30 |
--max-actions <n> | advisory action target for the previewed run | 100 |
--max-expansion-rounds <n> | advisory gap-round target for the previewed run | 2 |
--concurrency <n> | execution concurrency for the previewed run | 4 |
--retry-attempts <0..3> | automatic retries per step for the previewed run | 1 |
Read-only. Exit 0 valid, 2 invalid, 1 bad cwd. Advisories (authoring advice such as suite-wider-than-files, never a refusal) print on stderr as advisory: <code> [<step>] — <message>; with --json they are only in the JSON document, as advisories.
add
Append a fragment {steps, gates?, loops?, blocks?} to a v3 run. Nothing the run has changes, except through blocks: {"<new step id>": ["<existing step id>", ...]} makes existing steps that have not started also wait for a step the fragment adds.
# Put a fix in front of the report, then accept the failed check it replaces.
bullswarm workflow add ab12cd --steps fix.json # fix.json: {"steps":[{"id":"fix",…}],"blocks":{"fix":["report"]}}
bullswarm workflow step accept ab12cd check --reason "fix replaces it"| Flag | Meaning | Default |
|---|---|---|
--steps <file.json> | the fragment to append | one of --steps or --from-answer is required |
--from-answer <step> | append the fragment that step's checked answer holds | unset |
--summary <text> | the revision summary | add <ids> |
--wait <seconds> | how long to wait for a running kernel to apply it before reporting it queued | 120 |
--json | machine-readable result, with waits ([{step, waitsFor}]) when blocks was used | human text |
The human output names each added dependency (waits step report now also waits for fix). A blocks key must be a step the fragment adds, not a gate or loop; a named step must be a step of the run that has not started (pending, ready or blocked, with no attempts) and is in no loop, and must not be one the new step waits for. Anything else is refused in fragment words and the run is unchanged (exit 2); a running kernel checks it again against its own state when it applies the addition.
pause
Pause a run: the kernel starts no new step. By default running agents finish first and their results are kept; with --now they are stopped and those steps run again after resume.
# Let running agents finish, then record the pause.
bullswarm workflow pause ab12cd| Flag | Meaning | Default |
|---|---|---|
--now | stop running agents instead of letting them finish, and wait up to 60s for the pause to take effect | off (running agents finish) |
--json | print {status: paused|pausing, mode, appliedBy, next} | one human line |
Writes pause.json in the run directory. Refuses a terminal run, and a view-only run (exit 2). --now stops agents mid-step; files they already changed stay in the workspace.
cancel
Stop a run. A running kernel is asked to stop cooperatively at its next safe checkpoint (active workers are never killed mid-write). A run with no kernel alive is finalized here and now, a v2 run left live included. workflow add can still reopen a cancelled v3 run.
# Request cooperative cancellation.
bullswarm workflow cancel ab12cd --json| Flag | Meaning | Default |
|---|---|---|
--json | print {action: "cancel", finalized, status, result|next} | one human line |
Idempotent: an already-terminal run reports alreadyFinished and exits 0.
resume
Resume a v3 run with its durable routing. On a finished run (completed, partial, or cancelled) resume is a retry: it reopens pending and cancelled steps, failed steps whose failure kind a retry fixes, and the steps blocked behind them. A run an earlier Bullswarm started is view-only: resume exits 2 with the view-only sentence and dispatches nothing. With nothing retryable it prints nothing to retry, starts nothing, and exits 1.
# Retry a partial run once the handback's retryAfter time has passed.
bullswarm workflow resume ab12cd --json| Flag | Meaning | Default |
|---|---|---|
--foreground | run the kernel attached to this terminal; combines with --json | off (detaches) |
--watch | detach, then follow until terminal or paused; cannot combine with --foreground or --json | off |
--json | print the relaunch document (or, with --foreground, the final result or pause document) | human launch instructions |
--program is rejected here (add steps with workflow add). failed-evidence and not-produced stay failed: use step rerun, step accept, or workflow add.
reindex
Backfill the append-only rollup history index from finished workflow run directories. Runs completed before 0.33.0, or whose finish-time rollup write failed, are included. Legacy authored-graph runs get a minimal record marked legacy; only unfinished runs are skipped. The dashboard reads this index instead of parsing every state.json on each refresh.
bullswarm workflow reindex [--json] [--force]| Flag | Meaning | Default |
|---|---|---|
--json | print { ok, indexPath, scanned, written, skipped, legacy, unfinished, present, failed, failures[] } | human summary line |
--force | rebuild rollups that already exist, then refresh their index entries | leave existing rollups in place |
The command writes <runDir>/rollup.json and ~/.bullswarm/history/runs.jsonl (or the equivalent $BULLSWARM_HOME paths), and is safe to run again: an already indexed run is counted as present rather than duplicated.
reprice
Recalculate terminal V2 attempt usage against the current dated rate cards and the provider transcript hooks. The default is a dry run; use --apply only after reviewing the rows it would change.
# Inspect every terminal attempt without writing run files or calibration.
bullswarm workflow reprice --json
bullswarm workflow reprice --all --json
# Reprice only attempts started on or after a date for one exact pool, then persist.
bullswarm workflow reprice --since 2026-09-18 --pool claude-code:acme --apply
# Run the same incremental pass the kernel and dashboard use automatically.
bullswarm workflow reprice --incremental| Flag | Meaning | Default |
|---|---|---|
--apply | atomically rewrite changed state.json files, regenerate result.json, and refresh rollup/index records | dry run; no run or calibration writes |
--all | scan all retained attempts; cannot be combined with --since | last 30 days |
--since <date> | include attempts whose startedAt is on or after this date, inclusively | all terminal attempts |
--pool <name> | exact pool-name filter | all pools |
--json | stream one JSON object per attempt and finish with a summary object | human rows plus an attempt-count and elapsed-time summary |
--incremental | apply only still-unknown or byte-estimated attempts not closed by the retry ledger | off; manual full reprice |
--transcript-home <dir> | read provider transcript stores beneath this home | current user home |
--trigger <name> | label an incremental pass in home status | manual |
--delay-ms <n> | delay an incremental pass; used by detached completion hooks | 0 |
Only terminal V2 runs are scanned; ongoing and legacy runs are skipped. The command indexes each provider store once from bounded file heads and tails, then reads only matched transcripts in full. A provider-reported attempt keeps its measured tokens and is repriced. Other attempts call the provider transcript hook: an exact or time-window match is transcript-summed, while an absent or ambiguous match becomes unknown with null token totals and API cost. Historical repricing reads calibration but never appends a sample.
The automatic incremental reconciler shares these matching and pricing primitives. It runs at quiet/final kernel boundaries and in detached children started after watch completion and after the dashboard's first paint. Its per-attempt ledger prevents repeated work; changed transcript stores or a new reader version reopen unresolved attempts. Provider-reported capture is never downgraded by a later transcript sum.
The human table columns are run, action, try, pool, match, tokens, api, and subscription; - represents null. With --json, the result is:
{
action: "reprice",
apply: boolean,
filters: { since: string|null, pool: string|null },
scannedRuns: number,
scannedAttempts: number,
matched: number,
ambiguous: number,
missing: number,
changedRuns: number,
rows: Array<{
runId, shortId, actionId, attemptId, ordinal, pool,
confidence, tokenSource, totalKnown, apiUsd,
subscriptionUsd, subscriptionBasis
}>,
failures: Array<{runId:string,error:string}>
}capabilities
Report the workflow engine, step roles, current routing policy, and live pool/model/meter state.
# Always JSON: pools, lanes, models, meters, routing constraints.
bullswarm workflow capabilities| Flag | Meaning | Default |
|---|---|---|
--json | accepted so agents can pass it uniformly; it selects nothing | output is always JSON, with or without the flag |
Read-only. Performs live pool discovery; nothing is written.
tui
Open the interactive full-screen workflow dashboard, or print a static/JSON snapshot for a non-interactive caller. Bare bullswarm workflow is equivalent on a TTY.
The interactive dashboard has eight pages: Home (today's tiles, active and recent workflows, and pool/model/project breakdown), Runs (the active block over the day-grouped history table, plus the commands), Run (plan, Live, Next, ETA, and budget), Step (one action's full panel), Budget (quota, measured worker-time share, labelled money, fit, and biggest workflows), Stats (Spending, Pool, Model, Project), Fleet (lane/provider rungs), and Help. The tab row is Home, Runs, Budget, Stats, Fleet — Run and Step read as Runs, and Help appears only while it is open. Every page has a sticky header and bottom nav; the current run or page is marked ●, and Step prepends [ back ]. The shared keys are h Home, r Runs, b Budget, s Stats, y the first day header of the Runs history table, f Fleet, ? Help, 1–9 to open a run, Tab for sub-tabs, Shift+Tab for workflows, p for the period, v to switch Spending between By Pool and By Model, Esc/← for back, arrows for one line, PgUp/PgDn for a screen, Home/End for top/bottom, ctrl+s to copy, and q to quit. Enter/→/l opens a selection. In 0.33.0, r no longer refreshes, b no longer moves out, Tab no longer cycles workflows, and h opens Home rather than Help; the view refreshes itself, Esc/left moves out, and Shift+Tab still cycles workflows. Click any tab, tile, bar, run, step, date, or control; the mouse wheel scrolls.
# One plain-text frame of the timeline, live, and next sections.
bullswarm workflow tui ab12cd --overview --width 90 --height 24| Flag | Meaning | Default |
|---|---|---|
--json | print a JSON snapshot instead of opening the interactive browser | opens the interactive browser on a TTY; without a TTY, a given runId prints one static text detail tree |
--all | print the JSON run list including historical (finished) runs; implies --json and cannot be combined with a runId | ongoing only |
--show <runId> | equivalent to passing <runId> positionally; forces the --json code path for that one run | none |
--cancel <runId> | request cooperative cancellation of that run instead of viewing it | none |
--overview | print one static frame of the run's overview panel as plain text with no escape codes; needs a runId | off |
--width <cols> | columns of the --overview frame, at least 40 | 100 |
--height <rows> | rows of the --overview frame, at least 12 | 30 |
Interactive mode and the snapshot views are read-only. --cancel writes cancelRequested=true. Inside the browser, q detaches without stopping the workflow; c requests cancellation with a confirmation prompt.
watch
Follow one run (v2 or v3) by printing one attach line, then one line per notable event (in a v3 run, its gate and loop lines too), staying silent while work is merely in progress. Plain workflow watch <runId> follows until the outcome (a terminal status, a v3 run parked waiting at a gate or loop, a caller-planner wait, or an operator pause). --until trouble also wakes at a gate waiting or a loop out of rounds. --next prints no attach line and returns after the first notable event, or immediately at a pause or terminal status. Every --next exit that leaves the run going prints a next: relaunch line carrying --after and --since. A legacy authored-graph run has nothing to follow: the watcher prints its bounded read-only summary once and exits 0.
# Print the next notable event and exit; relaunch until outcome reports pause or terminal.
bullswarm workflow watch ab12cd --next
bullswarm workflow watch ab12cd --next --after 42 --since 2026-09-08T10:15:00.000Z
# Standard background observer: silent until caller attention is needed.
bullswarm workflow watch ab12cd --until trouble| Flag | Meaning | Default |
|---|---|---|
--classic | force the older heartbeat-based watcher instead of event mode; V2 runs only; cannot combine with --next | off (event mode) |
--interval <seconds> | poll interval while following | 2 |
--heartbeat <seconds> | print a periodic heartbeat line when nothing has changed; opt-in for V2, must be >= 1 | off in event mode, 60 with --classic |
--stall-after <seconds> | report a running agent as silent after this many seconds without activity; must be >= 1 | 300 |
--next | print no attach line; exit after the first poll that printed a notable event, or immediately at a pause or terminal status | off (follows until terminal or pause) |
--until <outcome|trouble> | print only trouble and outcome lines, plus one line per v3 loop that finished (not a wake-up); trouble exits for a needs-you block (a usage limit or no free pool is one), a review needing you, a rejected revision, a gate waiting or a loop out of rounds, pause, stale step, or steering; blocked dependents are listed inside the needs-you block | off |
--after <sequence> | start from this durable event sequence instead of the current high-water mark | attach at the current high-water mark |
--since <iso-timestamp> | the previous watcher's exit time, so an already-reported stall does not fire again | report every agent silent past --stall-after at attach |
--jsonl | emit one JSON object per line instead of human text; every object carries sequence; the next: relaunch line is not printed | off (human text) |
--once | print a single current snapshot and exit immediately instead of following | off |
--verbose | include started, retry, and steering-delivered lines in event mode, and per-agent action detail with --classic | off (compact) |
Read-only. Exits 0 if the run reaches a delivered status (or on --once), 1 if it reaches a non-delivered terminal status. --next exits 0 while the run continues or when it delivered, 1 when it ended without delivering or the kernel is not running. --until trouble prints a cursor-bearing relaunch line and, for stale work, the exact workflow step restart command. --until cannot combine with --next, --once, --classic, or --heartbeat.
step rerun
Rerun a failed or finished step, carrying its last attempt's handoff. Add --avoid to exclude pools; the exclusions are saved in the step's route.
bullswarm workflow step rerun ab12cd write-report
bullswarm workflow step rerun ab12cd write-report --avoid pool-a,pool-b| Flag | Meaning | Default |
|---|---|---|
--avoid <pool,...> | repeat the flag or pass a comma-separated list; avoid these pools on this rerun and later reruns | no additional exclusions |
--wait <seconds> | wait for a live kernel to apply the revision | 120 |
--json | machine-readable result | human confirmation |
A pending step accepts --avoid to amend its route without running it. A running step needs step restart; a blocked step needs its failed dependency resolved first. failed-evidence and not-produced are not rerun by workflow resume. The command checks that some capable pool remains. On a view-only run (one an earlier Bullswarm started) it exits 2 with the view-only sentence and changes nothing; so do step accept and step restart.
step accept
Record your choice to accept a failed step or failing requirements from a check. A choice is recorded as evidence choice; it never makes a requirement verified.
bullswarm workflow step accept ab12cd write-report --reason "The report is sufficient"
bullswarm workflow step accept ab12cd verify --requirement requirement-2 --reason "Accept this gap"| Flag | Meaning | Default |
|---|---|---|
--reason <text> | required, one line, at most 500 characters; saved with the choice | required |
--requirement <id> | accept only this failing requirement checked by the step; may be repeated | all failing requirements checked by the step |
--wait <seconds> | wait for a live kernel to apply the revision | 120 |
--json | machine-readable result | human confirmation |
Accepting a failed step lets its dependents run. A later rerun clears the acceptance. An isolated writer whose work was never merged cannot be accepted.
step restart
Stop one currently running step and put it straight back in the queue with the stopped attempt's durable handoff. Nothing restarts automatically.
bullswarm workflow step restart ab12cd write-report
bullswarm workflow step restart ab12cd write-report --pool codex| Flag | Meaning | Default |
|---|---|---|
--pool <pool> | strictly pin the next attempt to this configured pool | normal routing |
--wait <seconds> | wait for the live kernel to stop and requeue the step | 60 |
--json | machine-readable result | human confirmation |
The command refuses finished runs, non-running steps, unknown pools, and dead kernels without writing an intent. Shared-workspace edits remain in place; isolated workspaces are retained for review and the new attempt starts fresh.
events
Replay one run's durable, ordered event log from a sequence cursor — the machine-oriented alternative to watch/tui.
# Return every event from the start of the log.
bullswarm workflow events ab12cd --after 0| Flag | Meaning | Default |
|---|---|---|
--after <sequence> | only return events with a sequence number greater than this cursor | 0 (all events) |
--json | accepted for consistency, but has no effect | output is always JSON |
Read-only.
steer
Queue free-text guidance for a running goal's next orchestration checkpoint, without interrupting the currently active step. In a caller-planned program run the guidance never halts work: watch prints it at once, and the caller acts on it, for example with workflow add. A run that finishes before anyone acts on it lists it as steering not acted on.
# Queue guidance. The active worker is unaffected.
bullswarm workflow steer ab12cd --message "Focus only on the auth module"| Flag | Meaning | Default |
|---|---|---|
--message <guidance> | the guidance text; if omitted, all words after <runId> are joined and used instead | required, in one of the two forms |
--json | machine-readable confirmation | human-readable confirmation line |
Refuses if the run is already terminal, and refuses a view-only run (one an earlier Bullswarm started) with the view-only sentence and exit 2.
action show
Print one action's full record — its ledger entry, every dispatch attempt, its saved output, and the events tied to it.
# Inspect one action and all of its attempts.
bullswarm workflow action show ab12cd act-3| Flag | Meaning | Default |
|---|---|---|
--json | accepted for consistency, but has no effect | output is always JSON |
Read-only. The action id comes from the run's action ledger (workflow runs show or the TUI).
The outer actionRecord, attempts, and action-scoped events fields retain the durable records. step is the compact, versioned Step display document:
{
"schemaVersion": 2,
"identity": { "actionId": "sample-step", "status": "succeeded" },
"selectedAttemptOrdinal": 2,
"selectedAttempt": { "sameAs": "attempts[1]" },
"attempts": [
{ "ordinal": 1, "activity": { "events": [], "turns": [] } },
{ "ordinal": 2, "activity": { "events": [], "turns": [] } }
],
"presentation": { "header": {}, "activity": {}, "result": {}, "task": {}, "cost": {} }
}The complete durable attempt records remain in the outer attempts; the attempts inside step carry selection facts and activity only. Each distinct attempt activity occurs once. activity.events is its canonical event array; turn eventIndices select from it. Filtered aliases such as visibleEvents, todayEvents, and visibleDetailEvents, and the dashboard's top-level model aliases, are not serialized. A { "sameAs": "…" } object is a JSON reference marker for an identical value. workflow task show emits the same step schema around a standalone task record.
runs
Search ongoing and historical workflow run instances, including read-only legacy rows, or drill into one with show / result / delete. bullswarm runs is a documented alias that prints the same help with bullswarm runs in the synopsis.
Time filters compare each run's initiation timestamp and accept ISO timestamps, local dates (YYYY-MM-DD), today/yesterday/tomorrow/now, or relative durations such as 30m, 24h, 7d, 2w. --since is inclusive and --until is exclusive; --from / --started-after and --to / --started-before are aliases.
# List every run started in the last week.
bullswarm workflow runs --all --since 7d| Flag | Meaning | Default |
|---|---|---|
--all | include both ongoing and historical runs | ongoing only |
--historical | only historical (finished) runs | ongoing only |
--name <goal> | filter by exact goal text (a legacy row matches on its recorded workflow name) | no filter |
--since <time> | lower bound on start time (inclusive) | no lower bound |
--until <time> | upper bound on start time (exclusive) | no upper bound |
--limit <n> | cap the number of results; must be a positive integer, and anything else exits 2 | no cap |
--json | machine-readable output | human-readable one-line-per-run summary |
list is the default when no subcommand is given and takes the same flags.
runs show
Show one run's durable state and status summary. On a legacy run (from before 0.27) it prints a bounded read-only summary built from the run's saved files; minutes or cost the files do not hold stay unknown. runs result does the same.
# Human-readable state and report for one run.
bullswarm workflow runs show ab12cd| Flag | Meaning | Default |
|---|---|---|
--json | print the full state/report as JSON | human-readable summary lines |
Read-only.
runs result
Print the stable caller envelope. This is the intended integration point for scripts and agents. Use --summary for the compact status-loop envelope; read the full envelope on failed or partial runs and before judging evidence. Field-by-field documentation is in Result envelope.
# Compact status-loop JSON, then the full envelope.
bullswarm workflow runs result ab12cd --json --summary
bullswarm workflow runs result ab12cd --json| Flag | Meaning | Default |
|---|---|---|
--json | print the full versioned result document as JSON | human-readable result summary |
--summary | print the compact JSON status-loop envelope; implies --json | full result envelope |
The full JSON usage object retains total, byPool, and bytes, and adds steps[actionId] plus totals. Each aggregate contains attempts, minutes, tokens, cacheRead, cacheWrite, apiUsd, apiKnownSubtotalUsd, subscriptionUsd, subscriptionKnownSubtotalUsd, measuredAttempts, pricedAttempts, subscriptionPricedAttempts, tokenSource, and subscriptionBasis. An action may carry the same aggregate as action.usage. The complete per-attempt v2 record is exposed by workflow action show; null means that a value is unknown, while the explicitly named subtotal fields hold partial sums.
Read-only.
runs delete
Permanently remove one run's directory (state, report, events, logs).
# Irreversible delete of a finished run directory.
bullswarm workflow runs delete ab12cd --yes| Flag | Meaning | Default |
|---|---|---|
--yes | required — approves the deletion | none; the command refuses without it |
--force | also delete an ongoing run | refuses to delete an ongoing run |
--json | machine-readable confirmation | human confirmation line |
Recursively deletes ~/.bullswarm/workflows/<runId>/.
runs
Alias for workflow runs. Every subcommand, flag, and default is the same; the synopsis prints bullswarm runs instead of bullswarm workflow runs.
# Top-level shorthand for the same run list.
bullswarm runs --all --since 7dversion
Print the installed bullswarm package version. bullswarm --version is an equivalent alias handled the same way by the command dispatcher.
# Print the package version from package.json.
bullswarm versionNo flags. Self-initializes state.json on first use, like every other non-help command.
update
Upgrade this installation of bullswarm to the latest version published on npm, in place. A global npm install is upgraded with npm install -g bullswarm@<latest> --prefix <its own prefix>. A global pnpm install lives in a per-version store directory (<pnpm home>/.pnpm/bullswarm@<version>/) that is not a prefix, so it is upgraded with pnpm add -g bullswarm@<latest> and verified through the link under the pnpm global node_modules. A source checkout (a clone, or a global install that is an npm link into one) is pulled with git pull --ff-only instead and is refused while it has local changes. The result is verified by re-reading package.json on disk, never by the package manager's exit code.
# Compare with the registry, then upgrade in place.
bullswarm update --check
bullswarm update| Flag | Meaning | Default |
|---|---|---|
--check | only compare the installed version with the latest published one; changes nothing | off (upgrades) |
--json | machine-readable result: install {kind: global|pnpm-global|checkout|unknown, root, prefix}, verifiedAt, before, latest, after, upToDate, updated, error, notes[] | human-readable lines |
install.root is where the running copy was found. pnpm leaves that per-version store directory behind on upgrade, so it is not "where bullswarm lives now" — read verifiedAt, the copy after was read from, for that.
Reads https://registry.npmjs.org/bullswarm/latest (8s timeout). Exit 0 = at the latest published version afterwards (or --check reported); exit 1 = registry, npm, pnpm or git refused, or the install shape is unknown. The running process keeps its old version; the next bullswarm command runs the new one.
Next steps
- Getting started — install, setup, and the first
run - Workflow program — the
plan.jsonworkflow goal --programexecutes - Result envelope — JSON
run --jsonandruns result --jsonreturn
