CLI options

How the unified CAFleet CLI (cafleet) accepts configuration parameters. This page catalogs the flags, conventions, and error strings.

Subcommand summary

One row per subcommand. "Identity flags" are the per-subcommand options naming members: --member-id names the member in question on single-member commands, and two-party commands name both parties as --from-member-id (sender) + --to-member-id (recipient/target). In the --fleet-id column, yes means the flag is a required per-subcommand option; no means the subcommand rejects it with the parser's unknown-argument error.

SubcommandPurpose--fleet-idIdentity flagSection
setupMigrate the database schema + install the coding-agent assets (skills and presets)nononesetup
doctorPrint the resolved multiplexer backend + the calling pane's identifiers + the assets-install reportnononedoctor
serverStart the admin WebUI servernononeserver
fleet createCreate a fleet with its root Directornononefleet create
fleet listList non-deleted fleetsnononefleet list
fleet showShow one fleet (soft-deleted included)yesnonefleet show
fleet deleteSoft-delete a fleet and deregister its membersyesnonefleet delete
message sendSend a unicast messageyes--from-member-id (sender) + --to-member-id (recipient)message send
message broadcastBroadcast a message to all fleet membersyes--from-member-id (sender)message broadcast
message pollFetch un-acked incoming messagesyes--member-idmessage poll
message ackAcknowledge a received messageyes--member-idmessage ack
message showShow one messageyes--member-idmessage show
member createRegister a member and spawn its coding-agent paneyesnone (Director auto-resolved)member create
member deleteTear down a member's pane (when one exists) and deregister ityes--member-idmember delete
member showShow one member's detailyes--member-idmember show
member listList every active registry entry of the fleetyesnonemember list
member promptKeystroke a prompt (or, with --shell, a shell command) into a member's paneyes--member-idmember prompt
member pingInject an inbox-poll keystroke into a member's paneyes--member-idmember ping
monitor startRun the per-fleet scheduler loop in-process (launch as a background task)yesnonemonitor start
monitor captureCapture the tail of a member's paneyes--member-idmonitor capture

Option Source Matrix

Each parameter has exactly one input source:

ParameterSource
Fleet ID--fleet-id <int> per-subcommand option (placed after the subcommand name)
Database URLCAFLEET_DATABASE_URL env var (optional) — defaults to sqlite:/// + ~/.local/share/cafleet/cafleet_v6.db (home expanded at startup); a user-supplied value must be an absolute-path sqlite:/// URL.
Multiplexer backendCAFLEET_MULTIPLEXER env var (optional) — unset ⇒ auto-detect. See Multiplexer backends.
Member ID--member-id <int> subcommand option (the member in question)
Sender / recipient member IDs--from-member-id <int> / --to-member-id <int> on two-party subcommands
JSON output--json per-subcommand option (trailing canonical position — placed after all other flags)

Environment variables

Every CAFLEET_-prefixed variable cafleet reads:

Environment variableSettings fieldDefaultControlsOverridden by
CAFLEET_DATABASE_URLdatabase_urlsqlite:/// + ~/.local/share/cafleet/cafleet_v6.dbThe registry database location; an absolute path is required
CAFLEET_MULTIPLEXERmultiplexerunset ⇒ auto-detectThe multiplexer backend, per Backend selection
CAFLEET_MAX_TEXT_LENmax_text_len200Body truncation on message {send,poll,ack,show}, and the broker's inline-preview truncation--full
CAFLEET_BROKER_HOSTbroker_host127.0.0.1The cafleet server bind address--host
CAFLEET_BROKER_PORTbroker_port8000The cafleet server bind port--port

A flag wins over its environment variable, and the environment variable wins over the hardcoded default. member.description truncation uses a separate hard-coded 60-codepoint limit.

Global Options

--version, placed before the subcommand, prints cafleet <version> and exits 0, bypassing the --fleet-id requirement.

Output shapes

One row per subcommand. --full is the global "give me every field cafleet has, untruncated, unfiltered" escape hatch; an em-dash marks a subcommand that does not accept it.

SubcommandDefault text output--full text outputJSON payload
fleet createThe compact line <fleet_id> director=<director_member_id>The labeled blockThe fleet dict with a nested director (including its placement)
fleet listFLEET_ID, DIRECTOR, NAME, MEMBERS, CREATED_AT columns, one row per fleetOutput as JSON
fleet showThe fleet row, adding a deleted_at: lineAlways includes deleted_at, null when active
fleet deleteDeleted fleet <fleet_id>. Deregistered N members.
message sendMessage sent. plus the compact rendered envelopeUntruncated text; the verbose labeled blockThe rendered envelope, or the full typed-column message dict with --full
message broadcastbroadcast id=<message_id> recipients=<N> delivered=<k>The single broadcast_summary message as the full verbose envelope, never per-recipient envelopes or a recipient_ids listCarries both recipients and delivered
message pollThe compact rendered envelopes; No messages found. on an empty inboxUntruncated text; the verbose labeled blockThe rendered envelopes
message ackMessage acknowledged. plus the compact rendered envelopeUntruncated text; the verbose labeled blockThe rendered envelope
message showThe compact rendered envelope aloneUntruncated text; the verbose labeled blockThe rendered envelope
member createThe compact line <member_id> <name> backend=<coding_agent> pane=<pane_id>The 6-line Member registered and spawned. blockThe member dict with its placement
member deleteA Member deleted. header plus member_id: / pane_id: lines, pane status <pane_id> (killed){member_id, pane_status}
member showThe compact one-line row <member_id> <name> <status>The labeled block with kind, skills, and the placement sub-blockThe broker get_member dict, unchanged regardless of --full
member listOne row per member; 0 members. on an empty rosterOne dict per row
member promptSent prompt '<text>' to member <name> (<pane_id>)., or Sent shell prompt '<text>' … with --shell{member_id, pane_id, text, shell}
member pingPinged member <name> (<pane_id>) — poll keystroke dispatched., or the pending-placement skip line — see member ping{member_id, pane_id, skipped}skipped present on both success paths
monitor captureThe captured content, byte-identical to the prior text surface{member_id, pane_id, lines, content, captured_at, content_sha256} in that key order
doctorThe multiplexer: and assets: blocksOutput as JSON

setup, server, and monitor start are absent by design — they stream progress or run a loop rather than emitting a one-shot payload — so the Subcommand summary legitimately carries three more rows than this table.

--quiet semantics

--quiet suppresses the normal output and prints one bare value, for shell capture. It is a text-only shortcut, ignored in the JSON branch.

SubcommandQuiet output
message sendThe bare message_id
message ackThe bare message_id
member pingThe bare target member id

JSON output (--json)

--json is a shared per-subcommand flag, placed after the subcommand name — canonically trailing, after all other flags:

cafleet message poll --fleet-id 2 --member-id 7 --json
cafleet message poll --fleet-id 2 --member-id 7 --full --json

It switches the output to compact single-line JSON; non-ASCII (like the truncation suffix) is emitted as UTF-8, not escaped. --json and --full are independent and composable — truncation is applied to the result before the json-vs-text fork. --quiet is a text-only shortcut, ignored in the JSON branch. The trailing position keeps JSON invocations inside the existing per-subcommand allow patterns (see permissions.allow coverage).

Subcommands accepting --json, one row per subcommand:

SubcommandGroup
doctor(root)
fleet createfleet
fleet listfleet
fleet showfleet
message sendmessage
message broadcastmessage
message pollmessage
message ackmessage
message showmessage
member createmember
member deletemember
member showmember
member listmember
member promptmember
member pingmember
monitor capturemonitor

All other subcommands reject --json with the parser's unknown-argument error (exit 2) — including the root group itself, so a pre-subcommand cafleet --json <grp> <cmd> does not parse.

Fleet ID (--fleet-id)

--fleet-id is a per-subcommand option, typed int, placed immediately after the subcommand name (the canonical position). It is required with no environment default: a spawned member reads its fleet id from the FLEET ID: line rendered into its spawn prompt and passes it as a literal flag on every command. Members pass the literal integer — never a shell variable — because Claude Code's permissions.allow matches Bash invocations as literal command strings, and matching also depends on the canonical flag order (see permissions.allow coverage).

Member ID (--member-id)

--member-id is a per-subcommand option typed int (as are --from-member-id, --to-member-id, and --message-id; each rejects a non-integer with the parser's invalid-integer error, exit 2). Ids are DB-assigned integers, typically 1–4 digits, pasted in full — there is no prefix resolution. --member-id always names the member in question: the requester on message poll / ack / show, and the target on member delete / show / prompt / ping and monitor capture.

Sender and recipient (--from-member-id, --to-member-id)

Two-party commands name both parties: --from-member-id is the sender (message send, message broadcast) and --to-member-id is the recipient (message send). A pane-touching target must be an active member of --fleet-id with a placement row — see Member targeting and key delivery.

permissions.allow coverage

The allow set is generated mechanically, one Bash(...) pattern per allow-listed subcommand:

  • One pattern per subcommand, matching the canonical --fleet-id-first flag order — Bash(cafleet <grp> <cmd> --fleet-id *). A different flag order does not match and prompts. Trailing flags such as --json are covered by the same pattern.
  • member prompt is excluded so it stays under permissions.ask — its positional text body is operator-controlled, in both the plain and the --shell form.
Bash(cafleet message poll --fleet-id *)
Bash(cafleet member create --fleet-id *)
Bash(cafleet monitor capture --fleet-id *)

Apply the patterns to your user-level ~/.claude/settings.json manually; the repo does not ship a committed permissions block.

Message Body Truncation

The four subcommands that emit a user-supplied delivery body — cafleet message {send,poll,ack,show} — truncate the text body to the first CAFLEET_MAX_TEXT_LEN Unicode codepoints plus a single (U+2026) suffix by default, uniformly in text and --json output. Length is measured in Unicode codepoints, never bytes. Pass --full (a documented per-subcommand option) to disable truncation; it composes orthogonally with --json.

The limit is CAFLEET_MAX_TEXT_LEN — see Environment variables.

This applies to CLI emit sites only — the WebUI /api/* responses (webui-api.md) and monitor capture content are untouched.

cafleet setup — Onboarding and Schema Management

cafleet setup is a plain command — the single onboarding and schema-management entry point (the recommended end-user path — see Quickstart). Command help: Migrate the database schema and install the coding-agent assets (skills and presets). It takes no positional arguments — cafleet setup <word> fails with the parser's unexpected-argument error — and does not accept --fleet-id.

The one flag is --skip AGENT: optional and repeatable, a choice over claude / codex / opencode, with duplicates deduplicated and an unknown value failing with the parser's invalid-value error (exit 2). Its help text is Skip the named agent's assets install (repeatable).

The command runs two halves, in order:

  1. db half — initializes or migrates the registry database to the head of the migration chain embedded in the binary (idempotent). Each refusal message below becomes the db-half failure <msg>; <M> / <N> are the integer migration versions:

    Prior DB stateOutcomeOutput / refusal message
    No DB fileCreated and migrated to headCreated <db_file> and applied migrations to head (<N>).
    Behind headUpgradedUpgraded from <M> to <N>.
    At headNo-opAlready at head (<N>); nothing to do.
    Tables present but no refinery_schema_historyRefusedDB has existing tables but no refinery_schema_history. Refusing to migrate an unversioned database.
    Version unknown to this CLI versionRefusedDB schema is at version <M> which is unknown to this version of cafleet. Refusing to downgrade automatically.
  2. assets half — targets are the fixed list claude, codex, opencode (in that order) minus the skipped agents. Installs, from the data embedded in the binary at build time with no network access, per target the three skill directories plus the agent's bundled preset where one exists (creating the agent's directories as needed), and records one asset_installs row per target (see Assets half). When all three agents are skipped, the half is skipped entirely: the command echoes assets half skipped (all agents skipped) and the half counts as not-run.

The halves fail independently (db half failed: <msg> / assets half failed: <msg>); if any half that ran failed, the command exits 1 with the failed halves joined by ' and '.

HalfTriggerMessageEffect
dbEither refusal state in the table aboveThe refusal message, as db half failed: <msg>Exit 1
assetsThe asset_installs table is missing as the half startsthe database schema is missing or outdated; run 'cafleet setup' firstExit 1
assetsA skills install fails for a targetfailed to install skills into <skills_dir>: <error>Aborts the loop; rows recorded before the failure remain
assetsA preset install fails for a targetfailed to install preset into <target>: <error>Aborts the loop; rows recorded before the failure remain
assetsAll three agents skippedassets half skipped (all agents skipped)Not-run; cannot contribute a failure

The assets-half pre-flight fires only after a db-half failure or an externally broken schema, since the db half always runs first within the same command; it is kept as defense.

Schema-only invocation

The documented invocation for "bring the DB to head without touching assets" (the contributor and CI path):

cafleet setup --skip claude --skip codex --skip opencode

It is deterministic (independent of which agent homes exist) and exits 0 when the db half succeeds. The schema-only invocation never records asset_installs rows.

Assets half

Each agent's preset, where one exists, is a static file embedded in the binary next to the skills:

AgentBundled presetInstall target
claude— (skills only)
codexpresets/codex/cafleet.rules~/.codex/rules/cafleet.rules
opencodepresets/opencode/cafleet.md~/.opencode/agents/cafleet.md

Per target:

  1. The three skill directories are delete-and-reinstalled into the agent's skills dir; a failure aborts with failed to install skills into <skills_dir>: <error>.
  2. For agents with a bundled preset (codex, opencode), the preset is installed to its target, overwriting whatever exists there — a regular file, a directory, or a symlink. A filesystem error aborts with failed to install preset into <target>: <error>.
  3. The agent's asset_installs row is upserted only after both its skills and its preset install successfully — the row attests skills + preset — then the command echoes (the preset line appears for codex and opencode targets only):
<agent>: installed cafleet, cafleet-design-doc, cafleet-research (v<version>) -> <skills dir>
<agent>: installed preset (v<version>) -> <target>

An install failure aborts the loop; rows recorded before the failure remain.

Stale-assets guard

Every fleet-scoped command group — fleet, member, message, and monitor — validates the recorded assets installs before any subcommand body runs:

Recorded install stateResultExit
No DB file, no asset_installs table, or zero rowsError: no assets install is recorded; run 'cafleet setup' first1
A recorded cafleet_version differs from the runtime CLI version (string inequality — a downgrade also triggers)Error: stale assets detected (<agent>=<recorded>[, ...]; CLI <runtime>); run 'cafleet setup' to reinstall, stale agents in ascending order1
All recorded versions matchThe command proceeds silently0

Agents with no recorded row are not checked. Three surfaces are exempt:

Exempt surfaceWhy exemptBehavior under a stale/missing install
setupIt must remain runnable to repair the installRuns normally — it is the repair path
doctorIt reports instead of blockingPrints each recorded row marked ok or STALE
serverIt serves the WebUI rather than running a fleet-scoped command, and the guard wraps only the four fleet-scoped groupsStarts normally

--help renders at parse time and exits before any command body runs, so neither group-level help (cafleet fleet --help) nor subcommand help (cafleet fleet create --help) triggers the guard — both always print help, even under a missing or stale install.

cafleet fleet — Fleet Management

Fleet lifecycle; writes directly to SQLite — no server required.

fleet create

FlagRequiredNotes
--nameyesHuman-readable name for the fleet
--coding-agentyesOne of claude, codex, or opencode, recorded as the root Director's placement coding_agent — the operator declares the backend the Director is actually running on; see Coding agents.
--jsonnoOutput as JSON
--fullnoSwitches the non-JSON output from the compact one-line form to a labeled block.

Omitting --coding-agent exits 2 with the parser's native missing-required-argument error naming --coding-agent.

Must be run inside a tmux or herdr session — outside one it exits 1 with Error: cafleet fleet create must be run inside a tmux or herdr session and writes nothing. It creates the fleet and its root Director (hardcoded name="Director"; there is no --description flag) atomically — see data-model.md. Output shapes are in Output shapes.

fleet list

The only flag is the optional shared --json.

Lists all non-soft-deleted fleets. Each row exposes director_member_id so the Director's id can be recovered after fleet create output scrolls away.

fleet show

FlagRequiredNotes
--fleet-idyesThe fleet to show
--jsonnoOutput as JSON

Exits 1 with Error: fleet 'X' not found. if the row does not exist. Intentionally returns soft-deleted rows, so audit info stays reachable.

fleet delete

The only flag is --fleet-id (required), naming the fleet to delete.

Soft-deletes the fleet in one transaction: stamps deleted_at, deregisters every active member (root Director included), and removes their placement rows; messages are untouched. It is idempotent (Deregistered 0 members. on re-run). Unknown fleet_id exits 1 with Error: fleet 'X' not found.. Member panes are not closed — run cafleet member delete per member first for a clean teardown.

cafleet doctor — Placement Diagnostics

Resolves the active multiplexer backend via resolve_multiplexer() (Multiplexer backends), prints the resolved backend and the calling pane's session/window/pane identifiers, then the assets-install report (the runtime CLI version and every recorded asset_installs row, each marked ok or STALE). doctor is exempt from the stale-assets guard — a stale or missing install is reported, not fatal.

The only flag is the optional --json, a trailing per-subcommand flag.

multiplexer:
  backend:       tmux
  session:       main
  window_id:     @3
  pane_id:       %0
  presence:      TMUX=/tmp/tmux-501/default,12345,0
assets:
  cli_version: 0.6.0
  claude:      0.6.0 (2026-07-04T00:12:09.123456+00:00) ok
  codex:       0.5.0 (2026-06-20T10:00:00.987654+00:00) STALE

Exit 1 on any multiplexer or environment failure (no supported multiplexer detected, ambiguous environment, binary not on PATH, pane not discoverable).

cafleet server — Admin WebUI Server

Starts the admin WebUI app under the built-in HTTP server (single process, no auto-reload, no worker or log-level flags). CLI commands do not require this server. Does not accept --fleet-id.

FlagDefaultNotes
--hostsettings.broker_host (default 127.0.0.1)Bind address. Overrides CAFLEET_BROKER_HOST when both are set.
--portsettings.broker_port (default 8000)Bind port. Overrides CAFLEET_BROKER_PORT when both are set.

Flag wins over env var; env var wins over the hardcoded default. The WebUI assets are embedded in the binary at build time, so the served SPA always matches the binary; port-in-use errors propagate unwrapped.

cafleet message — Message Broker

All five subcommands require --fleet-id, name the acting member (--from-member-id on send / broadcast, --member-id on poll / ack / show), and run behind the stale-assets guard. The envelope schema is canonical in Message envelope; truncation and --full are canonical above; per-subcommand output shapes are in Output shapes.

message send

FlagRequiredNotes
--from-member-idyesSender.
--to-member-idyesRecipient member id.
--textnoInline message body. Exactly one of --text / --text-file.
--text-filenoPath to a UTF-8 file whose contents are the body (- = stdin); use it for bodies that would exceed the shell's ARG_MAX.
--fullnoSee Message Body Truncation.
--quietnoPrint only the bare message_id, for shell capture — see --quiet semantics.

message broadcast

FlagRequiredNotes
--from-member-idyesBroadcaster (sender).
--text / --text-fileone ofMessage body, as on message send.
--fullnoSee Output shapes.

delivered=<k> counts the best-effort inline previews that landed.

message poll

FlagRequiredNotes
--member-idyesRecipient whose inbox is fetched.
--fullnoSee Message Body Truncation.

Returns only un-acked (input_required) deliveries addressed to the member.

message ack

FlagRequiredNotes
--member-idyesRecipient acknowledging the message (recipient-only).
--message-idyesMessage to acknowledge.
--fullnoSee Message Body Truncation.
--quietnoPrint only the bare message_id, for shell capture — see --quiet semantics.

message show

FlagRequiredNotes
--member-idyesThe acting member (fleet-membership gate).
--message-idyesMessage to fetch.
--fullnoSee Message Body Truncation.

cafleet member — Member Lifecycle + Pane Interaction

The cafleet member subgroup owns the member lifecycle: create registers a member and spawns its coding-agent pane; delete tears it down; prompt / ping keystroke an existing member's pane; show and list are registry reads (no multiplexer requirement). All run behind the stale-assets guard.

Member targeting and key delivery

Resolution shared by the --member-id verbs (including monitor capture), by target state:

Target statemonitor capture / member promptmember pingshowdelete
Active, placed with a pane_idDispatchesDispatchesShows the memberKills the pane, then soft-deletes
Active, placement pending (pane_id is None)Exit 1Skips the keystroke; exit 0 — see member pingShows the memberTolerated — a plain registry soft-delete
Active, no placement rowExit 1Exit 1ToleratedTolerated — a plain registry soft-delete
Cross-fleet, unknown, or inactiveExit 1, Error: Member <member-id> not foundThe same errorThe same errorThe same error

Any active in-fleet member (the root Director included) is a valid target; there is no caller-auth check beyond fleet membership. Key sequences are delivered literally (send-keys with shell=False) — shell meta, key names, and multi-byte characters all arrive as plain characters.

ExitMeaning
0Dispatch success
1Multiplexer unavailable
1Member not found
1Missing placement
1Pending placement (monitor capture / member prompt only — member ping skips and exits 0)
1A send-keys failure
2Per-subcommand argument or validation errors

member create

Register a member and spawn its coding-agent pane. It takes no identity flag: the acting Director is auto-resolved from fleets.director_member_id first thing, before registration (the resolved id also feeds the member's backend inheritance and the spawn-prompt substitution). A fleet has exactly one root Director by construction, so no override flag exists.

FlagRequiredNotes
--nameyesDisplay name — see Known asymmetries for pane-title behavior.
--descriptionyesOne-sentence purpose.
--coding-agentnoOne of claude, codex, or opencode; when omitted, the member — every role — inherits the spawning Director's placement backend. Exits 1 with Error: binary <name> not found on PATH when the binary is missing.
--modelnoModel forwarded to the backend binary's own --model flag. The opencode backend additionally requires <provider-id>/<model-id>; per-backend formats and create-time validation are in Model selection.
--effortnoReasoning-effort level forwarded to the backend binary, validated per backend before any side effect. Accepted levels, forwarding forms, and rejection strings are in Reasoning effort.
--rolenomember (default) or monitor. monitor spawns the fleet's single dedicated monitoring member; a second spawn is rejected.
--fullnoSwitches the non-JSON output to the 6-line labeled block.
--textnoInline spawn prompt (backend-neutral template). Exactly one of --text / --text-file.
--text-filenoPath to a UTF-8 file whose contents are the spawn prompt (- = stdin). Inline prompts beyond a few KB exceed the multiplexer argv ceiling — use --text-file for long prompts.

The monitoring member's role and lifecycle are covered in Monitoring.

Spawn command per backend

The per-backend spawn argv and auto-approval flags live in Coding-agent backends.

Spawn-prompt substitution

cafleet member create runs str.format over the resolved prompt body, substituting exactly four placeholders:

PlaceholderSubstituted valueHow the spawned member sees it
{fleet_id}The member's fleet idFLEET ID: <fleet_id>
{member_id}The member's own newly-allocated idYOUR MEMBER ID: <member_id>
{director_member_id}The fleet's root Director idDIRECTOR MEMBER ID: <director_member_id>
{coding_agent}The resolved backend name (claude, codex, or opencode)CODING AGENT: <coding_agent>

Identity reaches the spawned member as literals rendered into its prompt; the only environment variable forwarded into the pane is CAFLEET_DATABASE_URL. A literal brace must be doubled ({{ / }}); an unknown placeholder or malformed brace expression exits 2 and rolls back the just-registered member (see Error Messages).

The spawn always creates the pane without stealing focus (tmux split-window -d): the Director's pane and active window stay active. In the default output, pane renders (pending) until the pane id is patched onto the placement.

member delete

The only flag is --member-id (required), naming the member to delete.

Tears down the target's pane (when one exists) and soft-deletes the member. Targeting the root Director is blocked (see Error Messages). A placementless or pending-placement delete is a pure registry soft-delete and succeeds outside a multiplexer. The pane path kills the pane immediately (tolerating an already-gone pane), then soft-deletes; exit 0. Pane status renders (no placement) for a placementless target and (pending — no pane) for a pending placement.

member show

FlagRequiredNotes
--member-idyesAny active in-fleet registry entry — placed or placementless (root Director included).
--fullnoText mode only — see Output shapes.

Registry read — no multiplexer requirement. In the --full block, the placement sub-block renders placement: none when the member is placementless, and None fields render -. kind is one of director, monitor, or member.

member list

No flags beyond --fleet-id and the shared trailing --json flag; no identity flag. Lists every active registry entry of the fleet — the root Director, the monitoring member, ordinary members, and placementless rows. An empty roster prints 0 members..

FieldText columnText rendering when absentJSON keyJSON type
member_idyesmember_idinteger
nameyesnamestring
kindyes (director / monitor / member)kindstring
backendyes- for a placementless row— (inside placement)
pane_idyes- placementless; (pending) before the pane id is patched— (inside placement)
idleyes- when the member has no message activityidleinteger seconds or null
placementplacementthe placement sub-dict, null for a placementless row
last_sentlast_sentISO timestamp or null
last_recvlast_recvISO timestamp or null
last_acklast_ackISO timestamp or null

idle is the wall-time since the member's most recent message activity — the latest of last_sent (most recent outgoing message) and last_recv (most recent delivery), broadcast summaries excluded — humanized as Ns / Nm / Nh in text mode. last_ack is the most recent acknowledged delivery. Per-member detail such as description and registered_at lives on member show.

member prompt

Director-only keystroke primitive with two forms. The plain form keystrokes TEXT into a member's pane as a submitted user turn — for text that only takes effect when it arrives as a direct user turn (slash commands, skill invocations, and other magic commands a broker message body cannot trigger). The --shell form keystrokes ! TEXT so the coding agent's ! shortcut runs the command natively — honored by all three backends; it is the dispatch half of the cafleet skill's bash-via-Director fallback protocol. Broker messaging remains the canonical coordination channel; the plain form is not a substitute for message send.

Flag / argumentRequiredNotes
--member-idyesTarget member's ID
--shellnoBoolean flag, default off. Dispatch ! TEXT (shell form) instead of TEXT (plain form).
(positional TEXT)yesSingle line of text; leading/trailing whitespace stripped before dispatch. Newline-containing or empty-after-strip text exits 2.

Shell metacharacters — pipes, &&, ;, $(...), and backticks — are forwarded opaquely. The newline check runs first, against the original text.

The --shell flag controls both the payload prefix and the Esc safeguard:

FormKeystroke sequenceFollow-up
Plain (no --shell)Esc → settle → literal TEXTEnterNone
--shellliteral ! TEXTEntercafleet member ping required

In the plain form the trailing Enter submits a real user turn, and the leading Esc (as in member ping and inline previews) keeps it from blindly confirming a pending permission prompt; the submitted turn opens the member's turn directly. The --shell form leads with no Esc because an Esc before ! <cmd> would mis-fire (see Esc safeguard), and its bang output only stages in the pane — the ping advances the member's turn to consume it.

The flag performs no content inspection: plain-form TEXT beginning with ! is delivered verbatim without the shell mechanics.

Output shapes are in Output shapes.

member ping

Re-pokes a member's inbox: keystrokes Esccafleet message poll --fleet-id <fleet-id> --member-id <member-id>Enter into the target's pane (the leading Esc is the permission-prompt safeguard — see Push notifications). The manual re-poke for a pane that missed the broker's automatic on-delivery notification; the action is wholly fixed by the command — no operator-controlled body — which is why member ping sits in permissions.allow while member prompt stays in permissions.ask.

FlagRequiredNotes
--member-idyesThe target member.
--quietnoPrint only the bare member id, for shell capture.

A pending placement (a placement row whose pane_id is not yet patched) takes the skip path: no keystroke is sent and the command succeeds — the pending member's inbox is intact and it polls it on spawn, so there is nothing a ping would add. Exit code 0 on both success paths in every mode; the skipped JSON key is present on both paths (stable schema).

ModeNormal successPending-placement skip
textPinged member <name> (<pane_id>) — poll keystroke dispatched.Member <name> has no pane yet (pending placement) — ping skipped; it will poll its inbox on spawn.
--json{"member_id": <id>, "pane_id": "<pane_id>", "skipped": false}{"member_id": <id>, "pane_id": null, "skipped": true}
--quietbare <member_id>bare <member_id>

A keystroke non-delivery, an unknown member, and a missing placement row all still exit 1.

cafleet monitor — Supervision Scheduler

The monitor group is exactly the monitoring toolkit: the scheduler loop and its read primitive. Every monitor subcommand requires --fleet-id and runs behind the stale-assets guard. The conceptual model is canonical on the Monitoring concepts page; there is no monitor stop — the loop terminates with the monitoring member's pane (member delete), and a still-running loop self-terminates on its next tick after fleet delete.

monitor start

The one flag is --tick (optional): the scan-tick cadence in seconds (an integer ≥ 1, default 5). The tick is the floor on interval precision — see Monitoring.

Runs the loop in-process (the monitoring member launches it as a background task in its own pane; the loop blocks the task and writes to its stdout — one <iso-ts> due member <id> (<name>) [<reasons>] -> wake monitor line per due member). On startup it runs the multiplexer precondition guard, atomically claims the single-instance monitor_runtime row, installs SIGTERM/SIGINT handlers (a clean stop clears the row), and — immediately after the successful claim, before the first tick — prints the startup line that backs the monitoring member's ready: monitor live handshake:

monitor loop started (fleet <fleet_id>, tick <tick>s, pid <pid>)

If the fleet has no monitoring member, it warns on stderr and runs anyway.

ExitMeaning
0Clean exit
1A monitor is already running for the fleet
1Unknown fleet
1Multiplexer unreachable
2Usage errors

monitor capture

FlagRequiredNotes
--member-idyesTarget member's ID
--linesnoNumber of trailing lines to capture (default: 20).
--ansi / --no-ansinoDefault --no-ansi strips ANSI escapes and cleans carriage-return redraws; --ansi emits the raw capture.

Output shapes are in Output shapes; target resolution is shared with the member keystroke verbs — see Member targeting and key delivery. A pending placement is a hard error (see Error Messages).

Text output remains byte-identical. JSON stamps captured_at from the local UTC clock at the capture read boundary and computes content_sha256 = sha256(content.encode("utf-8")) from the exact emitted content. --no-ansi hashes the ANSI-stripped, carriage-return-defragmented string; --ansi hashes the ANSI-preserving string. No normalization occurs after the selected mode, and capture content is never stored in SQLite.

Error Messages

CommandSituationError messageExitNotes
(any fleet-scoped command)No assets install recorded (missing DB file, missing asset_installs table, or zero rows)Error: no assets install is recorded; run 'cafleet setup' first1See Stale-assets guard
(any fleet-scoped command)A recorded asset_installs version differs from the runtime CLI versionError: stale assets detected (<agent>=<recorded>[, ...]; CLI <runtime>); run 'cafleet setup' to reinstall1See Stale-assets guard
setupThe asset_installs table is missing as the assets half startsthe database schema is missing or outdated; run 'cafleet setup' first1An assets-half failure, after a db-half failure or an externally broken schema
(any fleet-scoped command)Missing --fleet-idError: --fleet-id <int> is required for this subcommand. Create a fleet with 'cafleet fleet create' and pass its id.1
(any --member-id command)Missing --member-idError: Missing option '--member-id'.2
fleet createNo --nameError: Missing option '--name'.2
fleet create--coding-agent omittedError: Missing option '--coding-agent'. Choose from: followed by claude, / codex, / opencode, one per tab-indented line2Recorded verbatim under fleet create
fleet createRun outside a supported multiplexerError: cafleet fleet create must be run inside a tmux or herdr session1No DB writes
fleet deleteUnknown fleet_idError: fleet 'X' not found.1
member createUnknown --fleet-idError: Fleet '<fleet-id>' not found.2Director auto-discovery runs first thing
member createInto a soft-deleted fleetError: fleet X is deleted1
member createThe fleet row has no director_member_id recordedError: fleet <fleet-id> has no root Director recorded; re-create the fleet with 'cafleet fleet create'.1Mid-bootstrap corruption
member createWith a placement, when the fleet's root Director is not an active memberError: fleet <fleet-id>'s root Director (member <id>) is not active.1The register_member invariant guard
member deleteAgainst the root Director's idError: cannot deregister the root Director; use 'cafleet fleet delete' instead1
message *An acting member id (--member-id / --from-member-id) not in --fleet-idError: member <member-id> is not in fleet <fleet-id>.1The fleet-membership gate runs before any read/write operation, and also fires for an unknown id
member promptMissing positional TEXTError: Missing argument 'TEXT'.2
member prompt\n or \r in the textError: text may not contain newlines.2Checked first, against the original text — a "\n"-only input raises this, not the empty-text error
member promptEmpty / whitespace-only textError: text may not be empty.2
monitor capture / member promptThe member has a pending placementError: member <id> has no pane yet (pending placement) — nothing to <capture|prompt>.1member ping instead skips and exits 0 — see member ping
member pingThe keystroke failsError: send failed: tmux send-keys did not deliver the poll-trigger keystroke to pane <pane>.1
member show / prompt / ping, monitor captureA cross-fleet, unknown, or inactive target member idError: Member <member-id> not found1
member prompt / ping, monitor captureAn in-fleet target with no placement rowError: member <member-id> has no placement row; it was not spawned via `cafleet member create`.1
message send / message broadcast / member createNeither --text nor --text-fileError: Provide exactly one of --text or --text-file.2
message send / message broadcast / member createBoth flagsError: --text and --text-file are mutually exclusive.2
message send / message broadcast / member create--text empty or whitespace-onlyError: text may not be empty.2
message send / message broadcast / member create--text-file <path> to an empty (zero-byte or whitespace-only) fileError: --text-file <path>: file is empty.1
message send / message broadcast / member create--text-file - with empty or whitespace-only stdinError: --text-file -: stdin is empty.1
message send / message broadcast / member create--text-file <path> to a non-existent path or non-regular fileError: --text-file <path>: file does not exist or is not a regular file.1
message send / message broadcast / member create--text-file <path> to an unreadable fileError: --text-file <path>: file is not readable.1
message send / message broadcast / member create--text-file <path> to a file containing invalid UTF-8Error: --text-file <path>: file is not valid UTF-8.1
member create--coding-agent opencode --model violating the <provider-id>/<model-id> formatError: --model for the opencode backend must be '<provider-id>/<model-id>' (got '<value>').2Fires before any side effect
member create--effort with a level unknown to the claude backendError: --effort for the claude backend must be one of low, medium, high, xhigh, max (got '<value>').2Fires before any side effect
member create--coding-agent codex --effort with an unknown levelError: --effort for the codex backend must be one of minimal, low, medium, high, xhigh (got '<value>').2Fires before any side effect
member create--coding-agent opencode --effort with any valueError: opencode does not support reasoning effort.2Fires before any side effect
member createAn unknown {placeholder} in the promptError: Unknown placeholder '<name>' in custom prompt. Supported placeholders: {fleet_id}, {member_id}, {director_member_id}, {coding_agent}. Double literal braces ({{, }}) to keep them as text.2The just-registered member is rolled back
member createA malformed brace expression in the promptError: Malformed custom prompt: <detail>. Double literal braces ({{, }}) to keep them as text.2The just-registered member is rolled back
member create--role monitor when the fleet already has an active monitoring memberError: fleet <id> already has an active monitoring member (member <existing-id>); only one is allowed.1
member create--coding-agent omitted and the spawning Director not found in the fleetError: cannot resolve the member's coding agent: Director <director-id> not found in fleet <fleet-id>. Re-run with an explicit --coding-agent.1Nothing spawned
member create--coding-agent omitted and the spawning Director has no placement rowError: cannot resolve the member's coding agent: Director <director-id> has no placement row recording its backend. Re-run with an explicit --coding-agent.1Nothing spawned
monitor startThe fleet already has a live monitorError: monitor already running for fleet <id>1
monitor startAn unknown or soft-deleted fleetError: fleet <id> not found1