Docs

CLI reference — sirloop v3.0.0-alpha.2

Every command, option and default of the sirloop binary, generated from the released binary itself — the command tree exactly as shipped, never hand-maintained. Provenance: 3.0.0-alpha.2+gc12c43c8c-dirty (2026-09-17), released 2026-09-17. Other versions: the version index; the binary’s own help is always one flag away, sirloop <command> --help.

Global options

These ride every subcommand.

OptionValueDefaultDescription
--hermetic false

Benchmark-legal mode: for the life of this process sirloop opens no network of its own — no relay dial or attach, no depot, no peer dispatch, no introductions, no update fetch — starts no listener, and latches inbound dispatch shut as if `pause remote --hard` (in-process; nothing is written to pause.json). Receipts are stamped `hermetic`. NOTE THE BOUNDARY: the harness CLI sirloop drives (claude, codex, …) is a separate process and keeps its own network — sirloop guarantees only its own surface. Env twin: SIRLOOP_HERMETIC=1 (exported to children, so --detach inherits it)

--forbid-path GLOB (repeatable)

No-test-peeking guard: a glob a benchmark's gold tests live behind (repeatable; pass the task's FAIL_TO_PASS paths). If the verify command names one, or the run modifies one, the receipt is stamped `tainted: <reason>` — the run still finishes, and the bench exporter drops the row. File *reads* are not detectable this way and are not claimed. Env twin: SIRLOOP_FORBID_PATH (comma-separated)

sirloop id

Show this node's identity (label, public key, fingerprint)

Usage: sirloop id [OPTIONS]

sirloop rename

Rename a node — this one, or ask a paired peer to rename itself. Either way the renamed node tells its own paired peers, so the pins they hold follow the new name instead of stranding on the old one (labels are how verbs address a peer). A renamed daemon serves under its old in-memory label until it restarts; --restart asks the peer for the update hand-off so the new name serves immediately

Usage: sirloop rename [OPTIONS] <LABEL>

OptionValueDefaultDescription
label positional

The new label

Required.

--peer PEER

Ask this paired peer to rename itself, instead of renaming this node

--restart false

With --peer: the peer hands off to a replacement daemon so the new name serves immediately (interrupted daemon-owned jobs resume on its boot)

--addr ADDR

Override the peer's stored address (host:port)

sirloop introduce

Heal the relay mesh: re-run introductions between every pair of pinned peers that share a relay AND a named room, so a hub-and-spoke (pre-introductions) becomes the full mesh a fresh pairing now gets. Idempotent — an edge that already stands is re-pinned by key, which also refreshes the labels the two sides hold for each other. The daemon runs this same sweep on its own slow cadence by default

Usage: sirloop introduce [OPTIONS]

OptionValueDefaultDescription
--relay RELAY

Heal only rooms on this relay (host:port); default: every relay that appears among the pins

sirloop room

Group pins into named rooms (ADR 0019). Mesh management — introductions, heals, the daemon's periodic sweep — stays within a room, so one machine can sit in several isolated meshes on the same relay. Grouping is this node's own policy; the pairing boundary stays the only security boundary. With no subcommand: list rooms and members

Usage: sirloop room [OPTIONS] [COMMAND]

sirloop room set

Move a pinned peer into a named room ("default" moves it back to the open room). Local policy: it changes which mesh THIS node grows around that pin, not what the peer may do

Usage: sirloop room set [OPTIONS] <PEER> <ROOM>

OptionValueDefaultDescription
peer positional

Peer label (as `sirloop peers` prints it)

Required.

room positional

Room name (≤64 bytes, printable)

Required.

sirloop room heal

Heal one named room now instead of waiting for the daemon's sweep

Usage: sirloop room heal [OPTIONS] [ROOM]

OptionValueDefaultDescription
room positional

Room name; default: the default room

sirloop serve

Run the daemon: listen for verbs from paired peers

Usage: sirloop serve [OPTIONS]

OptionValueDefaultDescription
--bind BIND 127.0.0.1:4520

Bind address. Default is loopback-only

--lan false

Accept paired peers from other machines (shorthand for --bind 0.0.0.0:4520). Only paired keys get past the handshake

sirloop supervise

Keep the daemon alive across crashes (P1's restart supervisor): spawn `sirloop serve` on --bind and bring a fresh one back with bounded exponential backoff whenever it dies. `sirloop stop` stops both — the supervisor honours the stop latch at every decision and stands down instead of restarting. The daemon is its own detached process: killing (or Ctrl-C-ing) this supervisor leaves the daemon serving, exactly as unsupervised

Usage: sirloop supervise [OPTIONS]

OptionValueDefaultDescription
--bind BIND 127.0.0.1:4520

Bind address the supervised daemon serves. Default matches `serve`

sirloop connect

Attach to a hosted relay with a signed attach token — the whole onboarding a stranger needs (issue #358): validate and store the token, prove the relay takes this node's attach, then serve with the token riding every room park

Usage: sirloop connect [OPTIONS] <TOKEN>

OptionValueDefaultDescription
token positional

The attach token from sirloop.com (or `sirloop relay mint`)

Required.

sirloop pair

Pair with another node

Usage: sirloop pair [OPTIONS] [ADDR]

OptionValueDefaultDescription
addr positional

Address of the listening node (host:port). Omit with --listen or --relay

--listen false

Listen for an incoming pairing instead of initiating one

--bind BIND 0.0.0.0:4521

Bind address for --listen (LAN pairing)

--code CODE

The 6-digit code shown by the listening node

--relay RELAY

Rendezvous relay (host:port) — pair across networks: both sides dial the relay outbound, like a WhatsApp/Zoom call setup

--room ROOM

Room id printed by the relay-listening node

--relay-key RELAY_KEY

The relay's public key as printed by the listening node — pins the relay identity so a DNS-spoofed impostor fails the handshake

--serve-port SERVE_PORT 4520

Port this node's own daemon serves on — advertised to the peer during pairing so it knows where to reach us afterwards

sirloop relay

Run a rendezvous relay (deploy on any small VM with a public address; it forwards opaque Noise ciphertext and can read nothing)

Usage: sirloop relay [OPTIONS] [COMMAND]

OptionValueDefaultDescription
--bind BIND 0.0.0.0:8443

sirloop relay mint

Mint a signed attach token with $SIRLOOP_RELAY_TOKEN_KEY — how the operator mints free-tier tokens (and how tests mint any of them). Paid tiers are the website's checkout doing exactly this, with its own copy of the key

Usage: sirloop relay mint [OPTIONS] --relay <RELAY>

OptionValueDefaultDescription
--relay RELAY

The relay address the token opens, as clients dial it (host:port)

Required.

--cap CAP 3

Distinct rooms the fleet may park at once (the tier's cap)

--days DAYS 30

Days the token stays valid

--fleet FLEET

Fleet id (32 hex chars); default: a fresh random one

sirloop peers

List pinned peers, kick one out of the fleet, or disband it all

Usage: sirloop peers [OPTIONS]

OptionValueDefaultDescription
--unpair UNPAIR

Kick this peer out of the fleet: best-effort tell it to forget this node too, then drop its pin here regardless

--disband false

Disband the whole fleet: kick every paired peer, one by one

--caps false

Include each peer's last-known capabilities (from the cache)

--p2p PEER (repeatable)

Direct P2P for one pin: `--p2p <peer> on|off` (default off = relay-only, no IP disclosure; ADR 0020). Takes effect once the WebRTC transport is built in; the relay stays the fallback

sirloop caps

Show capabilities — this node's (probed + declared), or a peer's

Usage: sirloop caps [OPTIONS] [LABEL]

OptionValueDefaultDescription
label positional

Peer label to query live (omitted = this node)

--declare DECLARE (repeatable)

Declare a fact about this node: --declare azure=prod-access

--undeclare UNDECLARE (repeatable)

Remove a declared fact by key

--refresh false

Force a fresh probe instead of the cache

--verify-auth false

Prove harness auth with a real turn: harnesses with no `status` command of their own get ONE bounded hello run each ("Reply with the single word: ok"), cached in harness-auth.json until the next --verify-auth. Costs real spend — never part of a routine sweep

--addr ADDR

Override the peer's stored address (host:port)

sirloop gates

Verify-gate coverage: which nodes and landing points are judged by a deterministic gate, and which only ever report their own success

Usage: sirloop gates [OPTIONS]

OptionValueDefaultDescription
--from FROM

Read one paired peer's gate table instead of this node's

--all false

The whole fleet: this node plus every paired peer (an offline peer answers from its last-known manifest)

--strict false

Exit 1 when any listed landing point has no verify gate (a trivial one that cannot fail counts as none) — or a node could not be asked at all: a CI check on fleet honesty

--history false

Read the same table for trend: per landing point, the current green/red streak under the gate configured now, the last green, the last red, and the gate's fingerprint — folded from each node's own receipts, no separate history kept

--addr ADDR

Override the peer's stored address (host:port)

sirloop quota

Subscription & quota state per harness account (issue #404): the plan, the renewal timer, the usage windows each provider exposes — codex's from its own rollout logs, claude's from the vendor's usage endpoint — this node's read live, or a paired peer's from its manifest. Read-only; unknown is the honest answer for a cell the provider does not expose

Usage: sirloop quota [OPTIONS] [LABEL]

OptionValueDefaultDescription
label positional

Peer label to read instead (asked live; its manifest carries the same probe's rows)

--json false

Print the raw JSON rows instead of the table

--addr ADDR

Override the peer's stored address (host:port)

--live false

Dial the claude usage endpoint once before reading (local only): the ONLY code path that spends that endpoint's strictly-budgeted requests. Everything else — including a peer's rows — serves the 15-minute cache this writes

sirloop setup

Install a harness CLI bare-metal (npm/pip, no containers) and verify it

Usage: sirloop setup [OPTIONS] <FRAMEWORK>

OptionValueDefaultDescription
framework positional

The harness to install: claude | codex | opencode | qwen | zcode | gemini | copilot | cursor-agent | aider | crush

Required.

--as-entry false

After a successful install, also make it this node's entry harness

sirloop entry

Show or set this node's entry harness — the landing point for instructions from paired peers/controllers

Usage: sirloop entry [OPTIONS]

OptionValueDefaultDescription
--framework FRAMEWORK

Known framework: claude | codex | opencode | qwen | zcode | gemini | copilot | cursor-agent | aider | crush

--cmd CMD (repeatable)

Custom argv template instead (repeat per arg; {goal} lands the instruction): --cmd mytool --cmd --flag --cmd {goal}

--access ACCESS

What the harness may do here: full (default — write files, run commands; the verify gate is the safety) or ro (read-only analysis; a verify gate that needs edits cannot pass)

--cwd CWD

Working directory for the harness and verify command

--model MODEL

Model injected via the framework's native flag

--verify VERIFY

Verify command (local shell) — its exit code is the verdict

--timeout-secs TIMEOUT_SECS

Kill the harness past this many seconds

--stdin false

Deliver the goal on the harness's stdin instead of argv — required for npm/batch-shim CLIs on Windows (claude, etc.), which mangle multi-line argv arguments

--clear false

Remove the entry configuration

sirloop project

Manage this node's named projects — each is its own landing point (cwd, harness, verify gate) on the one shared daemon

Usage: sirloop project [OPTIONS] [NAME]

OptionValueDefaultDescription
name positional

Project name; omit to list all projects

--framework FRAMEWORK

Known framework: claude | codex | opencode | qwen | zcode | gemini | copilot | cursor-agent | aider | crush

--cmd CMD (repeatable)

Custom argv template (repeat per arg; {goal} lands the goal). Hyphen-leading values are taken literally, so a real harness invocation types as-is: --cmd claude --cmd -p --cmd {goal}

--access ACCESS

What the harness may do here: full (default) or ro (read-only)

--cwd CWD

Working directory for the harness and verify command

--model MODEL

Model injected via the framework's native flag

--verify VERIFY

Verify command (local shell) — its exit code is the verdict

--timeout-secs TIMEOUT_SECS

Kill the harness past this many seconds

--stdin false

Deliver the goal via stdin (needed for npm-shim CLIs on Windows)

--repo REPO

Git repo this project provisions from — makes it portable to any paired node via `sirloop provision`

--branch BRANCH

Branch to provision

--root ROOT

Set where provisioned projects are placed on this node (default C:\git on Windows, ~/git elsewhere)

--remove false

Remove this project

sirloop providers

Show or edit the model provider registry: named endpoints (anthropic-compat | openai-compat | omniroute) and the ordered `model_classes` that cascade over them. A task then asks for a model by requirement — `provider/model`, `class:<name>`, `omniroute:<route>`, or a bare model id — and the resolver binds the first reachable survivor into the harness's own model flag plus an environment overlay for that child alone. Nothing here is ever written into a harness's own config

Usage: sirloop providers [OPTIONS]

OptionValueDefaultDescription
--preset PRESET

Write a built-in endpoint profile (rows + a class) for a vendor with a public compatible API but no CLI of its own — `deepseek` adds its OpenAI- and Anthropic-dialect endpoints so installed harnesses can front DeepSeek models (--model class:deepseek)

--set SET

Add or replace a provider row (with --kind/--base-url/--auth-env)

--kind KIND

anthropic-compat | openai-compat | omniroute

--base-url BASE_URL

Endpoint base url (e.g. localhost:20128)

--auth-env AUTH_ENV

NAME of the environment variable holding this endpoint's key — never the key itself

--models MODELS

Comma-separated model ids: the declared list for --set, or the cascade order for --class

--hermetic false

Ask an omniroute row for hermetic traffic (memory injection and compression off)

--remove REMOVE

Remove a provider row

--class CLASS

Add or replace a model class (with --models a,b,c)

--remove-class REMOVE_CLASS

Remove a model class

--no-probe false

Skip the reachability probe when listing

--probe FRAMEWORK

Conformance-probe one harness: stand up a fake local endpoint, redirect the CLI at it, and prove the base-url mechanism this node's adapter table claims for it actually reaches the wire. Spawns the real CLI (no key, no spend); records the verdict in frameworks.json

--probe-timeout PROBE_TIMEOUT 120

Seconds the probed harness may run before it is killed

sirloop browser

Pull a newer sirloop binary from a paired peer (no compiling) and install it in place — restart `serve` afterwards. A different- platform peer falls back to its release channel: the sha comes over the authenticated channel, the bytes from the relay depot, verified. `--push` inverts the direction: the peer is told to run this same pull against this node's daemon, so a fleet member can be updated without a shell on it — the same rails apply on its side. `--push-channel` (optionally with `--all`) configures the peer's release channel remotely instead of moving a build. Drive a browser — locally, or on a paired peer over the authenticated channel. Remote sessions persist per caller (open, then act); local runs are one-shot. The answering node's rails apply everywhere: http/https only, a throwaway headless profile, no wire-supplied JavaScript

Usage: sirloop browser [OPTIONS] [PEER] <COMMAND>

OptionValueDefaultDescription
peer positional

Paired peer to drive; omit for this node (one-shot, local)

--addr ADDR

sirloop browser open

Open a URL (http/https only)

Usage: sirloop browser open [OPTIONS] <URL>

OptionValueDefaultDescription
url positional

Required.

sirloop browser snapshot

Where is the page now — url, title, text snippet

Usage: sirloop browser snapshot [OPTIONS]

OptionValueDefaultDescription
--url URL

Local one-shot: open this page first

sirloop browser shot

A PNG of the viewport (1024x768); --out writes the file

Usage: sirloop browser shot [OPTIONS]

OptionValueDefaultDescription
--out OUT
--url URL

Local one-shot: open this page first

sirloop browser click

Click the element a CSS selector matches

Usage: sirloop browser click [OPTIONS] <SELECTOR>

OptionValueDefaultDescription
selector positional

Required.

--url URL

Local one-shot: open this page first

sirloop browser type

Type text into the element a CSS selector matches

Usage: sirloop browser type [OPTIONS] <SELECTOR> <TEXT>

OptionValueDefaultDescription
selector positional

Required.

text positional

Required.

--url URL

Local one-shot: open this page first

sirloop browser scroll

Scroll the page (positive is down)

Usage: sirloop browser scroll [OPTIONS] <DY>

OptionValueDefaultDescription
dy positional

Required.

--url URL

Local one-shot: open this page first

sirloop browser close

End the session on the peer (kills its throwaway engine)

Usage: sirloop browser close [OPTIONS]

sirloop update

Usage: sirloop update [OPTIONS] [PEER]

OptionValueDefaultDescription
peer positional

Paired peer to pull from; omit to use the release channel

--force false

Install even if the peer's build is not newer

--channel CHANNEL

Set the release channel: the peer this node updates from when no peer is named — and, with --auto on, unprompted by the daemon

--auto AUTO

Let the daemon install newer channel builds by itself and hand off to them (on|off)

One of: on, off.

--every EVERY

Seconds between the daemon's unprompted checks (floor 300)

--show false

Print the release channel and the last unprompted check

--forget-channel false

Forget the release channel (the daemon stops checking)

--restart false

Restart onto the new build. With --push the peer performs the hand-off itself (the new image spawns as the replacement daemon, the old process exits, interrupted daemon-owned jobs resume on its boot); on a local pull the daemon running here is restarted

--push false

Push instead of pull: tell the peer to update itself from this node's daemon (the same rails apply on its side — platform match, newer build unless --force; it restarts `serve` on its own schedule, never mid-connection)

--relay RELAY

Relay hosting the depot (default: the peer's stored relay)

--addr ADDR

Override the peer's stored address (host:port)

--push-channel false

Configure the PEER's release channel remotely instead of pulling/pushing a build — the wire side of --channel, so a node can be pointed without a shell on it. Combined with --channel <label>, that label is who the peer should follow (must already be paired on the peer's side); with --channel omitted, the peer follows THIS node ("follow me"). --auto, --every and --forget-channel apply on the peer exactly as they would locally

--all false

With --push-channel, apply to every paired peer instead of the one named — drop the peer name when using this

sirloop outbox

The standing outbox: fleet commands addressed to an offline peer that STAND — parked durably and delivered by this node's daemon when the peer returns. Only verbs that mean the same thing whenever the machine comes back stand (an update push, a rename, a channel setting); a task (`run`/`ask`/`shell`) never parks — it fails to your face immediately so you reassign it to a machine that is on. With no flag: list what stands

Usage: sirloop outbox [OPTIONS]

OptionValueDefaultDescription
--drop DROP

Cancel one standing command by id (a unique prefix is enough)

--flush false

Try to deliver everything standing right now, ignoring backoff

sirloop ops

The ops ledger: control-plane instructions — update pulls and pushes, the daemon's unprompted channel checks, outbox deliveries — each recorded when it fires and settled with what actually happened. In-flight instructions print first; that is the question this ledger exists to answer. (Runs have their own audit surface: `sirloop results` and `sirloop trail`.)

Usage: sirloop ops [OPTIONS]

sirloop publish

Publish a sirloop build to the relay depot and record it in this node's release catalog, so different-platform peers can update from it. With no file, publishes this running binary

Usage: sirloop publish [OPTIONS] [FILE]

OptionValueDefaultDescription
file positional

Path to a (cross-)built sirloop binary; omit for self

--os OS

Target platform of the file (defaults to this machine's)

--arch ARCH
--epoch EPOCH

Build epoch of the file (unix secs). Must equal the SIRLOOP_BUILD_EPOCH the file was compiled with, or a peer that installs it sees the catalog as forever newer and reinstalls on every `update`. Default: now (only right for a build you just made without pinning the epoch)

--relay RELAY

Relay hosting the depot (default: first pinned relay)

sirloop docs-gen

Generate the CLI reference for the docs site: walk this binary's own command tree and write `sirloop-cli-reference.json` into --out. The release pipeline feeds that JSON to sirloop.com's renderer, so the published docs are the tree as shipped — the build stamp rides inside as provenance. Output is deterministic (no timestamps)

Usage: sirloop docs-gen [OPTIONS]

OptionValueDefaultDescription
--out OUT .

Directory to write sirloop-cli-reference.json into (created if missing)

sirloop provision

Materialize one of this node's projects on a paired peer: the peer clones/refreshes the repo under its own projects root and registers the project, ready for `run --project`

Usage: sirloop provision [OPTIONS] [PEER] [NAME]

OptionValueDefaultDescription
peer positional
name positional

Name of a local project that has a --repo configured

--addr ADDR

Override the peer's stored address (host:port)

--cloud CLOUD

A disposable cloud node (issue #364, provider v1 = SSH): ssh://user@host — you bring the instance, sirloop installs its own image (platform match, sha verified), pairs it over the relay, stamps its cost hint and starts the daemon

--relay RELAY

The rendezvous relay the cloud node pairs through (host:port) — the only road a cloud node has to this machine

--size SIZE small

The instance's size NAME, for cost attribution on every receipt it settles (declared, never measured)

--destroy false

Tear a cloud node down: pause → drain (zero lanes, empty outbox) → stop → unpair → remove sirloop from the instance. Any failing step leaves it paused and paired

sirloop orchestrate

Self-orchestrate a goal: loop harness→verify→feedback locally, cascade across installed harnesses, or route to the fleet when requirements (or a missing local entry) demand it

Usage: sirloop orchestrate [OPTIONS] [GOAL]...

Self-orchestrate a goal: loop harness→verify→feedback locally, cascade across installed harnesses, or route to the fleet when requirements (or a missing local entry) demand it.

EXIT CODES: 0 = settled ACCEPTED. 1 = settled REJECTED (the gate ran and the work failed — a real verdict, not an error). 2 = hard error: nothing settled, because the lane, the repo, the config or the harness stopped the run before it could produce a verdict. A caller scoring runs wants 0/1 as the score and 2 as "this trial did not happen".

WHERE THE WORK HAPPENS: when the entry/project cwd is inside a git repo, the run gets its own throwaway worktree lane, and an ACCEPTED run's lane is deleted with nothing merged back — the cwd never sees the edits (this is deliberate: it is what keeps two runs on one project from racing, and what makes `--deliver patch` the way to collect the diff). Pass --in-place to work in the cwd itself instead

OptionValueDefaultDescription
goal positional

The goal (free text)

--attempts ATTEMPTS

Total attempt budget for the primary harness (default 3, or the workflow's own when --workflow names one)

--workflow WORKFLOW

Run a saved workflow instead of a typed goal: its template is expanded with --param values and its harness/model/gate/project bindings fill in whatever this command line leaves unsaid (`sirloop workflow list`). The receipt records the workflow's name, file digest and the parameter values

--param NAME=VALUE (repeatable)

name=value for a placeholder in --workflow's template (repeatable)

--require REQUIRE (repeatable)

Capability requirements: docker | key=value | os:linux | device:android (also android-emulator | android-physical | ios-simulator | ios) (repeatable; routes to whichever node satisfies)

--peer PEER

Force execution on a specific peer

--scatter false

Fan the same goal out to every eligible node (self + peers with entry harnesses, filtered by --require) and report all verdicts

--project PROJECT

Named project to land on (here or on the chosen peer)

--model MODEL

Model requirement for this dispatch, overriding the project's: provider/model | class:<name> | omniroute:<route> | a bare model id passed to the harness untouched (see `sirloop providers`)

--require-model false

Refuse the run (exit 2, before attempt 1) if --model names a provider this node's registry does not have, instead of passing the string to the harness verbatim — which a CLI that does not recognize it answers by silently using its own default model. Implied by --hermetic, where a number attributed to a model that never ran is worse than no number at all

--in-place false

Run the harness in the entry/project cwd itself instead of this run's own throwaway git worktree lane, so accepted edits stay where you pointed sirloop. Still exclusive: a second run on the same directory is refused rather than raced

--addr ADDR

Override the peer's stored address (host:port)

--detach false

Start the loop in a background process and return immediately with its run id — close the terminal, collect the result later with `sirloop results <id>`

--kind KIND

Pin the task-deliverable kind the report contract asks the harness for: code | brainstorm | research | ops | generic, or patch (sirloop extracts the run's own diff — needs a git workspace). Unset lets the harness choose from the registry

--parent RUN_ID

Link this run as a child of <RUN_ID> in the run tree; defaults to $SIRLOOP_JOB when invoked from inside a harness run, so a driver's breakdown stays visible as a tree in `sirloop runs`

--repo REPO

Run in a workspace of this repository (git url or local path) instead of a project's tree: sirloop materializes it here, at --at, in this run's own lane. Clones are cached as one bare mirror per repo, so a batch of runs against one repo clones once

--at AT

The exact commit --repo is checked out at — the base a `patch` deliverable diffs against, and the base an evaluation harness replays that patch onto

--needs-approval false

Park this run at the door before attempt 1: nothing runs until `sirloop approve <id>` — here, from a paired peer, or the dashboard. Silence past the deadline (30 min; settings.json `approval_timeout_secs`) denies it, charging no attempt

sirloop notify

Show, set, or test the settle notification hook — a command of your own that sirloop fires (out of process, outbound only) whenever a run settles here. The run is described in the environment: SIRLOOP_RESULT_ID, SIRLOOP_VERDICT, SIRLOOP_GOAL, SIRLOOP_TARGET, SIRLOOP_ATTEMPTS, and SIRLOOP_PROJECT (present only when the run targeted a named project)

Usage: sirloop notify [OPTIONS]

OptionValueDefaultDescription
--cmd CMD (repeatable)

Command argv to fire on settle (repeat per arg): --cmd curl --cmd -s --cmd api.telegram.org/…

--test false

Fire the configured hook now with placeholder values

--clear false

Remove the hook

sirloop plugin

Out-of-process plugins (ADR 0022): programs this node's owner registers, the daemon spawns and supervises, and sirloop talks to over the child's stdin/stdout — a chat channel (Microsoft Teams first, in its own repository) that polls for messages and posts replies. What a message may DO is decided here, not in the plugin: allowlist by default, expiring pairing code, constrained verbs (/ask, /run, /status), never raw passthrough. No subcommand: list

Usage: sirloop plugin [OPTIONS] [COMMAND]

sirloop plugin list

List registered plugins and what the daemon last knew about them

Usage: sirloop plugin list [OPTIONS]

sirloop plugin add

Register a plugin directory (the one holding plugin.json)

Usage: sirloop plugin add [OPTIONS] <PATH>

OptionValueDefaultDescription
path positional

Path to the plugin's directory

Required.

--name NAME

Register under this name instead of the manifest's

--disabled false

Register but do not start it

sirloop plugin remove

Forget a plugin (its state under ~/.sirloop/plugins/<name>/ stays)

Usage: sirloop plugin remove [OPTIONS] <NAME>

OptionValueDefaultDescription
name positional

Required.

sirloop plugin enable

Let the daemon start it (takes effect on the daemon's next start)

Usage: sirloop plugin enable [OPTIONS] <NAME>

OptionValueDefaultDescription
name positional

Required.

sirloop plugin disable

Stop starting it; a running one is not restarted after it exits

Usage: sirloop plugin disable [OPTIONS] <NAME>

OptionValueDefaultDescription
name positional

Required.

sirloop plugin set

Host options (project=, framework=, model=) or the plugin's own config (key=value; JSON values are kept typed; an empty value clears the key)

Usage: sirloop plugin set [OPTIONS] <NAME> <PAIRS>...

OptionValueDefaultDescription
name positional

Required.

pairs positional

Required.

sirloop plugin pair

Mint a pairing code: a sender types `/pair <code>` in the channel within 15 minutes to be allowlisted

Usage: sirloop plugin pair [OPTIONS] <NAME>

OptionValueDefaultDescription
name positional

Required.

sirloop plugin allow

Allowlist a sender id directly, without a code

Usage: sirloop plugin allow [OPTIONS] <NAME> <SENDER>

OptionValueDefaultDescription
name positional

Required.

sender positional

The channel's own id for the sender (what the plugin reports)

Required.

--label LABEL

A display name to remember it by

sirloop plugin deny

Remove a sender from the allowlist

Usage: sirloop plugin deny [OPTIONS] <NAME> <SENDER>

OptionValueDefaultDescription
name positional

Required.

sender positional

Required.

sirloop plugin senders

List allowlisted senders

Usage: sirloop plugin senders [OPTIONS] <NAME>

OptionValueDefaultDescription
name positional

Required.

sirloop plugin run

Run one plugin in the foreground on this console — no daemon needed — until it exits or Ctrl-C

Usage: sirloop plugin run [OPTIONS] <NAME>

OptionValueDefaultDescription
name positional

Required.

sirloop plugin log

Show the tail of the plugin's stderr log

Usage: sirloop plugin log [OPTIONS] <NAME>

OptionValueDefaultDescription
name positional

Required.

--lines LINES 40

sirloop mcp

Serve this node and its paired fleet as MCP tools over stdin/stdout (newline-delimited JSON-RPC 2.0) to any MCP-capable agent — Claude Code, codex, opencode. Runs with the authority of this terminal's CLI. The read-only tools (status, peers, caps, gates, results, runs, lanes, schedules) are always on; tools that start, stop or redirect work exist only for the verbs named in --allow, and a call to one that was not named is refused by name. No port, no token: the pipe is the boundary. Serves until stdin closes

Usage: sirloop mcp [OPTIONS]

OptionValueDefaultDescription
--allow VERBS

Action verbs to grant this session, comma-separated: run | ask | cancel | swap (default: none — read-only)

sirloop remote

Open the browser seat: a temporary, token-guarded web surface for controlling this sirloop from a browser — the live session feed, a goal input, slash palette, results/trail views, and the browser verbs with inline screenshots. Opt-in and ephemeral: one random token per run (printed once as a URL, never written down), loopback by default, gone when the process dies. `--bind` beyond loopback is an explicit operator choice; the token is the only boundary there

Usage: sirloop remote [OPTIONS]

OptionValueDefaultDescription
--bind BIND

Bind address [default: 127.0.0.1:8791]

--token-from TOKEN_FROM

Read the seat token from this environment variable instead of generating one (secrets come from the environment, not argv)

sirloop dashboard

Open the local dashboard: a token-guarded web control room for this sirloop — fleet, projects, models, results, audit trail, node health, and a tiered surface from plain-language buttons (simple) down to the executable's raw verb set (RAW). First visit opens a tutorial wizard. Same security posture as `remote`: one random token per run, loopback by default, gone when the process dies

Usage: sirloop dashboard [OPTIONS]

OptionValueDefaultDescription
--bind BIND

Bind address for THIS run (overrides any saved default)

--default-bind HOST:PORT|loopback

Make a bind the persistent default so every start — including the login launcher — serves it without flags. `host:port`, or `loopback` to reset to 127.0.0.1:8792. Beyond-loopback defaults are said loudly on every start: the token is the only boundary

--token-from TOKEN_FROM

Read the token from this environment variable instead of the one this machine keeps (secrets come from the environment, not argv)

--url false

Print the URL this machine's dashboard serves — token and all — and exit. What to run when a page has gone stale, or when the login launcher started the dashboard and printed its URL to a stdout nobody sees. Add --open to open it

--new-token false

Mint a fresh token before serving. Every URL handed out so far stops working — for a link that leaked, or a shared screen

--open false

Open the default browser on the dashboard once it is listening

--startup STARTUP

Manage the login-time launcher: install | remove | status. `install` makes the dashboard start (and open a browser) when you log in; the launcher stores no token — the machine's kept token makes the URL the same one every login. The dashboard brings the mesh daemon up with it, so this is the whole node autostarting — also controllable from the page

--no-serve false

Do not start (or supervise) the mesh daemon from this dashboard — for a machine that runs `sirloop serve` its own way

sirloop run

Land an instruction on a paired peer's entry harness

Usage: sirloop run [OPTIONS] <LABEL> [GOAL]...

OptionValueDefaultDescription
label positional

Required.

goal positional

The instruction (free text)

--project PROJECT

Named project on the peer to land on

--addr ADDR

Override the stored address (host:port)

--kind KIND

Pin the task-deliverable kind the peer's report contract asks the harness for: code | brainstorm | research | ops | generic. Unset lets the harness choose

--framework FRAMEWORK

Override the peer's entry framework for this job: claude | codex | opencode | qwen | zcode. The peer refuses one it does not have installed; if the chosen (or configured) harness fails, the peer cascades to its other installed harnesses before settling REJECTED

--model MODEL

Model requirement for this job, overriding the peer entry's: provider/model | class:<name> | omniroute:<route> | a bare model id passed to the harness untouched. Resolved against the peer's own registry (its `sirloop providers`), and refused by it when the harness that would run could not carry the model at all

--parent RUN_ID

Link this job as a child of <RUN_ID> in the run tree; defaults to $SIRLOOP_JOB when invoked from inside a harness run

sirloop ask

Ask an ephemeral peer agent: spawn a fresh harness session on a paired peer (or here, with --local) for exactly one prompt, print the answer, and the agent is gone — no project, no verify gate, no queue, nothing durable. It thinks in a throwaway scratch directory, read-only by default (--full lets a scratch experiment write and run commands, under the same pairing-is-the-boundary trust as `run`). Built for borrowing judgement rather than landing work: brainstorming, a second opinion from another harness or model, or the same question fanned across several nodes at once — name comma-separated peers (or --all) and every answer comes back under its node's own header. Where `run` is goal → gate → durable receipt, `ask` is prompt → answer → gone

Usage: sirloop ask [OPTIONS] [LABEL] [PROMPT]...

OptionValueDefaultDescription
label positional

Peer label — or comma-separated labels to fan the same prompt across several nodes. Omit with --local or --all

prompt positional

The prompt (free text)

--all false

Ask every paired peer at once (the brainstorm sweep)

--local false

Ask this node itself — no peer, the agent spawns right here

--framework FRAMEWORK

Harness for the agent: claude | codex | opencode | qwen | zcode (or a frameworks.json row). The answering node vets it; unset uses its configured entry harness

--model MODEL

Model for the harness's native flag, verbatim (a literal id)

--full false

Let the agent write files and run commands in its scratch (default: read-only — an opinion needs no write access)

--timeout-secs TIMEOUT_SECS

Harness wall-clock seconds; the answering node caps it at its own ceiling (900)

--keep false

Keep the exchange in the answering node's receipts ledger — prompt as goal, answer as transcript, shown in `sirloop results` and the dashboard (as a self-report: no gate ran). Default: nothing durable anywhere

--brief false

Ask for a structured brief instead of prose: the agent ends with a marked JSON line and only that summary comes back (kind, summary, items with their critical line, refs). The long answer stays on the answering node — pair with --keep to keep it auditable there. Built for a caller that pays by the token

--reduce false

Fan out, then reduce: one local agent reads every answer and returns a single brief — the consensus, the real disagreements, a recommendation — instead of N transcripts. Needs at least two answers. Each node's harness/model/exit is still printed verbatim beneath it; the rollup never restates a verdict. Add --brief to compress the leaves on their own nodes too

--addr ADDR

Override the stored address (host:port) — single peer only

--parent RUN_ID

Link this ask as a child of <RUN_ID> in the run tree; defaults to $SIRLOOP_JOB when invoked from inside a harness run

sirloop shell

Run a command in a persistent shell session on a paired peer — the direct way to set a node up (clone a repo, register a project, install a tool) so the first sirloop can create what a node needs instead of only landing goals on projects that must already exist. A named session keeps its working directory across commands; open several names for several terminals. Paired-only, like every verb

Usage: sirloop shell [OPTIONS] <LABEL> [COMMAND]...

OptionValueDefaultDescription
label positional

Required.

command positional

The command line to run (free text). Omit to just open the session at --cwd, or with --list / --close

--shell SHELL default

Named session on the peer — each keeps its own cwd. Reuse a name to send another command to the same terminal

--cwd CWD

Working directory to open the session in (its starting cwd)

--list false

List the shell sessions open on the peer for this node

--close false

Close the named session on the peer

--addr ADDR

Override the stored address (host:port)

sirloop term

Open an interactive terminal — the project's coding CLI in its own TUI, or the platform shell — here or on a paired peer. The twin of `sirloop run` for the times a human wants to drive the harness personally: same project, same resolved argv, same provider and endpoint environment a headless run of that entry would get, minus the goal. Nothing durable is created and no lane is taken. The terminal lives exactly as long as this command does — leave it and the program on the far node dies with the connection

Usage: sirloop term [OPTIONS]

OptionValueDefaultDescription
--peer PEER

Paired peer to open the terminal on. Omit for this machine

--project PROJECT

Project whose entry (harness, directory, model) to open. Omit for the node's default entry

--framework FRAMEWORK

Harness to open instead of the project's own — or `shell` for the platform shell in the project's directory

--addr ADDR

Override the stored address (host:port)

sirloop harness

Harness fleet lifecycle (issue #372): install, configure, verify — and open a harness's own login on a paired node. `--all` fans the action out to every paired peer

Usage: sirloop harness [OPTIONS] [PEER] <COMMAND>

OptionValueDefaultDescription
peer positional

Paired peer to act on (omit with --all)

--all false

Every paired peer, one line each

--addr ADDR

Override the stored address (host:port)

sirloop harness setup

Install a harness CLI on the node(s) with the bare-metal recipe (npm/pip), then re-probe it

Usage: sirloop harness setup [OPTIONS] <FRAMEWORK>

OptionValueDefaultDescription
framework positional

Harness to install (claude, codex, opencode, …)

Required.

sirloop harness config

Push adapter and provider rows to the node(s) — the same JSON a hand-edited file takes, vetted the same way; an invalid row refuses the whole push

Usage: sirloop harness config [OPTIONS] <FILE>

OptionValueDefaultDescription
file positional

Path to a JSON file: {"frameworks": […], "providers": {"name": …}}

Required.

sirloop harness login

Open the harness's own login in a terminal on the peer — the vendor's flow, driven by sirloop launching it (never implemented by sirloop); the credential stays in the CLI's own store there

Usage: sirloop harness login [OPTIONS] <FRAMEWORK>

OptionValueDefaultDescription
framework positional

Harness whose login to run

Required.

sirloop harness verify

Run the auth-state probe on the node(s): the harness's own status command where it has one, else the cached verdict — `--hello` spends one real turn

Usage: sirloop harness verify [OPTIONS] <FRAMEWORK>

OptionValueDefaultDescription
framework positional

Harness to verify

Required.

--hello false

Spend one real turn when the harness has no status command

sirloop harness status

The replication matrix: every harness this controller pushed to a peer, and where each cell stands — pushed, node-verified, or user-confirmed (the green check is BOTH sides, never a probe or a click alone)

Usage: sirloop harness status [OPTIONS]

sirloop harness confirm

The human half of the two-way gate: confirm, from THIS controller, that a peer's verified verdict is truly the access you meant

Usage: sirloop harness confirm [OPTIONS] <FRAMEWORK>

OptionValueDefaultDescription
framework positional

Harness whose verdict is confirmed

Required.

sirloop results

Read run receipts — the durable results of past runs. With no id, the most recent runs; with one, that run in full plus its transcript. Own node by default; `--from <peer>` collects a paired peer's instead; `--fleet` merges what every reachable node is running and queueing

Usage: sirloop results [OPTIONS] [ID]

OptionValueDefaultDescription
id positional

Receipt id (a prefix is enough)

--tail TAIL 80

Transcript lines to show with an id

--from FROM

Read a paired peer's results over the wire instead of this node's

--addr ADDR

Override the stored address (host:port) of --from

--fleet false

Every node at once: this node plus every paired peer, asked in parallel, merged into one table of what is running and queued and, below it, the newest settled receipts across the fleet (--limit per node). Unreachable peers are named as unreachable, never omitted

--limit LIMIT 0

How many settled receipts to list (per node with --fleet). 0 = the default page

--patch false

With an id: print that run's patch deliverable and nothing else, so it can be piped straight into `git apply` (or a benchmark's model_patch field). Reads the full patch file when the receipt holds only a truncated prefix

--json false

Print receipts as JSON instead of tables — one object with an id, the page as an array without. The receipt is the whole durable record (verdict, deliverable, segments, git evidence); the transcript stays in its own file, transcripts/<id>.log

sirloop feed

Follow the controller feed — what the fleet is doing, live. Every paired node's feed at once by default (one connection each), folded into a single view: runs in flight, gate verdicts, refusals, and attempts spent. Read-only; nothing here can change a node

Usage: sirloop feed [OPTIONS]

OptionValueDefaultDescription
--from FROM (repeatable)

Follow only these peers (repeatable). Default: every pin

--once false

Print one page and exit instead of following

--json false

Emit the fleet view as JSON — one object per change while following, one object with --once

--since SINCE 0

Resume from a cursor instead of asking for a fresh snapshot (only meaningful with --once and a single --from)

sirloop offer

Offer a task to another node: park a sealed manifest on its board for it — or anyone paired with it — to claim. The unit of handoff is a **pushed branch**: commit and push first, because nothing uncommitted ever crosses this wire

Usage: sirloop offer [OPTIONS] --project <PROJECT> --repo <REPO> --branch <BRANCH> <PEER> <GOAL>

OptionValueDefaultDescription
peer positional

The peer whose board to park it on

Required.

goal positional

What the claiming node should accomplish

Required.

--project PROJECT

Project name the claimant registers it under

Required.

--repo REPO

Clonable repo url

Required.

--branch BRANCH

The pushed branch holding the work

Required.

--attempts ATTEMPTS 3

Attempts the claimant may spend

--verify VERIFY

The gate that decides it; omitted, the claimant's own

--framework FRAMEWORK

Preferred harness — a preference the claimant may override

--ttl TTL 86400

Seconds the offer stays claimable

--addr ADDR

Override the stored address (host:port)

sirloop offers

Read an offer shelf — what work is parked on it, and who holds what

Usage: sirloop offers [OPTIONS] [PEER]

OptionValueDefaultDescription
peer positional

The peer whose board to read; omit for this node's own

--addr ADDR

Override the stored address (host:port)

sirloop claim

Claim an offer and land it here: the board grants it to exactly one node, then this node clones the manifest's branch under its own projects root and runs the goal through its own loop and its own gate. A landing that fails releases the claim

Usage: sirloop claim [OPTIONS] <PEER> <ID>

OptionValueDefaultDescription
peer positional

The peer whose board holds it

Required.

id positional

Offer id (16 hex characters, as `sirloop offers` prints it)

Required.

--no-adopt false

Take it without landing it — the claim is then a lease that expires unless an adopt is confirmed before it does

--addr ADDR

Override the stored address (host:port)

sirloop swap

Take a running task away from its driver and hand it to another harness: the current one is stopped where it stands, the next gets a deterministic handoff brief (goal, gate, git evidence, what has been tried), and the run keeps its attempt budget. With no id, lists the runs in flight here so you can pick one

Usage: sirloop swap [OPTIONS] [ID]

OptionValueDefaultDescription
id positional

Run id (a prefix is enough); omit to list what is running

--to TO

Harness to hand it to: claude | codex | opencode | qwen. Omitted = the next installed one in the cascade

--from FROM

Redirect a run executing on a paired peer instead of here: the request is parked on that node (the same file its own CLI writes) for the loop driving the run to honour. The peer vets the target against what is installed there, fail-closed

--addr ADDR

Override the stored address (host:port) of --from

sirloop cancel

Stop a live run: park a stop beside its intent for the loop driving it to honour at its next poll (the same file a peer's `cancel` verb writes). The gate still answers first — a run that goes green before the stop lands settles green — and a stopped run's receipt reads STOPPED, never `rejected`, because no gate ruled on it

Usage: sirloop cancel [OPTIONS] [ID]

OptionValueDefaultDescription
id positional

Run id (a prefix is enough); omit to list what is running

--reason REASON

Why, in one line — it rides into the receipt and the ledger

--from FROM

Stop a run executing on a paired peer instead of here

--addr ADDR

Override the stored address (host:port) of --from

sirloop approve

Let a run waiting at the door start: one dispatched with --needs-approval, or landing on a project that requires approval. The decision is a file parked beside the run's intent (like `cancel`), honoured by whichever loop is waiting at its next poll. With no id, lists the runs waiting here and the landing points whose policy parks every dispatch

Usage: sirloop approve [OPTIONS] [ID]

OptionValueDefaultDescription
id positional

Run id (a prefix is enough); omit to list what is waiting

--from FROM

Decide for a run waiting on a paired peer instead of here

--addr ADDR

Override the stored address (host:port) of --from

--require LANDING

Policy: make every dispatch landing on this project (or `default`, the default entry) wait at the door — whoever sent it: a peer, a plugin's /run, a schedule fire. Kept in settings.json (`approval_required`)

--release LANDING

Lift that policy from a landing point

sirloop deny

Refuse a run waiting at the door: it settles DENIED with no attempt charged — nothing ran and no gate ruled, so the receipt says neither `rejected` nor STOPPED

Usage: sirloop deny [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Run id (a prefix is enough)

Required.

--reason REASON

Why, in one line — it rides into the receipt and the ledger

--from FROM

Decide for a run waiting on a paired peer instead of here

--addr ADDR

Override the stored address (host:port) of --from

sirloop stop

Stop this node's daemon (`sirloop serve`) from any shell — or a paired peer's with --from (P1's missing verb). The daemon writes its disarm latch and exits: no launcher, dashboard auto-start or restart brings it back until `sirloop serve` (or the dashboard's Start button) arms it again. Detached runs are their own processes and keep going to their receipts; daemon-owned jobs resume on the next boot

Usage: sirloop stop [OPTIONS]

OptionValueDefaultDescription
--from FROM

Stop a paired peer's daemon instead of this node's

--addr ADDR

Override the stored address (host:port) of --from

--drain false

Let what is running there settle first; start nothing new meanwhile

sirloop lanes

Workspace lanes (M6.4): a landed job or local run gets its own git worktree rather than sharing a project's cwd, so two runs landed on the same project never race. With no flag, lists lanes still kept around for inspection (a rejected run's worktree, left exactly as its harness left it until reclaimed)

Usage: sirloop lanes [OPTIONS]

OptionValueDefaultDescription
--fleet false

The whole fleet's lane picture, one line per node (queue depth included — the number that says "add capacity": a cron'd `provision --cloud` reads exactly this, issue #364 ruling 2)

--reap false

Reclaim every kept lane now (normally left for a human to inspect first)

--slots SLOTS

How many runs this node may execute at once (M6.9). A run that lands with every slot busy is queued, not refused, and starts when one frees. Omit to see the cap in force

--default-slots false

Back to the built-in default cap (this machine's parallelism, bounded)

sirloop job

Poll a job started with `run`

Usage: sirloop job [OPTIONS] <LABEL> <ID>

OptionValueDefaultDescription
label positional

Required.

id positional

Required.

--addr ADDR

Override the stored address (host:port)

sirloop tui

Open the fleet TUI: rooms as tabs, nodes with live presence, capability details, and a command bar that dispatches to the selected node

Usage: sirloop tui [OPTIONS]

sirloop recover

Sweep for interrupted runs (crash/reboot) and resume them now. With `--reassign`/`--orphan`: act on a delegated job whose peer went MIA mid-work (`sirloop runs` lists them) — reassignment is always manual, never automatic, and both refuse a record whose peer still answers

Usage: sirloop recover [OPTIONS]

OptionValueDefaultDescription
--reassign ID

Close a MIA delegated job by re-dispatching its goal to the peer named with --peer (new job id; the old record is ledgered as reassigned in jobs.jsonl)

--peer PEER

Where --reassign lands the goal

--orphan ID

Give up on a MIA delegated job: drop its record and ledger it

sirloop runs

List every durable intent this node is holding in `pending/` — runs still in flight (same rows `sirloop swap` lists), plus any record that crashed into a corrupted, unparseable state (a torn write, or a reboot that zeroed a file's content while its length survived). `sweep` already ages the corrupt ones out on its own tick, but this is the only way to actually see them before that, and `--clear-stale` forces the same cleanup immediately, with no daemon required

Usage: sirloop runs [OPTIONS]

OptionValueDefaultDescription
--clear-stale false

Remove corrupt intent records old enough that no writer could still be touching them (same tolerance the heartbeat staleness check uses). Never touches a valid, resumable run — that stays `sirloop recover`'s job

--deadlock false

Sweep the whole fleet for cross-node wait cycles: node A holding a lane on a run it dispatched to B while B's work queues behind A. Prints cycles and stuck lanes, ledgers each finding to jobs.jsonl, and exits 1 iff a cycle exists (warnings alone exit 0). Detection only — it never breaks a cycle

sirloop ping

Round-trip a ping to a paired peer

Usage: sirloop ping [OPTIONS] <LABEL>

OptionValueDefaultDescription
label positional

Required.

--addr ADDR

Override the stored address (host:port)

sirloop status

Ask a paired peer for its status — hostname, user, free disk, version, uptime. With no label: this node plus every paired peer, one line each (the fleet validation sweep); exit 1 if any peer failed to answer

Usage: sirloop status [OPTIONS] [LABEL]

OptionValueDefaultDescription
label positional
--addr ADDR

Override the stored address (host:port)

sirloop bench

Benchmark artifacts: turn what this node recorded into what a leaderboard wants to read

Usage: sirloop bench [OPTIONS] <COMMAND>

sirloop bench swe

Run a SWE-bench-shaped dataset headlessly and write the predictions JSONL an evaluation harness eats

Usage: sirloop bench swe [OPTIONS] --dataset <DATASET> --out <OUT>

Run a SWE-bench-shaped dataset headlessly and write the predictions JSONL an evaluation harness eats.

Each instance is one orchestrated run in its own workspace-at-commit lane (`--repo`/`--at` from the dataset), delivering that run's own diff as `model_patch`. `model_name_or_path` is the model the receipt says actually bound — never the `--model` request.

Resumable: an instance already in the predictions or in `<out>.skipped.jsonl` is not re-run, and every settled instance is fsynced before the next starts. Tainted, patch-less and errored instances write NO prediction but ARE logged in the skip sidecar with the reason — a silent gap is how a benchmark gets mis-scored.

Local only: there is no `--scatter`, because a workspace-at-commit run is materialized on this node and cannot be routed to a peer that holds nothing to run in. Concurrency is this node's own lane cap.

OptionValueDefaultDescription
--dataset DATASET

Dataset: JSONL, one instance per line, with at least instance_id, repo, base_commit and problem_statement (unknown fields are ignored; the gold `patch`/`test_patch` are never read)

Required.

--out OUT

Predictions file to write/append (JSONL). Its siblings `<out>.skipped.jsonl` and `<out>.meta.json` are written beside it

Required.

--model MODEL

Model requirement for every instance: provider/model | class:<name> | omniroute:<route> | a bare model id

--limit LIMIT

Run at most N instances (after --instances filtering)

--instances INSTANCES (repeatable)

Run only these instance ids (comma-separated, repeatable). An id the dataset does not have is an error, not a shorter benchmark

--attempts ATTEMPTS 1

Attempt budget per instance

--jobs JOBS

Run at most N instances at a time. Clamped to this node's lane cap (`sirloop lanes --slots`) — never above it

sirloop bench export

Export a predictions file plus this node's own record into the swe-bench/experiments submission layout: `all_preds.jsonl`, `trajs/<instance>.md`, `logs/<instance>.log`, `metadata.yaml` and `README.md`

Usage: sirloop bench export [OPTIONS] --preds <PREDS> --dest <DEST>

Export a predictions file plus this node's own record into the swe-bench/experiments submission layout: `all_preds.jsonl`, `trajs/<instance>.md`, `logs/<instance>.log`, `metadata.yaml` and `README.md`.

Trajectories are assembled from receipts, the job ledger and transcripts, and every one says so in its own header: they are SUPERVISION-level (attempts, harness and routed model per attempt, driver swaps and their handoff briefs, gate outcomes, captured output), never the supervised CLI's own token stream — sirloop drives a separate process and does not intercept its reasoning.

Any instance whose receipt is stamped `tainted` (the no-test-peeking guard saw the run touch or grade itself on the benchmark's gold tests) is EXCLUDED from `all_preds.jsonl`, named on stderr. An instance with a prediction but no local receipt still exports, with a stub trajectory stating the gap — one hole never costs the run.

OptionValueDefaultDescription
--preds PREDS

The predictions file (`sirloop bench swe`'s output, or any JSONL with an `instance_id` per row)

Required.

--dest DEST

Where to write the submission directory (created if absent)

Required.

--meta META

The run-metadata sidecar. Default: `<preds>.meta.json` beside the predictions file, if it is there

--model-name MODEL_NAME

The submission's name in `metadata.yaml`/`README.md`. Wins over the sidecar — this is what makes the exporter usable standalone on a hand-built predictions file

--tail-lines TAIL_LINES 400

Transcript lines each trajectory carries (`logs/` gets the whole thing regardless)

sirloop doctor

Diagnose this node: state files parse, identity loads, relays answer, projects can run. One line per check; exit 1 if any failed. --relay additionally proves this node can ATTACH to its hosted relay (entitlement, not just reachability)

Usage: sirloop doctor [OPTIONS]

OptionValueDefaultDescription
--relay false

sirloop proxy

Show or set the outbound proxy relay traffic tunnels through (HTTP CONNECT — the path git and curl use on corporate networks that allow no direct outbound TCP). The setting lives with the node in network.json, so doctor, the dashboard and the daemon all dial the same way; SIRLOOP_PROXY in the environment still outranks it for a one-off. `--set direct` pins "no proxy" even when the shell exports HTTPS_PROXY. `--test` dials every pinned relay through a candidate (or the current route) without saving it

Usage: sirloop proxy [OPTIONS]

OptionValueDefaultDescription
--set SET

host:port or host:port, or "direct"

--clear false

Forget the setting — back to the environment

--test TEST

Dial every pinned relay through this candidate (or, with no value, the route in effect) and report — nothing is saved

sirloop introductions

Show or set whether this node accepts introductions (ADR 0018): the one pairing path with no ceremony at this keyboard — a peer this node already trusts hands it a one-time secret and it pairs with a third machine on its own, which is how a room self-meshes. `off` refuses inbound introductions in both roles (the refusal is ledgered naming the sender's pin, and the introducer sees that edge decline, never the pairing fail), restoring ADR 0017's "a compromised node cannot enroll new nodes" on this node. Default `on`. With no argument (or `show`), prints the current setting

Usage: sirloop introductions [OPTIONS] [on|off|show]

OptionValueDefaultDescription
state positional

`on`, `off`, or `show` (the default)

sirloop pause

Stop this node taking work, without killing it (ADR 0017 D7). `remote` refuses every inbound work verb from paired peers — run, shell, provision, update, browser, swap — each refusal ledgered naming the pause, while status, caps and results keep answering, so the fleet sees this node as *paused*, not *dead*; local runs, schedules and the recovery sweep continue. `all` additionally freezes new local starts and the queue pump — queued work stays queued and running sessions finish, because a pause never kills work. The latch lives in pause.json and survives a restart. With no argument (or `show`), prints the current state

Usage: sirloop pause [OPTIONS] [remote|all|show]

OptionValueDefaultDescription
scope positional

`remote`, `all`, or `show` (the default)

--hard false

Make the latch local-only: a paired peer's `resume` is refused too, and lifting it takes `sirloop resume --hard-clear` at this keyboard. Use when the machine must stay yours

--drain DRAIN

Drain to a stop: with `all`, refuse new starts and let what is running settle, bounded by this many seconds (0 uses the default window). `sirloop pause show` reports when the node has drained and is safe to stop

sirloop resume

Lift this node's pause and take work again. Also reachable over the wire from a paired peer — the one work verb a paused node still accepts, so pausing a node remotely is not a one-way door — unless the latch was set `--hard`, which lifts only here

Usage: sirloop resume [OPTIONS]

OptionValueDefaultDescription
--hard-clear false

Clear a `--hard` (local-only) latch too. Without it, a hard latch is left in force and the command says so

sirloop schedule

Cron-style scheduled fleet runs: a saved goal, a five-field expression (or @hourly/@daily/@weekly/@monthly) saying when it fires, and a target — this node or a paired peer. `sirloop serve` fires them; every surface that manages a node (this CLI, the dashboard, the phone app) reads and edits the same rows. With no subcommand: list

Usage: sirloop schedule [OPTIONS] [COMMAND]

sirloop schedule add

Save a schedule. The expression is parsed and gated NOW — a spec that can never match a real date is refused here, never stored as a schedule that would silently never run

Usage: sirloop schedule add [OPTIONS] --id <ID> --spec <SPEC>

OptionValueDefaultDescription
--id ID

Schedule id: [a-z0-9_-], 1..40 chars

Required.

--spec SPEC

When it fires: five fields "m h dom mon dow" or @hourly, @daily, @weekly, @monthly — in this machine's local time

Required.

--goal GOAL

What the fired run is asked to do (or --workflow)

--workflow WORKFLOW

Fire a saved workflow instead of a literal goal: the REFERENCE is stored, so editing the workflow file changes future fires. It must exist and expand with --param values now, or the row is refused

--param NAME=VALUE (repeatable)

name=value for a placeholder in --workflow's template (repeatable)

--target TARGET

Fire at this paired peer instead of this node (its label)

--project PROJECT

Named project on the target the goal runs in (None = its default)

--model MODEL

Model (or routing requirement, e.g. class:frontier) the fired run is pinned to

--grace-minutes GRACE_MINUTES 60

How late (minutes) a missed occurrence may be and still fire when the machine wakes

--disabled false

Save it switched off — it never fires until `enable`

sirloop schedule list

Every schedule on this node, with its next fire time and last outcome

Usage: sirloop schedule list [OPTIONS]

sirloop schedule enable

Switch one back on. Enabling is a fresh start: it does not fire for occurrences that passed while it was off

Usage: sirloop schedule enable [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Required.

sirloop schedule disable

Switch one off. Its cursor stands still; nothing is deleted

Usage: sirloop schedule disable [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Required.

sirloop schedule run-now

Fire one right now, ignoring its expression — a manual run through the same orchestrate path a foreground goal takes (so the exit code is the run's own verdict: 0 accepted, 1 rejected, 2 nothing ran). Does not move the cursor: a manual run never cancels the next occurrence

Usage: sirloop schedule run-now [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Required.

sirloop schedule remove

Delete one schedule by id (its ledger lines stay)

Usage: sirloop schedule remove [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Required.

sirloop schedule next

Show the next few fire times for an expression — or for a saved schedule's id — so a spec can be checked before it is trusted

Usage: sirloop schedule next [OPTIONS] <SPEC_OR_ID>

OptionValueDefaultDescription
spec_or_id positional

An expression ("0 9 * * 1-5") or a saved schedule's id

Required.

sirloop schedule log

What happened to the schedules, in order: every fire and skip (the ledger) interleaved with every config change and who made it (the audit trail) — one schedule's when an id is given

Usage: sirloop schedule log [OPTIONS] [ID]

OptionValueDefaultDescription
id positional

A saved schedule's id (all schedules when omitted)

--lines LINES 20

How many of the newest lines to print

sirloop github

The GitHub issue trigger: watched repositories whose labelled open issues become verify-gated runs here or at a paired peer, the verdict posted back as a comment. `sirloop serve` polls them (`gh` on PATH, or curl + SIRLOOP_GITHUB_TOKEN); the first poll baselines and fires nothing; an issue is dispatched once, ever. With no subcommand: list

Usage: sirloop github [OPTIONS] [COMMAND]

sirloop github add

Watch a repository. The first poll baselines and fires nothing (unless --backlog); after that a labelled issue updated later is dispatched once

Usage: sirloop github add [OPTIONS] <REPO>

OptionValueDefaultDescription
repo positional

owner/name

Required.

--label LABEL

Only issues carrying this label are work (default: every open issue — think twice)

--project PROJECT

Named project on the target the run lands in

--target TARGET

Dispatch at this paired peer instead of this node (its label)

--workflow WORKFLOW

Expand a saved workflow instead of the default goal; the template receives --param values plus the issue params it declares (repo, number, title, body, url)

--param NAME=VALUE (repeatable)

name=value for a placeholder in --workflow's template (repeatable)

--model MODEL

Model (or routing requirement, e.g. class:frontier) the run is pinned to

--backlog false

Process the issues that are already open at the first poll

--no-comment false

Do not post the pickup and the verdict back to the issue

--max-in-flight MAX_IN_FLIGHT 1

At most this many unsettled runs per repository at once

--disabled false

Save it switched off — nothing is polled until `enable`

--no-board false

Do not open a board thread per dispatched issue (default: each dispatch opens one — `sirloop board`)

--mirror MIRROR qa

What the thread mirrors to the issue as comments: `qa` (the default — questions, answers and the verdict) or `all` (every agent post too: status, links, text)

sirloop github list

Every watched repository with its poll state

Usage: sirloop github list [OPTIONS]

sirloop github enable

Switch one back on

Usage: sirloop github enable [OPTIONS] <REPO>

OptionValueDefaultDescription
repo positional

Required.

sirloop github disable

Switch one off (its state stays: nothing is re-dispatched later)

Usage: sirloop github disable [OPTIONS] <REPO>

OptionValueDefaultDescription
repo positional

Required.

sirloop github remove

Stop watching one repository (state and ledger lines stay)

Usage: sirloop github remove [OPTIONS] <REPO>

OptionValueDefaultDescription
repo positional

Required.

sirloop github status

The poll state and the recent ledger lines, for one repo or all

Usage: sirloop github status [OPTIONS] [REPO]

OptionValueDefaultDescription
repo positional
--lines LINES 20

How many ledger lines

sirloop github poll

One poll now, from this console: dispatches via `orchestrate --detach` into this home. The daemon's tick is the same pass

Usage: sirloop github poll [OPTIONS]

sirloop github run-now

Dispatch one issue by hand, ignoring the baseline and whether it was handled before — foreground, through the same orchestrate path a goal takes (exit 0 accepted, 1 rejected, 2 nothing ran)

Usage: sirloop github run-now [OPTIONS] <REPO> <NUMBER>

OptionValueDefaultDescription
repo positional

Required.

number positional

Required.

sirloop github reset

Forget one issue so the next poll may dispatch it again

Usage: sirloop github reset [OPTIONS] <REPO> <NUMBER>

OptionValueDefaultDescription
repo positional

Required.

number positional

Required.

sirloop board

The board (ADR 0024): one thread per subject, the repositories it concerns, the agents on it and every post — progress, questions, verdicts, replies — in one place. A watched GitHub issue opens a thread per dispatch; a run on a thread posts with `board post` / `ask` / `link` (it knows its thread from SIRLOOP_BOARD); a person replies with `board reply`. With no subcommand: list

Usage: sirloop board [OPTIONS] [COMMAND]

sirloop board list

Every thread here, phpBB-style: repository › subject · state · posts · origin. `--all` sweeps the paired fleet (a dark home is listed from the replica cache, marked stale); `--peer` one peer

Usage: sirloop board list [OPTIONS]

OptionValueDefaultDescription
--all false
--peer PEER

sirloop board open

Open a thread by hand

Usage: sirloop board open [OPTIONS] <SUBJECT>

OptionValueDefaultDescription
subject positional

The subject (one per thread)

Required.

--repo REPO (repeatable)

owner/name it concerns (repeatable; the first is where it files)

sirloop board show

The thread: header, members, then the posts — newest first (the latest reply on top); `--oldest` reads it as a story. The summary is inline; a post's detail collapses to one line unless `--full`. `--peer` reads it from the peer that holds it (the replica cache when the peer is dark, marked stale)

Usage: sirloop board show [OPTIONS] [ID]

OptionValueDefaultDescription
id positional

Thread id or prefix; inside a run: defaults to its own thread

--peer PEER
--since SINCE 0

Only posts after this seq

--lines LINES 50

At most this many posts (the newest)

--oldest false

Oldest post first — the thread as a story

--full false

Expand every post's detail inline

sirloop board post

Post on the thread. Inside a run: as its member, on its thread. Outside: as a person, --thread required (a `text` post — see `reply` for an answer that resumes the agent). The text is what a person reads; --detail is the full technical record an agent resuming digests; --attach pins links to the post

Usage: sirloop board post [OPTIONS] <TEXT>

OptionValueDefaultDescription
text positional

Required.

--kind KIND text

text | status | link (a run's kinds; a person posts text)

--thread THREAD
--detail DETAIL

The full technical record under the summary: commands, output, diffs — collapsed for a person, whole in a digest

--attach ATTACH (repeatable)

A link to pin to the post (repeatable): a GitHub pull/commit/issue URL, any URL, or a run id

sirloop board ask

Ask a person something and let the thread wait (a run's verb)

Usage: sirloop board ask [OPTIONS] <QUESTION>

OptionValueDefaultDescription
question positional

Required.

--detail DETAIL

The technical record under the question, for whoever resumes

--attach ATTACH (repeatable)

A link to pin to the question (repeatable)

Link a PR or a commit (a run's verb)

Usage: sirloop board link [OPTIONS] <URL>

OptionValueDefaultDescription
url positional

Required.

--detail DETAIL

What the link is about, for whoever resumes

sirloop board reply

Reply as a person: an `answer` — from WAITING, PARKED or REVIEW it asks the board to bring the agent back

Usage: sirloop board reply [OPTIONS] <ID> <TEXT>

OptionValueDefaultDescription
id positional

Required.

text positional

Required.

--to TO

The post seq this answers

--detail DETAIL

The technical record under the answer (rarely needed from a person; it is the agent-facing half)

--attach ATTACH (repeatable)

A link to pin to the reply (repeatable)

sirloop board dispatch

Put a harness on this thread and queue its first real run: binds the member and queues it in one step, for the daemon's board sweep to dispatch through the same gated path a watched issue takes — the project's own verify command decides the verdict, which settles back here. Free: it does not spend the thread's automatic resume budget. Serial per thread and fail-closed — a WAITING, CLOSED or LOCKED thread, a thread with a run already queued, or a framework already on the thread is refused by name, never overwritten

Usage: sirloop board dispatch [OPTIONS] --project <PROJECT> --framework <FRAMEWORK> --goal <GOAL> <ID>

OptionValueDefaultDescription
id positional

Thread id or prefix

Required.

--project PROJECT

The named project the run happens in (`sirloop project` lists them)

Required.

--framework FRAMEWORK

The harness that drives it: an installed framework (claude, codex, zcode, …) or `entry` for whatever the project is already configured with

Required.

--model MODEL

Model (or routing requirement, e.g. class:frontier) to pin

--goal GOAL

What the member is asked to do. Kept on the member, so every later resume is briefed with the thread above *this* goal

Required.

--peer PEER

Not supported: a dispatch lands on the node whose board this is

sirloop board resume

Bring a member back by hand (free — it does not spend the thread's automatic budget): queued for the daemon's board sweep, which dispatches it with the thread digest as its brief

Usage: sirloop board resume [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Required.

--member MEMBER

Which member (default: the one that ran last)

sirloop board close

Close a thread (running members are told to stop)

Usage: sirloop board close [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Required.

sirloop board reopen

Reopen a closed thread (its resume budget resets)

Usage: sirloop board reopen [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Required.

sirloop board lock

Lock a closed thread: read-only, for good

Usage: sirloop board lock [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Required.

sirloop board log

The transition ledger, for one thread or all

Usage: sirloop board log [OPTIONS] [ID]

OptionValueDefaultDescription
id positional
--lines LINES 20

sirloop lessons

Fleet lessons: per-project memory distilled from this node's settled receipts — never from a run's self-report, never by a model — and injected into every brief a driver is handed here (goal, handoff, `$SIRLOOP_LESSONS_FILE`). Every entry names the runs that prove it. With no subcommand: list. `docs/lessons.md` has the rules

Usage: sirloop lessons [OPTIONS] [COMMAND]

OptionValueDefaultDescription
--project PROJECT

Only this project (`default` is the default entry)

sirloop lessons distill

Re-derive the distilled lessons from the ledger now. This runs on every settle anyway; here it is for a ledger edited or imported by hand. Idempotent: human entries untouched, contradicted ones stale

Usage: sirloop lessons distill [OPTIONS]

OptionValueDefaultDescription
--project PROJECT

One project (`default` is the default entry); omit for --all

--all false

Every landing point that has receipts

sirloop lessons add

Write a lesson yourself. Pinned from the start: a distill never rewrites or retires what a human wrote

Usage: sirloop lessons add [OPTIONS] [TEXT]...

OptionValueDefaultDescription
--project PROJECT

The project it belongs to (`default` is the default entry)

text positional

The lesson, one line

sirloop lessons mute

Stop injecting a lesson. Kept on disk with its evidence, marked muted; a distill never un-mutes it

Usage: sirloop lessons mute [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Required.

sirloop lessons pin

Keep injecting a lesson whatever the ledger says next

Usage: sirloop lessons pin [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Required.

sirloop lessons unpin

Back to the ledger's judgement (active now; stale if contradicted)

Usage: sirloop lessons unpin [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Required.

sirloop lessons remove

Delete a lesson outright

Usage: sirloop lessons remove [OPTIONS] <ID>

OptionValueDefaultDescription
id positional

Required.

sirloop workflow

Saved workflows: a goal template plus harness/model/gate/project bindings and declared `{param}`s, kept as one JSON file — this node's under ~/.sirloop/workflows/, a project's under its .sirloop/workflows/ (which shadows). Runnable from here, from `orchestrate --workflow`, and from a schedule. With no subcommand: list

Usage: sirloop workflow [OPTIONS] [COMMAND]

sirloop workflow add

Save a workflow: from a JSON file, or from flags. Validated NOW — a template with an undeclared or unused placeholder is refused here, never stored to fail at run time

Usage: sirloop workflow add [OPTIONS] [FILE]

OptionValueDefaultDescription
file positional

A workflow JSON file (its own `name` names it). Exclusive with the field flags below

--name NAME

Workflow name: [a-z0-9][a-z0-9_-]{0,63}

--goal GOAL

The goal template — `{name}` placeholders, `{{` for a literal brace

--param NAME[=DEFAULT] (repeatable)

Declare a placeholder: `name` (required) or `name=default` (repeatable)

--framework FRAMEWORK

Harness binding (a known framework name)

--model MODEL

Model binding: provider/model | class:<name> | omniroute:<route>

--verify VERIFY

Verify gate binding (applies where the run lands here)

--project PROJECT

Named project the run lands on unless the dispatch names one

--attempts ATTEMPTS

Attempt budget

--deliver DELIVER

Deliverable kind: code | brainstorm | research | ops | generic | patch

--into-project PROJECT

Save into this project's .sirloop/workflows/ (versioned with the repository) instead of this node's ~/.sirloop/workflows/

sirloop workflow list

Every workflow visible from here: the addressed project's (or the default entry's), then this node's own, shadowing marked

Usage: sirloop workflow list [OPTIONS]

OptionValueDefaultDescription
--project PROJECT

Look in this project's .sirloop/workflows/ too

sirloop workflow show

One workflow in full — the file, where it was found, its digest

Usage: sirloop workflow show [OPTIONS] <NAME>

OptionValueDefaultDescription
name positional

Required.

--project PROJECT

sirloop workflow remove

Delete this node's copy (a project's file is edited in its repository)

Usage: sirloop workflow remove [OPTIONS] <NAME>

OptionValueDefaultDescription
name positional

Required.

sirloop workflow run

Expand and run one: rides the same orchestrate path a typed goal takes, so the exit code is the run's own verdict (0 accepted, 1 rejected, 2 nothing ran) and the receipt carries the stamp

Usage: sirloop workflow run [OPTIONS] <NAME>

OptionValueDefaultDescription
name positional

Required.

--param NAME=VALUE (repeatable)

name=value for a placeholder (repeatable)

--target TARGET

Run it at this paired peer (its label) instead of here

--project PROJECT

Named project to land on (wins over the workflow's own)

--model MODEL

Model requirement (wins over the workflow's own)

--attempts ATTEMPTS

Attempt budget (wins over the workflow's own; default 3)

--detach false

Start the loop in a background process and return its run id

← All docs