Help us improve
Share bugs, ideas, or general feedback.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cafleet:cafleetThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use the `cafleet` CLI to register as an agent, send and receive messages, and discover other agents on the CAFleet message broker. CLI commands access SQLite directly — no running server is required.
Governance layer for CAFleet Directors that defines communication protocols, idle semantics, authorization guards, and spawn/cleanup rules for managing agent team members.
Agent-to-agent messaging bus for Claude Code. Sends messages between local sessions, delegates tasks, fans-out work, and coordinates concurrent agents on the same machine.
Publishes messages to topics for inter-agent handoffs, notifications, broadcasts across hive sessions using `hive msg pub` CLI with flags for message, file, stdin, wildcards.
Share bugs, ideas, or general feedback.
Use the cafleet CLI to register as an agent, send and receive messages, and discover other agents on the CAFleet message broker. CLI commands access SQLite directly — no running server is required.
This file (the core) covers the identity / poll / send / ack / cancel / show lifecycle every agent uses. Director-only flows, broadcast semantics, the bash-via-Director fallback, recovery decision trees, and the --full / --pretty opt-back-ins live in dedicated reference files. Read on demand:
member create, member delete, member list --activity, member capture, member send-input, member exec, member ping, plus the AskUserQuestion three-beat workflow), Read reference/director.md.origin_task_id, Read reference/broadcast.md.member exec dispatch, serialization, cross-Director boundary), Read reference/exec-routing.md.reference/recovery.md.--full / --pretty / --json / --quiet opt-back-in semantics and CAFLEET_MAX_TEXT_LEN, Read reference/legacy-flags.md.If you are a member and your default Bash is denied on a specific command, the bash-via-Director fallback is in reference/exec-routing.md. If you are a Director, Read reference/director.md before spawning your first member.
For broadcast, member spawning, member capture, member ping, and member exec, see the reference files above.
Every cafleet invocation that touches agents or messages must carry two literal UUIDs as flags. There is no env-var fallback.
| Flag | Scope | Required for | Notes |
|---|---|---|---|
--session-id <uuid> | global (placed before the subcommand) | every client + member subcommand (register, send, broadcast, poll, ack, cancel, show, agent *, deregister, member *) | UUID of the session created via cafleet session create. Silently accepted (and ignored) on db init / session * / server / doctor. |
--agent-id <uuid> | per-subcommand (placed after the subcommand name) | every subcommand except register | The acting agent's UUID. register returns the new agent_id — record it and pass it to every subsequent command. |
If --session-id is missing on a subcommand that needs it, the CLI exits with Error: --session-id <uuid> is required for this subcommand. Create a session with 'cafleet session create' and pass its id.
Why literal flags, not env vars? Claude Code's
permissions.allowmatches Bash invocations as literal command strings. A literalcafleet --session-id <uuid> <subcmd> --agent-id <uuid>invocation matches a single allow pattern across every subcommand for that session. Shell-expansion patterns (export VAR=...then$VAR) break that matching and force per-invocation permission prompts that interrupt agent loops. Substitute the literal UUIDs printed bycafleet session createandcafleet agent register— never store them in shell variables.
The environment variables the CLI reads (all wired through cafleet.config.Settings via explicit validation_alias on each field, so the CAFLEET_ prefix is uniform):
CAFLEET_DATABASE_URL — SQLite database URL (optional; default builds sqlite:///<path> from ~/.local/share/cafleet/registry.db). Use an absolute path when overriding — SQLAlchemy does not expand ~ in SQLite URLs.CAFLEET_BROKER_HOST / CAFLEET_BROKER_PORT — defaults for cafleet server (127.0.0.1 / 8000).CAFLEET_MAX_TEXT_LEN — body truncation codepoint limit (default 200); see reference/legacy-flags.md.In every example below, substitute the literal UUID strings printed by cafleet session create / cafleet agent register. Angle-bracket tokens are placeholders, not shell variables:
<session-id> — the session UUID printed by cafleet session create<my-agent-id> — the UUID returned by your own cafleet ... agent register call<director-agent-id> — the Director's UUID (in your spawn prompt if you are a member)<target-agent-id> — the recipient of a unicast message<task-id> — the task UUID printed by message poll / message sendOnly --json, --pretty, --session-id, and --version are global (before the subcommand). --agent-id is a per-subcommand option and must appear after the subcommand name:
cafleet --session-id <session-id> --json agent register --name "My Agent" --description "..."
cafleet --session-id <session-id> --json agent list --agent-id <my-agent-id>
cafleet --session-id <session-id> --json --pretty message poll --agent-id <my-agent-id>
cafleet agent list --json will fail with No such option: --json. Same for --session-id placed after the subcommand — keep it before. --agent-id must come after the subcommand, not before it.
cafleet --version prints cafleet <version> and exits 0 without --session-id. --pretty switches JSON output from compact (the default) to indented; see reference/legacy-flags.md.
Two backends are supported: claude (default) and codex. The Director picks per member at create time via --coding-agent {claude,codex}. Both backends honor a leading-! shortcut on the input line, so cafleet member exec and message-send inline previews work uniformly. Operational details for codex members live in docs/codex-members.md.
Use --json so the output is machine-parseable, and capture agent_id for every subsequent call:
cafleet --session-id <session-id> --json agent register \
--name "<short-label>" \
--description "<one-sentence purpose>"
JSON response (field order is not guaranteed):
{"agent_id":"<uuid>","name":"<short-label>","registered_at":"<iso8601>"}
Rules:
Claude-A, reviewer-bot, …). Not test, foo, etc.agent_id immediately. It is required for every subsequent call; losing it forces re-registration.--json output prints Agent registered successfully! followed by agent_id: <uuid> and name: <name>. Parse the agent_id: line if --json is not an option.cafleet --session-id <session-id> agent deregister --agent-id <my-agent-id> at end of session so stale registrations do not accumulate.Reserved name —
Administrator: every session is auto-seeded with exactly one built-inAdministratoragent atsession createtime. Do NOT register a human or member agent under the nameAdministrator. The built-in Administrator is marked internally viaagent_card_json.cafleet.kind == "builtin-administrator"and is protected against deregister and Director placement (see Deregister below).
Send a message to a specific agent by ID.
cafleet --session-id <session-id> message send --agent-id <my-agent-id> \
--to <target-agent-id> --text "Did the API schema change?"
| Flag | Required | Notes |
|---|---|---|
--to <agent-id> | yes | Recipient agent UUID. |
--text <body> | yes | Message body. Truncated to CAFLEET_MAX_TEXT_LEN codepoints + … in the echoed response by default. |
--full | no | Disable body truncation; emit the full typed-column envelope. See reference/legacy-flags.md. |
--quiet | no | Emit only the new task id (8-char prefix). Useful in scripted loops. |
After persisting the message, the broker keystrokes a 2-line inline preview into the recipient's pane via tmux.send_inline_preview:
[cafleet msg <task_id_8> from <sender_8> <ts>]
<text-truncated-to-CAFLEET_MAX_TEXT_LEN>
The recipient's coding agent processes the keystroked text as a fresh user-turn input — no cafleet message poll invocation is in the auto-fire path. The recipient acks via cafleet message ack --task-id <task_id> once it has consumed the message. The notification is skipped when: the sender is the recipient (self-send), the recipient has no placement row or no tmux_pane_id, the pane is dead, or tmux is not on PATH. The message is always available in the queue regardless of notification outcome — recipients that miss an inline preview catch up on their next manual message poll (or via a Director-issued cafleet member ping).
Poll for incoming messages. Returns tasks addressed to this agent.
cafleet --session-id <session-id> message poll --agent-id <my-agent-id>
cafleet --session-id <session-id> message poll --agent-id <my-agent-id> --since "2026-03-28T12:00:00+00:00"
cafleet --session-id <session-id> message poll --agent-id <my-agent-id> --page-size 10
cafleet --session-id <session-id> message poll --agent-id <my-agent-id> --full
| Flag | Required | Notes |
|---|---|---|
--since <iso8601> | no | Filter tasks at or after this status_timestamp. |
--page-size <int> | no | Cap the number of returned tasks. |
--full | no | Disable body truncation; emit the full typed-column envelope for every task. |
--since accepts an ISO 8601 timestamp. The broker stores status_timestamp via datetime.now(UTC).isoformat(timespec='microseconds'), which renders as YYYY-MM-DDTHH:MM:SS.ffffff+00:00 (microsecond precision, +00:00 suffix — not Z). The filter is applied as a raw SQLite TEXT comparison, so pass timestamps in the same +00:00 form for correct lexicographic ordering.
Acknowledge receipt of a message. Moves the task from INPUT_REQUIRED to COMPLETED.
cafleet --session-id <session-id> message ack --agent-id <my-agent-id> --task-id <task-id>
cafleet --session-id <session-id> message ack --agent-id <my-agent-id> --task-id <task-id> --quiet
| Flag | Required | Notes |
|---|---|---|
--task-id <uuid> | yes | Task to acknowledge. |
--full | no | Disable body truncation in the echoed task. |
--quiet | no | Emit only the acked task id (8-char prefix). |
Cancel a sent message that has not been acknowledged yet. Only the sender can cancel.
cafleet --session-id <session-id> message cancel --agent-id <my-agent-id> --task-id <task-id>
| Flag | Required | Notes |
|---|---|---|
--task-id <uuid> | yes | Task to cancel. |
--full | no | Disable body truncation in the echoed task. |
Get details of a specific task by ID.
cafleet --session-id <session-id> message show --agent-id <my-agent-id> --task-id <task-id>
| Flag | Required | Notes |
|---|---|---|
--task-id <uuid> | yes | Task to fetch. |
--full | no | Disable body truncation; emit the full typed-column envelope. |
agent list returns all registered agents in the session. To fetch detail for a single agent, use agent show --id <target-agent-id>.
cafleet --session-id <session-id> agent list --agent-id <my-agent-id>
cafleet --session-id <session-id> agent show --agent-id <my-agent-id> --id <target-agent-id>
Default output is one row per agent (<id8> <name> <status>); description is truncated to 60 codepoints. Pass --full for the four-line per-agent block including the full agent_card_json blob — see reference/legacy-flags.md.
Print the calling pane's tmux session/window/pane identifiers (plus $TMUX_PANE) for operators diagnosing placement issues without reaching for raw tmux commands.
cafleet doctor
cafleet --json doctor
Does NOT require --session-id. Requires TMUX and TMUX_PANE env vars to be set (the standard tmux pane environment).
Remove this agent's registration from the broker.
cafleet --session-id <session-id> agent deregister --agent-id <my-agent-id>
Root Director cannot be deregistered. The agent created by
cafleet session create(the session'ssessions.director_agent_id) is protected —cafleet agent deregister --agent-id <root-director-id>exits 1 withError: cannot deregister the root Director; use 'cafleet session delete' instead.Usecafleet session delete <session-id>for session teardown.
Administrator cannot be deregistered. Passing the built-in Administrator's
agent_idtocafleet agent deregisterexits 1 withError: Administrator cannot be deregistered. The Administrator row staysactive; there is no override flag. Every session has exactly one Administrator; deregister regular agents only.
cafleet session delete <session-id>
# → Deleted session <session-id>. Deregistered N agents.
Soft-deletes a session in a single transaction: stamps sessions.deleted_at, deregisters every active agent in the session (root Director + Administrator + remaining members), and physically deletes every associated agent_placements row. Tasks are preserved. Idempotent.
After soft-delete, the session is hidden from cafleet session list and further cafleet --session-id <deleted> agent register calls fail with Error: session <id> is deleted. Surviving member coding-agent processes are not automatically closed — call cafleet member delete per member before cafleet session delete for a clean teardown. See the Shutdown Protocol in reference/recovery.md for the full ordering.
Verify pane env (Director / spawn-aware operator):
cafleet doctor
# tmux:
# session_name: <name>
# window_id: @<n>
# pane_id: %<n>
# TMUX_PANE: %<n>
Confirms the calling shell has TMUX and TMUX_PANE set. Reach for this BEFORE cafleet session create and BEFORE any cafleet member create call — it is the canonical pane-identity probe, replacing raw tmux display-message and TMUX / TMUX_PANE env-var expansion. See § Doctor for the subcommand's --session-id and env-var requirements, plus the --json variant.
Create a session (if one does not already exist):
cafleet session create --label "my-project"
# text output line 1: <session-id>; line 2: <root-director-agent-id>
cafleet session create --label "my-project" --json
# JSON: { "session_id": "...", "director": {...}, "administrator_agent_id": "..." }
Must be run inside a tmux session — outside tmux the command exits 1 with Error: cafleet session create must be run inside a tmux session and writes nothing.
Register with the broker:
cafleet --session-id <session-id> agent register \
--name "Code Review Agent" --description "Reviews pull requests"
# → returns <my-agent-id>
Discover other agents:
cafleet --session-id <session-id> agent list --agent-id <my-agent-id>
Send a message:
cafleet --session-id <session-id> message send --agent-id <my-agent-id> \
--to <target-agent-id> --text "Please review PR #42"
Poll for incoming messages:
cafleet --session-id <session-id> message poll --agent-id <my-agent-id>
Acknowledge received messages:
cafleet --session-id <session-id> message ack --agent-id <my-agent-id> --task-id <task-id>
Repeat steps 4–6 as needed. Use cafleet --session-id <session-id> --json <cmd> when parsing output programmatically; pair with --pretty when a human is reading the output.
For Director-side spawn / capture / exec / ping flows, see reference/director.md. For shutdown ordering, see reference/recovery.md.
Messages are modeled as tasks with this lifecycle:
For broadcast threading (the origin_task_id self-reference shape), see reference/broadcast.md.
--session-id on a client/member subcommand exits with Error: --session-id <uuid> is required for this subcommand. Create a session with 'cafleet session create' and pass its id. (exit 1).--agent-id on commands that need it exits with Error: Missing option '--agent-id'. (Click built-in, exit 2).cafleet --session-id <session-id> --json <cmd> for machine-parseable output (including errors).member commands require a tmux session (TMUX env var must be set) and exit 1 with Error: cafleet member commands must be run inside a tmux session if not.