CLI options
The unified cafleet CLI uses literal subject IDs, synchronous SQLite access and explicit pane actions. Run each command as one isolated shell-tool invocation.
Subcommand summary
Environment variables
The default expands HOME once. An explicit URL has the form sqlite:///<path>; resolve a relative file path against the invoking working directory before canonicalizing the database file for shared pane-lock identity. A literal tilde in an override is not expanded. Count values accept nonnegative integers and invalid values fail with <NAME> must be a non-negative integer (got '<value>').
Global Options
cafleet --version prints cafleet <version> and exits 0 before dispatch.
Output shapes
Member list output
Roster records include member_id, name, kind, placement, last_sent, last_recv, last_ack and idle. kind is director when the fleet reference matches, otherwise member. Missing activity is null; placement may be null, or its pane ID pending. Text uses - for absent values and (pending) for a pending pane.
JSON output (--json)
Use a trailing per-subcommand --json. It emits compact single-line JSON with UTF-8 and complete untruncated message text. Application errors remain stderr text; parse errors exit 2. Commands marked no reject the flag, including the root group.
Positional subject ids
Place FLEET_ID, MEMBER_ID or MESSAGE_ID immediately after the subcommand. The subject row supplies related fleet/recipient state. IDs are complete DB-assigned integers, without prefix resolution or environment defaults. A missing or malformed subject uses the parser's native error, exit 2.
Spawned members read literal FLEET ID:, YOUR MEMBER ID:, DIRECTOR MEMBER ID: and CODING AGENT: lines. Pass these literal IDs on each invocation.
Sender and recipient (--from-member-id, --to-member-id)
Send requires integer sender and recipient flags. Both must be active in the same live fleet. The sender row determines the fleet; IDs are coordination identifiers within the local trust model.
permissions.allow coverage
Use prefix rules such as Bash(cafleet message *) to cover broker operations, including cafleet message listen ID --timeout 300 and finite cafleet message wait. Verify native Monitor authorization separately: a Bash-rule check establishes no Monitor permission. Director capture is covered by Bash(cafleet fleet capture *) or a broader authorized prefix. Literal IDs and trailing JSON flags preserve prefix matching.
Keep Bash(cafleet member prompt *) and Bash(cafleet member exec *) under permissions.ask, because their bodies carry operator-controlled input. Ask rules outrank allow rules. The permission profiles describe each role's needs.
Message Body Truncation
CLI text bodies use CAFLEET_MAX_TEXT_LEN Unicode codepoints followed by … when truncated. Full JSON and pane captures preserve their complete content. Pane notification lines follow their own inline-body limit. Message envelope owns the compact rendering.
Text bodies (positional TEXT / --file)
Send and member creation require exactly one positional body or --file PATH; --file - reads UTF-8 stdin. Preserve exact nonblank body text. Missing/both sources or a blank positional body exit 2; blank file/stdin sources and file-read errors exit 1 with the authored diagnostics below. File read/UTF-8 failures retain the actual cause. Compact role-by-path spawn prompts remain necessary because resolved prompts enter backend/multiplexer argv.
member prompt takes one positional line. Native exec has the separate command contract.
cafleet setup — Onboarding and Schema Management
cafleet setup [--coding-agent AGENT...] validates the selected database format before either half writes. Initialize an empty zero-identity file with the embedded baseline in one transaction; migrate known current-family versions. An ahead ledger version or incompatible shape rejects both setup write paths before database mutation, asset installation or asset-install recording. Independent half-error reporting applies after this compatibility gate. A format failure prevents asset installation and content writes. Compatible setup installs embedded assets offline and records versions. Backend selection is multi-value/repeatable, deduplicated and defaults to the supported backend registry.
The database half prints Created PATH and applied migrations to head (N)., Upgraded from M to N. or Already at head (N); nothing to do. according to state. Report actual errors as db half failed: CAUSE or assets half failed: CAUSE; failed halves exit 1. Assets require a compatible usable current schema.
Config-dir resolution
Every surface that installs to or checks a backend's user-level config directory resolves it through the backend's native config-location environment variable, falling back to the default when the variable is unset:
opencode splits by purpose: agents/ is in OPENCODE_CONFIG_DIR's
documented search list, so the preset may relocate and remain a valid
--agent cafleet discovery path; skills are not in that list — opencode
discovers them only at fixed paths — so the skills install ignores the
variable.
OPENCODE_CONFIG_DIR adds a preset search directory alongside default home configuration. Agent definitions with the same name can merge across these sources; inspect the effective configuration when customizing a preset.
Validation. A set variable must hold an absolute path. Any other value —
the empty string, a relative path, a literal unexpanded ~/… — fails at
resolution time with exit 1:
Validation is lazy: a variable is read and validated only when a site
actually resolves that backend's directory. cafleet setup --coding-agent claude with an invalid CODEX_HOME succeeds because the selector resolves
only the targeted agent's directory; plain cafleet setup resolves all
three identity paths, so an invalid variable fails its assets half. The
spawn preconditions themselves read
none of the three variables for claude and codex (PATH-check-only;
opencode's resolves the preset base) — but the
stale-assets guard fronting every fleet-scoped
command, member create included, resolves all three identity paths. One
exception to strict lazy failure: doctor catches per-agent resolution
errors and renders them as issues instead of aborting (see
doctor).
Recorded-path identity. Every surface that keys on "the agent's resolved
path" — the asset_installs rows, the
stale-assets guard, and doctor's setup column — uses
one canonical path per agent, the resolved base directory, stored absolute
exactly as resolved (no canonicalization beyond the absolute-path
validation):
Assets half
Each agent's preset, where one exists, is a static file embedded in the binary next to the skills; install targets resolve per Config-dir resolution:
Each backend replaces the two embedded skills sequentially, removes a leftover
cafleet-research entry, then replaces its preset where present. Existing targets
are deleted before their replacements are written. Unrelated skills remain
untouched. A symlink target is unlinked as an entry;
its referent is not deleted. Skills-operation errors retain failed to install skills into <skills_dir>: <error> and preset-operation errors retain failed to install preset into <target>: <error>.
The command prints each success line after that operation completes, then records the installed version after all operations succeed. The preset line appears only for codex and opencode:
Shared diagnosis and connection reuse
Use the invocation’s shared classifier and connection for guard/setup work. Doctor completes read-only sections even when the database is unavailable. Compatibility includes identity, ledger version and expected table/column shapes.
Schema-version guard
The fresh baseline creates exactly the application tables declared in the schema. Compatibility diagnosis verifies current application identity, ledger version, every required table’s column shape, and required indexes/foreign keys. Known versions use their recorded migration shape; newer versions are diagnosed before any head-shape comparison. Unrelated auxiliary tables, indexes and triggers are compatible while those required definitions remain intact.
Every fleet/member/message command validates current format before writes and asset checks. Identity is application_id=1128351288; malformed or incompatible files fail with database format is incompatible at 'PATH'; choose an empty database path and run 'cafleet setup'. Preserve file bytes and existing sidecars. Empty state reports no cafleet database; run 'cafleet setup'. Behind state reports database schema is outdated (schema M, head N); run 'cafleet setup'; ahead reports database schema M is newer than this cafleet (head N); upgrade cafleet. Database-open/read errors preserve their cause. Setup initializes/repairs compatible known versions; doctor reports rather than blocks its other diagnostics.
Stale-assets guard
Every fleet-scoped surface — the fleet, member, and message groups — validates the recorded assets installs after the
schema-version guard passes and before any
subcommand body runs. The guard resolves each agent's identity path per
Config-dir resolution and checks only the row at
that path. Apply the following precedence:
Staleness checks use version records at currently-resolved paths; agents without
a matching row contribute nothing to staleness. Plain cafleet setup installs
agents at their resolved paths. Setup and doctor are exempt:
--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 either guard — both always print
help, even under a missing database or a missing or stale install.
cafleet fleet — Fleet Management
fleet create
cafleet fleet create --name NAME --coding-agent AGENT [--json]
Require the selected backend token and a valid tmux/herdr context. Outside that context fail with cafleet fleet create must be run inside a tmux or herdr session. Insert fleet, current Director member and placement in one transaction and backfill director_member_id before commit. The Director has name="Director" and description="Root Director for this fleet". Creation splits no pane and launches no process. An ordinary member can be created immediately.
The calling pane becomes the Director's concrete placement. Run fleet create from the coding-agent session declared by --coding-agent, then arm native message reception before depending on member replies. Root-Director messages remain in the durable inbox for native or manual reads; automatic pane previews target ordinary members.
fleet list
List non-deleted fleets by descending creation timestamp/fleet ID. Rows retain the Director reference so its ID remains discoverable.
fleet show
cafleet fleet show FLEET_ID [--json] includes soft-deleted rows. Unknown ID: fleet 'X' not found., exit 1.
fleet delete
cafleet fleet delete FLEET_ID [--json] locks active concrete placements in sorted key order, revalidates the placement set, then stamps deletion and deregisters active members/removes placements in one transaction. It retains messages and is idempotent. Close owned member panes individually first.
One operation-wide five-second deadline bounds lock acquisition, with 50-millisecond retries and set-change restart. Contention leaves state unchanged and exits 1 with fleet ID pane input remained busy for 5 seconds at member MEMBER_ID; fleet was not deleted.
fleet capture
cafleet fleet capture FLEET_ID [--lines N] [--ansi] [--json] checks fleet existence and soft-deletion before resolving the multiplexer. An absent or deleted fleet exits 1 with Error: fleet ID not found. Capture reads the current active roster once, Director first then placement-owning members by ascending ID. The default depth is 20. Per-pane failures, including a pending Director placement, annotate entries and leave the rest available. It writes no broker state.
Text sections begin === ID (NAME; kind=KIND; coding_agent=AGENT; pane=PANE; captured_at=TS) ===, separated by a blank line. A failed/pending pane omits capture time and carries its diagnostic.
JSON entries are ordered member_id, name, kind, coding_agent, pane_id, lines, content, captured_at, content_sha256, error. Kind is director/member. On error, content/time/hash are null; pane_id is null only without a concrete pane. Successful hash/timestamp semantics match member capture.
cafleet doctor — Environment Diagnosis
A full-environment diagnosis that renders all sections even when the
multiplexer is unavailable or the database is missing or stale — no early
abort. Diagnosis order: multiplexer, database, coding agents, member
permissions. doctor is
exempt from the schema-version guard and the
stale-assets guard — a missing or outdated database
and a stale or missing install are reported, not fatal.
The only flag is the optional --json, a trailing
per-subcommand flag.
Text layout
The first output line of the whole report is cafleet <version>. Each
section is led by a single-width verdict glyph (✓ U+2713 / ✗ U+2717)
plus the section name; detail lines are indented two spaces beneath. A
worked example (at-head schema, one stale agent, one superseded record):
Multiplexer section
✓ with the five detail lines (backend, session, window_id,
pane_id, presence). On any multiplexer or environment failure (no
supported multiplexer, ambiguous environment, binary not on PATH, pane not
discoverable): ✗ multiplexer with the resolver's error message as the
single detail line, and the report continues. One issue.
Database section
One detail line describes the database state (<M> recorded version,
<N> embedded head). An empty database has application_id=0 and no
non-internal tables. Validate application identity 1128351288 and the
migration ledger first, then read the recorded version. For supported
versions, validate required table/column, index and foreign-key shapes
against that recorded version's migrations. Older supported versions
therefore remain eligible for in-place migration. A newer version reports
ahead without comparing its unknown shape to the current head.
A missing required table (including asset_installs) or malformed shape in
a supported version is incompatible even when its ledger version equals
head. Compatible auxiliary tables, indexes and triggers remain accepted.
Connection and SQL inspection failures render ✗ with their actual cause as
the detail line (one issue). Doctor completes the coding-agents section for
every database result. It reads recorded installs only for a compatible
at-head database. Every other database state renders resolvable agents as
–, with no recorded versions or superseded footnotes, and contributes one
database issue to exit status 1. A dropped asset_installs table therefore
produces the incompatible-format diagnosis before any install-row lookup.
Coding agents section
A light box-drawing framed table (┌ ─ ┬ ┐ │ ├ ┼ ┤ └ ┴ ┘), header separator
only, no per-row rules. Column alignment uses display width, never byte
length. One row per agent in the fixed order claude, codex, opencode.
Records at other paths only feed informational footnote lines under the
table, one per superseded row, ordered ascending (coding_agent, path),
~-abbreviated:
Footnotes are informational — they never count as issues.
Member permissions section
Reports every Claude Code setting that would stop a claude member from running
its broker commands, so the failure is visible before a spawn. The section
runs for claude when the claude assets are installed, and reads each of
these files that exists:
Each rule is tested against the broker-command probe registry, maintained as PROBE_COMMANDS in the permission probe source. A representative probe is cafleet message listen 1 --timeout 300. This checks Bash command rules; native Monitor authorization is verified separately.
A rule matches a probe when it is the bare Bash, or Bash(<pattern>) whose
pattern matches the probe. * matches any text; a trailing * or the legacy
:* also matches the bare command.
With no finding the section is ✓; otherwise it is ✗ with one detail line
per finding, and each finding is one issue:
The three ✗ lines are, in order, a blocking rule (naming the file, the
list, the rule, and the probe it matches), the managed-rules-only finding
(naming the first unmatched probe), and an unreadable file.
When the claude assets are not installed the row reads
claude: – not installed and never counts as an issue.
The section cannot see a blocking PreToolUse hook, or a rule added after a
member is spawned — see
Spawn-time allow rules.
Footer and exit code
Last line: no issues found, 1 issue found, or <N> issues found (proper
pluralization). Exit code: 0 when no issues, 1 otherwise — the – state and
footnotes never count. Every failure is a rendered issue; no failure exits
before output.
--json
Mirrors the sections with ok booleans, unabbreviated absolute paths, and
the issue count. source holds the winning env-var name (no $) or the
literal "default"; state is "ok" | "stale" | "not_installed" | "error"
("error" is the per-agent resolution-error state; "not_installed" never
contributes to issues). Every agent row carries the same keys. error is
the validation message for "error", and otherwise null, without an Error: prefix. Section error fields likewise hold
the detail text when ok is false, else null.
member_permissions sits between coding_agents and issues. Each element
of findings carries the keys coding_agent, file, list, rule, and
command, in that order:
Each finding adds one to issues.
On a multiplexer failure the multiplexer object is {"ok": false, "backend": null, "session": null, "window_id": null, "pane_id": null, "presence_var": null, "presence_value": null, "error": "<message>"}. On an
agent resolution error the row is {"coding_agent": "...", "path": null, "source": "<VAR>", "recorded_version": null, "installed_at": null, "state": "error", "error": "<VAR> must be an absolute path (got '<value>')"} — the
raw invalid value appears only inside error; path stays null because
no path resolved. schema_version contains the recorded version after identity
and ledger validation, plus shape validation for supported versions. A newer
version retains its recorded version without a head-shape comparison;
incompatible or missing state uses null. For every non-ok database report, recorded
installs are not read: resolved agents use "not_installed", path errors
remain "error", recorded fields are null and superseded is empty.
A missing asset_installs table is an incompatible database state.
Exit-code semantics are identical to text mode.
cafleet message — Message Broker
message send
cafleet message send --from-member-id ID --to-member-id ID (TEXT | --file PATH) [--json]
Resolve the complete text body before starting the five-second persistence/notification budget, including when --file - waits for stdin. Persist one point-to-point delivery, then drain an ordinary recipient's pending messages oldest-first into its concrete pane within the remaining budget, the new message included. Root-Director messages use native or manual broker reads: sends succeed with notification_sent=false, no pane-delivery diagnostic and no multiplexer requirement. Self-sends persist with no pane input. A committed/persisted send remains successful with exit 0; held/unavailable ordinary-member notifications preserve the stdout record and notification_sent=false.
notification_sent is true exactly when this invocation delivered the newly committed message, including when it was drained after an older one. When another invocation delivered the new message, notification_sent stays false and stderr carries no diagnostic; the recipient still received the line. Messages left pending when the budget no longer covers another submission stay pending with no diagnostic; the next send to that recipient or a message wait rotation retries them. Push notifications specifies the line, the drain and under-lock revalidation.
Delivery outcome
A notification failure prints Message NEW_ID was persisted. followed by its diagnostic on stderr, without suppressing successful stdout. Every per-attempt failure uses this diagnostic:
A lock wait that ends after another sender delivered every pending message reports nothing. Other failures, including corrupt, incompatible or unreadable storage and a termination-span start failure, retain their causes after the persisted prefix. Storage failure before commit exits 1 with its cause. Consume an existing committed ID once; notification failure supplies no reason to create another delivery.
After a failed delivery the sender sends exactly one follow-up to the same recipient with the body message N was not delivered to your pane; poll your inbox. Its drain retries N first. When the follow-up also fails, the sender stops.
message poll
cafleet message poll MEMBER_ID [--json] returns only incoming input_required records, descending by status timestamp/message ID. Unknown or inactive member: Member ID not found, exit 1.
message wait
cafleet message wait MEMBER_ID [--timeout SECONDS] [--json]
Timeout is an integer in 1..=1800, default 300. Validate an active subject and live fleet. Existing unread deliveries return immediately; otherwise check at one-second monotonic intervals. The invocation is a blocking receive for scripts and manual use and never ACKs messages.
Between inbox checks, retry pending deliveries for at most one ordinary member per interval with an in-memory round-robin cursor. Select concrete ordinary-member placements in the fleet, excluding its root Director, the wait's own subject, self-notifications, ACKed and already-notified rows. Each attempt drains that member's pending messages under the delivery procedure without consulting pane state. One invocation attempts each pending message at most once; a failed message is retried by a later send or wait invocation, while later messages for the same member remain eligible. Recheck the inbox before starting another attempt. Shared input locks and under-lock revalidation keep multiple waiters safe.
Start each receive check or notification attempt before the monotonic receive deadline. An attempt already in flight retains its original five-second budget for the lock wait, revalidation, Escape, typing, settle and Enter. A busy pane lock makes the attempt wait up to 1.5 seconds; a lock still busy then records the not-notified diagnostic in notification_errors. Cap subprocess/database busy waits to its remaining budget and start input only with time reserved for settle and Enter. Mandatory database finalization uses the later of the original deadline and one second after finalization begins, keeping pane transport within its original budget. A receive returns by its deadline plus at most one six-second in-flight attempt/finalization allowance. A snapshot blocked until receive expiry returns timeout with retained notification diagnostics; earlier transient contention can retry before expiry. Corruption and other fatal storage errors retain their causes.
The ordered JSON result is:
outcome is messages with nonempty full poll records, or timeout with an empty array. Each diagnostic has ordered keys {member_id, message_id, error}. Keep one diagnostic per not-notified message, ordered by message ID.
Pending placement and a placement change under the lock are ordinary holds that keep entries unchanged. A drain that notifies a recorded message removes that message's entry. Reconcile diagnostics with each successful receive snapshot and the typed observations of its owned notification attempt. Remove entries when these observations show that message ACKed, its recipient gone or the message notified by any invocation. After receive expiry, return the last successful snapshot reconciled with the completed attempt's observations. Subsequent external changes become visible to the next invocation; final reconciliation starts no additional I/O or tail budget.
Text is Messages available for member ID. plus normal messages, or Timed out waiting for member ID.; diagnostics go to stderr. Both outcomes exit 0. Deleted subject/fleet or corrupt/unreadable state exits 1 with its cause and retains earlier notification diagnostics on stderr, including when JSON output was requested. Ctrl+C outside a delivery attempt terminates the process normally; inside an attempt it takes effect when the attempt's termination-deferral span ends.
A script or operator uses message wait for finite manual reception and ordinary-member retry recovery, then polls and ACKs consumed messages. Native Director reception retains the existing listener process between events.
message listen
cafleet message listen MEMBER_ID [--timeout SECONDS]
Stream any active member's pending inbox through UTF-8 JSONL. Derive and pin its fleet at startup, using the configured database and ordinary schema/assets guards. Require a live root Director with a concrete registered placement, without resolving a multiplexer or checking its executable/physical pane. The subject's own placement may be absent. Unknown/inactive startup subjects use Member ID not found, exit 1.
The command always emits JSONL; text mode and --json are absent. Stdout contains protocol records only:
The inner message is exactly the full message record, preserving original text beyond CLI/pane limits. closed.reason is timeout, member_inactive or fleet_deleted; fleet deletion takes precedence when both lifecycle closures are observed. Check normal closures before revalidating a continuing live fleet. Missing persisted rows, changed fleet identity, invalid stored enums/root relationships or missing root placement are fatal errors.
Each short read transaction validates identity and selects every input_required record for the pinned subject ordered by (created_at ASC, message_id ASC). Commit before output or sleep. A later commit with an earlier timestamp emits when first observed. A process-local ID set suppresses already-emitted pending IDs; insert only after the complete record and LF flush successfully. Retain only IDs still pending in successful snapshots; a failed snapshot leaves the set unchanged. A snapshot containing only previously emitted pending IDs waits one second, while newly emitted records permit immediate recheck.
Listening leaves consumption and notified_at unchanged and performs no pane retry, lock or dispatch. Stamped pending self-sends remain stream-eligible. Concurrent listeners are independent read-only observers with no consumer lock; restart replays all pending IDs. ACK-before-snapshot removes a message, while ACK racing with buffered output may leave a stale pending envelope. Reconcile known/replayed IDs with poll --json or show --json before acting, record deferred actions and explicitly ACK consumption. Completed records need no second action or ACK.
Each snapshot has a fresh database deadline at most five seconds away, shortened by the remaining lease. Scoped busy waiting and an execution progress handler every 1,000 VM instructions bound lock/SQL work. Check that deadline before/after prepare and execution, before/after decoding each row and before commit. Expiry discards partial snapshots; interrupted SQL maps to deadline exceeded only when that deadline expired. Restore busy handling and remove the listener-owned progress handler on every path, preserving cleanup failures alongside the original cause. These cooperative boundaries include row collection without claiming hard real-time preemption of one allocation or SQL operation.
Output owns one serialized record at a time and applies backpressure before selecting more messages. Nonblocking filesystem/poll output preserves/restores descriptor flags on normal return, resumes partial writes from their byte offset and checks every write/flush. Its five-second deadline is shortened by the lease. Broken pipe, write-zero, flush failure or output deadline exhaustion terminates visibly with exit 1, even after partial records. Normal lease expiry between records uses a separate bounded final write for closed. Complete records survive a later error; an incomplete final line remains unconsumed and recovers through broker replay. Only the current snapshot, one record and pending emitted IDs occupy listener memory.
Database contention retries stay inside the snapshot budget; exhaustion or another fatal error terminates the stream. The listener retains its connection/PID across healthy events and ordinary signal behavior outside pane termination deferral. Host cancellation may end it without closed. Native owners retain one handle, reconcile durable state after failure and establish old-process termination before replacement. The manual lifecycle explains operation; installed backend instructions own concrete native tool recipes and their evidence limits.
message ack
cafleet message ack MESSAGE_ID [--json] transitions an existing unread message to completed, updating its status timestamp. The recipient/fleet derive from that row. ACK signifies consumption.
message show
cafleet message show MESSAGE_ID [--json] returns one full record. Existence is the read guard.
cafleet member — Member Lifecycle + Pane Interaction
Member targeting and key delivery
Require an active member and placement for pane operations; concrete pane IDs remain opaque. Pending placement is valid during creation and yields contextual prompt/exec/capture errors. Missing placement fails pane actions; list/show retain diagnostic nulls. Prompt and exec reject the root Director before pane I/O; individual member deletion protects it.
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.
Spawn command per backend
The per-backend spawn argv and auto-approval flags live in Coding-agent backends.
Spawn-prompt substitution
member create renders its resolved spawn prompt body
with the same formatter, substituting exactly four placeholders:
Identity reaches the spawned member as literals rendered into its prompt; the
only environment variable forwarded into the pane is CAFLEET_DATABASE_URL.
The formatter accepts only the four exact names above and doubled literal
braces ({{ / }}), not Python format specifications, conversions, or
attribute/index access. An unknown placeholder or
malformed brace expression exits 2. Member creation attempts deregistration;
fleet creation attempts bootstrap transaction rollback. Cleanup failures follow the primary error
(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.
Creation failure compensation
Member creation registers a pending placement, renders the prompt, creates a
pane, and patches its id. The CLI owns a registration guard after successful
registration and acquires a pane guard only when split_window succeeds.
The backend owns the pane before that return; see
pane ownership.
Continue remaining compensation even if an earlier cleanup fails. A backend
PaneCleanup::Attempted result is never killed again by the CLI. A failed
split with no confirmed id reports that the pane id is unknown and cleanup
is unconfirmed, including for member creation; it does not infer which other
pane should be closed. Creation rollback uses kill_pane(id, true).
Keep the primary cause and its exit category. Member split failures retain
the primary reason tmux split-window failed: <error>; placement errors retain
placement update failed: <error> or placement row vanished before pane-id patch. Placeholder failures retain their usage error and exit 2. Append
applicable cleanup diagnostics after the primary error, on stderr:
cleanup failed for pane <id>: <detail>cleanup failed for member <id>: <detail>cleanup failed for fleet <id> transaction: <detail>
Report transaction rollback failures explicitly. Existing rollback-success
suffixes may describe confirmed compensation; they must not claim success or
complete rollback when cleanup failed or pane compensation is unconfirmed.
Guards disarm through explicit finish/rollback, preventing repeated kill
or deregistration; Drop handles only remaining unhandled ownership. Once a
member placement is confirmed , disarm all creation
guards before the existing emit call. Successful text/JSON output keeps its
shape, ordering, and nulls. No new stdout-failure exit or diagnostic contract
is introduced.
member delete
Lock the concrete placement before kill/deregistration. A member without a concrete pane needs no lock. Retry contention every 50 milliseconds against one five-second deadline; expiry exits 1 with member ID's pane input remained busy for 5 seconds; member was not deleted. and leaves state unchanged. Preserve pane/registry errors and layout cleanup ownership.
Revalidate pending or absent placement and deregister atomically. For a concrete pane, retain input ownership while closing it outside a SQLite write transaction, then revalidate and finalize the registry. After successful close, mandatory registry finalization uses the later of the original deadline and one second after finalization begins, allowing short reader/writer contention to clear; actual database failures report that the pane was closed and preserve the database cause. Optional layout work follows mandatory finalization within the original deadline. A failed or expired restoration emits a stderr diagnostic while preserving successful deregistration.
member show
cafleet member show MEMBER_ID [--json] — the positional MEMBER_ID names
any active registry entry — placed or placementless (root Director included).
Registry read — no multiplexer requirement. Text is the compact one-line row;
the detailed view — including kind (director / member) and the
placement sub-dict — is the --json payload (see
Output shapes).
member list
cafleet member list FLEET_ID [--json] — the positional FLEET_ID names the
fleet whose roster is listed; no identity flag. Lists every active
registry entry of the fleet —
the root Director, ordinary members, and placementless rows. An empty roster
prints 0 members..
Field projection and absent values are in Member list output.
Rows and activity follow the data-model query contract.
Text idle values render as Ns / Nm / Nh.
Per-member detail such as description and registered_at lives on
member show.
member prompt
cafleet member prompt MEMBER_ID TEXT [--json] validates a nonblank line without CR/LF or C0/DEL controls, otherwise exits 2 with text must be one non-empty line without control characters.
A root Director target exits 1 with cannot prompt the Director's own pane before any pane I/O, because Escape would interrupt the Director's own running turn. Otherwise acquire the shared input lock within its bounded wait and revalidate activity/placement. Submit the Director-authored text unchanged with the Escape lead: Escape, settle, literal text, settle and Enter. Escape can interrupt a working turn or dismiss an open prompt in the member pane. Exit 0 means submission only, with {member_id, pane_id, text}.
A lock still busy when the wait ends uses member ID's pane input remained busy; prompt was not dispatched. Pre-write error, including an Escape failure: prompt was not dispatched to member ID: CAUSE. Partial uncertainty: prompt delivery to member ID is uncertain: CAUSE. Inspect the pane before retrying. These exit 1 without a success record.
member exec
cafleet member exec MEMBER_ID COMMAND [--json] or cafleet member exec MEMBER_ID --file PATH [--json]; --file - reads stdin. Exactly one source is required. File/stdin is UTF-8 and strips at most one terminal LF or CRLF. Preserve inline bytes exactly.
Require a nonblank line without CR/LF, NUL or other C0/DEL controls, including tabs. Invalid input exits 2 before pane I/O with command must be one non-empty line without control characters. A multiline task supplies a one-line explicit script invocation.
Resolve an active ordinary member and concrete placement, reject the root Director, lock input within its bounded wait and reload. With the Escape lead, send Escape, settle, type native shell input ! followed by the actual command once, settle and Enter. The backend owns working directory, environment, permissions, output and exit rendering.
Successful exit 0 establishes complete submission/dispatch only. Ordered JSON is:
Text: Dispatched native command to member ID (PANE). Inspect the pane for its result.
The first text-writing call can partially type even if it fails. Failure after Enter may mean execution began. Inspect native pane output before any newly authorized retry. The command writes no execution ledger, helper script or wrapper and returns only dispatch metadata. The Director authorizes native input under its host rules; visibility establishes no second member-tool permission check.
After native completion, inspect the pane and send an explicit prompt to use the output and continue when the host has not resumed the member.
member capture
cafleet member capture MEMBER_ID [--lines N] [--ansi] [--json] — the
positional MEMBER_ID names the target member.
--lines: Number of trailing lines to capture (default: 20).--ansi: Preserve ANSI escapes in the raw capture. The default strips ANSI escapes and cleans carriage-return redraws.
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).
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. The default mode 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.
The shared CaptureSnapshot::from_raw(raw, ansi, now) supplies this
content, timestamp, and lowercase SHA256 hex for both member capture and
fleet capture. It hashes the final selected content's UTF-8 bytes, including
Unicode and empty captures. Existing line validation/windowing and timestamp
format stay unchanged. Text member capture prints content without adding a
newline; JSON retains its existing fields and ordering.
Error Messages
Application errors print as stderr text even with --json; the flag selects successful output. Input errors use the documented usage/application category. Dispatch, input-lock, compatibility and deletion-lock errors are specified at their action owners above.