{"id":49342106,"url":"https://github.com/himkt/cafleet","last_synced_at":"2026-04-27T04:32:49.478Z","repository":{"id":347689760,"uuid":"1194272239","full_name":"himkt/cafleet","owner":"himkt","description":"☎️ A2A communication, even for Coding Agent","archived":false,"fork":false,"pushed_at":"2026-04-13T13:27:12.000Z","size":1124,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-13T14:27:30.391Z","etag":null,"topics":["a2a","a2a-protocol","agent","coding-agent","mcp","mcp-server","skills"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/himkt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-28T05:56:46.000Z","updated_at":"2026-04-13T13:27:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/himkt/cafleet","commit_stats":null,"previous_names":["himkt/hikyaku","himkt/cafleet"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/himkt/cafleet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himkt%2Fcafleet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himkt%2Fcafleet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himkt%2Fcafleet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himkt%2Fcafleet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/himkt","download_url":"https://codeload.github.com/himkt/cafleet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/himkt%2Fcafleet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32323214,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-26T23:26:28.701Z","status":"online","status_checked_at":"2026-04-27T02:00:06.769Z","response_time":128,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["a2a","a2a-protocol","agent","coding-agent","mcp","mcp-server","skills"],"created_at":"2026-04-27T04:32:47.595Z","updated_at":"2026-04-27T04:32:49.464Z","avatar_url":"https://github.com/himkt.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CAFleet\n\nA2A-inspired message broker and agent registry for coding agents.\n\n\u003e **CAFleet is a local-only tool.** It is designed to run on a single developer machine and does not perform authentication. Do not expose the broker on a shared network unless you accept that every listener can see and act within every session.\n\nCAFleet enables ephemeral agents -- such as Claude Code sessions, CI/CD runners, and other coding agents -- to discover each other and exchange messages. All CLI commands access SQLite directly through a shared `broker` module -- no HTTP server is needed for agent operations. Agents are organized into **sessions** identified by a non-secret `session_id` created via `cafleet session create`. Agents sharing the same session can discover and message each other; agents in different sessions are invisible to one another.\n\n## Features\n\n- **Agent Registry** -- Register, discover, and deregister agents via CLI\n- **Session Isolation** -- A `session_id` defines a session boundary; cross-session agents are fully invisible to each other\n- **Auto-bootstrap root Director on session create** -- `cafleet session create` runs a single transaction that inserts the session row, registers a hardcoded root Director (`name=\"Director\"`) with an `agent_placements` row pointing at the current tmux pane, back-fills `sessions.director_agent_id`, and seeds the built-in Administrator. Must be run inside a tmux session — the CLI fails fast with `Error: cafleet session create must be run inside a tmux session` (exit 1) otherwise. The root Director's placement has `director_agent_id=NULL` to indicate \"no parent\"; this is what allows Member → Director tmux push notifications to work out of the box. `cafleet deregister` refuses the root Director (use `cafleet session delete` instead)\n- **Soft-delete sessions** -- `cafleet session delete \u003cid\u003e` is a single-transaction logical delete: stamps `sessions.deleted_at`, sweeps every active agent in the session (root Director included) to `status='deregistered'`, and physically deletes their `agent_placements` rows. Tasks are preserved (audit trail). Idempotent — re-running against an already-deleted session prints `Deregistered 0 agents.` and exits 0. Soft-deleted sessions are hidden from `cafleet session list` and rejected by `cafleet register` with `Error: session \u003cid\u003e is deleted`. Surviving member tmux panes are intentionally orphaned — call `cafleet member delete` per member first for a clean teardown\n- **Built-in Administrator agent** -- `cafleet session create` auto-seeds exactly one built-in `Administrator` agent per session (marked via `agent_card_json.cafleet.kind == \"builtin-administrator\"`); the Admin WebUI always sends from this identity. The broker rejects deregister and placement operations targeting an Administrator (`AdministratorProtectedError` currently surfaces as `Error: ...` + exit 1 in the CLI; WebUI HTTP 409 mapping is reserved for a future deregister endpoint/handler) and filters Administrators out of broadcast recipient sets so they are write-only identities. Alembic revision `0006` backfills one Administrator into each pre-existing session on `cafleet db init`\n- **Unicast Messaging** -- Send messages to a specific agent by ID (same-session only)\n- **Broadcast Messaging** -- Send messages to all agents in the same session\n- **Inbox Polling** -- Agents poll for new messages at their own pace; supports delta polling via `statusTimestampAfter`\n- **Message Lifecycle** -- Acknowledge, cancel (retract), and track message status\n- **Session-Based Routing** -- `session_id` + `agent_id` (identity) parameters on all operations; no authentication or bearer tokens\n- **WebUI** -- Browser-based dashboard; session picker at `/ui/#/sessions`, then a Discord-style unified timeline per session (sidebar of active/deregistered agents, message timeline with broadcasts collapsed to one entry + per-recipient ACK reactions on hover, and a multi-line `@\u003cagent\u003e` / `@all` textarea input with Discord-style autocomplete popover). Every message is sent as the built-in Administrator — a fixed read-only `Sending as Administrator` label replaces the old sender dropdown. Typing `@` opens a popover of matching active agents (plus virtual `@all`); ArrowUp/Down navigate, Enter/Tab insert, Esc dismisses. Enter sends, Shift+Enter inserts a newline, and IME composition (Japanese/Chinese candidates) never triggers an accidental submit. Newlines in message bodies are preserved end-to-end and rendered on multiple lines in the timeline\n- **Member Lifecycle** -- `cafleet member create/delete/list/capture/send-input` commands wrap tmux pane spawning + agent registration into atomic operations; the `agent_placements` table persists the agent-to-pane mapping in the registry. `member send-input` is a safe `tmux send-keys` wrapper for answering an `AskUserQuestion` prompt (digit 1/2/3 or free text) that mirrors the `capture` authorization boundary. Director-side usage is AskUserQuestion-delegated: the Director asks the user via its own `AskUserQuestion` call and then invokes the resolved command via Bash — see [`skills/cafleet/SKILL.md`](skills/cafleet/SKILL.md) \"Answer a member's AskUserQuestion prompt\" for the canonical three-beat workflow and pane-shapes table\n- **Multi-Runner Support** -- `--coding-agent claude|codex` flag on `member create` selects which coding agent to spawn; defaults to `claude` for backward compatibility. Codex runs with `--approval-mode auto-edit`\n- **tmux Push Notifications** -- After persisting a message, the broker injects a `cafleet poll` command into each recipient's tmux pane via `tmux send-keys` for near-instant delivery. Best-effort: self-sends are skipped, missing/dead panes fail silently, and the message queue remains the source of truth\n- **Director Monitoring Skill** -- `.claude/skills/cafleet-monitoring/SKILL.md` defines mandatory supervision protocol for Directors: 2-stage health check (poll inbox → capture terminal), spawn protocol, stall response, and a `/loop` prompt template\n- **Design Document Orchestration Skills** -- `.claude/skills/design-doc-create/` and `.claude/skills/design-doc-execute/` replicate the global `/design-doc-create` and `/design-doc-execute` workflows using CAFleet primitives (register + `cafleet send` + `cafleet member create`). Every inter-agent message is persisted in SQLite and visible in the admin WebUI timeline. A plugin-local `design-doc` template skill (copy of the global `/design-doc`) keeps the plugin self-contained. Exposed as `/cafleet:design-doc-create` and `/cafleet:design-doc-execute` to other projects via the `cafleet` plugin\n- **Unified CLI** -- Single `cafleet` command for all operations: server admin (`db init`, `session`), agent messaging (`register`, `send`, `poll`, `ack`), and member lifecycle (`member create/delete/list/capture/send-input`)\n- **SQLite Storage** -- Single-file database; no daemon required. Schema managed by Alembic via `cafleet db init`\n\n## Architecture\n\n```\nCLI (click)  ──→  broker.py (sync SQLAlchemy)  ──→  SQLite\n                                                      ↑\nAdmin WebUI  ──→  server.py (minimal FastAPI)         |\n                  +- webui_api.py  ──→  broker.py  ───+\n                  +- static files (/ui/)\n```\n\nKey design decisions:\n\n- **Direct SQLite access**: CLI commands call `broker.py` directly — no HTTP server needed for agent operations. The FastAPI server is only used for the admin WebUI.\n- The `session_id` is the session boundary. Sessions are created via `cafleet session create` and are non-secret identifiers for organizing agents. All agents registered with the same session form one session.\n- The `contextId` field is set to the recipient's agent ID on every delivery task, enabling inbox discovery via `broker.poll_tasks(agent_id=myAgentId)`.\n- Task states map to message lifecycle: `input_required` (unread), `completed` (acknowledged), `canceled` (retracted), `failed` (routing error).\n- Sessions are created via `cafleet session create`, which must be run inside a tmux session and atomically bootstraps the session + root Director + placement + Administrator in one transaction. Deleting a session via `cafleet session delete` is a soft-delete: the row is stamped with `deleted_at`, all agents are deregistered, and their placements are physically removed — tasks are preserved.\n- The WebUI requires no login. A session picker at `/ui/#/sessions` lets the user select which session to view.\n- **Storage layer**: All data is persisted in a single SQLite file (`~/.local/share/cafleet/registry.db` by default). Indexed fields are columns; task payloads are stored as JSON blobs. `PRAGMA busy_timeout=5000` handles concurrent access. No physical cleanup loop -- deregistered agents and tasks persist forever and are invisible to normal traffic via `status='active'` filters.\n- **tmux push notifications**: After persisting a message, the broker looks up the recipient's `agent_placements` row and, if a tmux pane is available, injects `cafleet --session-id \u003csession-id\u003e poll --agent-id \u003crecipient-agent-id\u003e` via `tmux send-keys`. This is best-effort -- failures are silent, and the queue remains the source of truth. Unicast responses include `notification_sent`; broadcast summaries include `notifications_sent_count`.\n\n## Quick Start\n\n### Prerequisites\n\n- Python 3.12+\n- SQLite (built into Python; no daemon needed)\n- [uv](https://docs.astral.sh/uv/)\n- [mise](https://mise.jdx.dev/) (for the documented `mise //cafleet:*` workflow)\n\n### Initialize the Schema (one-time)\n\nBefore starting the server for the first time, apply the database schema:\n\n```bash\ncafleet db init\n```\n\nThis command is idempotent -- running it on a database that is already at head is a no-op. The database file is created at `~/.local/share/cafleet/registry.db` by default. Override with `CAFLEET_DATABASE_URL` (e.g. `sqlite:////var/lib/cafleet/registry.db`).\n\n### Create a Session\n\nBefore spawning any members, create a session. The command **must be run inside a tmux session** — it reads the caller's tmux context (`session`, `window_id`, `pane_id`) and bakes it into the root Director's placement row so the Director can receive Member → Director tmux push notifications.\n\n```bash\ncafleet session create --label \"my-project\"\n```\n\nNon-JSON output (line 1 is the `session_id`, line 2 is the root Director's `agent_id`):\n\n```\n550e8400-e29b-41d4-a716-446655440000\n7ba91234-5678-90ab-cdef-112233445566\nlabel:            my-project\ncreated_at:       2026-04-16T08:50:00+00:00\ndirector_name:    Director\npane:             main:@3:%0\nadministrator:    3c4d5e6f-7890-1234-5678-90abcdef1234\n```\n\nJSON output:\n\n```bash\ncafleet session create --label \"my-project\" --json\n```\n\n```json\n{\n  \"session_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n  \"label\": \"my-project\",\n  \"created_at\": \"2026-04-16T08:50:00+00:00\",\n  \"administrator_agent_id\": \"3c4d5e6f-7890-1234-5678-90abcdef1234\",\n  \"director\": {\n    \"agent_id\": \"7ba91234-5678-90ab-cdef-112233445566\",\n    \"name\": \"Director\",\n    \"description\": \"Root Director for this session\",\n    \"registered_at\": \"2026-04-16T08:50:00+00:00\",\n    \"placement\": {\n      \"director_agent_id\": null,\n      \"tmux_session\": \"main\",\n      \"tmux_window_id\": \"@3\",\n      \"tmux_pane_id\": \"%0\",\n      \"coding_agent\": \"unknown\",\n      \"created_at\": \"2026-04-16T08:50:00+00:00\"\n    }\n  }\n}\n```\n\nOutside tmux the command fails fast with `Error: cafleet session create must be run inside a tmux session` and exit 1 — no DB rows are written. All five writes (session, Director agent, Director placement, back-fill `sessions.director_agent_id`, Administrator agent) run in a single `with session.begin():` block, so any failure rolls back the whole thing.\n\nCapture the printed `session_id` and pass it as `--session-id \u003csession-id\u003e` (a global flag, placed before the subcommand) on every subsequent command. CLI commands access SQLite directly — no server needed. Start `cafleet server` (or `mise //cafleet:dev` from a repo clone) only if you want the admin WebUI.\n\nThe root Director is the session's built-in team lead: `cafleet deregister --agent-id \u003cdirector_agent_id\u003e` is rejected with `Error: cannot deregister the root Director; use 'cafleet session delete' instead.`. Use `cafleet session delete` when you want to tear down the session.\n\nThe built-in `Administrator` agent is seeded in the same transaction. The Admin WebUI always sends from this identity; the broker rejects deregister and placement operations targeting it. Pre-existing sessions are backfilled on `cafleet db init` via Alembic revision `0006`.\n\n### Delete a Session\n\n```bash\ncafleet session delete 550e8400-e29b-41d4-a716-446655440000\n# → Deleted session 550e8400-e29b-41d4-a716-446655440000. Deregistered 3 agents.\n```\n\n`session delete` is a single-transaction logical delete that stamps `deleted_at`, deregisters every active agent (root Director included), and physically deletes every associated `agent_placements` row. Tasks are preserved (audit trail). The command is idempotent — re-running against an already-deleted session prints `Deregistered 0 agents.` and exits 0.\n\nMember tmux panes that were spawned via `cafleet member create` are **not** automatically closed by `session delete`. For a clean teardown, call `cafleet member delete` on each member first (which sends `/exit`), then call `session delete`. If a member pane refuses to close (e.g. blocked on a confirmation prompt), rerun `cafleet member delete` with `--force`, which kill-panes the target, sweeps the placement, and rebalances the layout.\n\n\u003e **Why a literal flag, not an env var?** Claude Code's `permissions.allow` matches Bash invocations as literal command strings. Passing `--session-id \u003cliteral-uuid\u003e` lets a single allow-list pattern match every subcommand for that session; shell-expansion patterns (`export VAR=...` followed by `$VAR` substitution) break that matching and force per-invocation permission prompts. Substitute the literal UUIDs printed by `cafleet session create` and `cafleet register` — do not introduce shell variables to hold them.\n\n### Register an Agent\n\n```bash\ncafleet --session-id 550e8400-e29b-41d4-a716-446655440000 register \\\n  --name \"my-agent\" --description \"A coding assistant\"\n# → prints: 7ba91234-5678-90ab-cdef-112233445566\n```\n\nSave the returned `agent_id` for subsequent commands.\n\n### Send a Message\n\n```bash\ncafleet --session-id \u003csession-id\u003e send --agent-id \u003cyour-agent-id\u003e \\\n  --to \u003crecipient-agent-id\u003e --text \"Hello from my agent\"\n```\n\n### Poll for Messages\n\n```bash\ncafleet --session-id \u003csession-id\u003e poll --agent-id \u003cyour-agent-id\u003e\n```\n\n### Acknowledge a Message\n\n```bash\ncafleet --session-id \u003csession-id\u003e ack --agent-id \u003cyour-agent-id\u003e --task-id \u003ctask-id\u003e\n```\n\n### Start the Admin WebUI (optional)\n\n```bash\n# Defaults to 127.0.0.1:8000 (settings.broker_host / settings.broker_port)\ncafleet server\n\n# Override via flags\ncafleet server --host 0.0.0.0 --port 9000\n\n# Or via env vars (same values pydantic-settings resolves for settings.broker_host / broker_port)\nCAFLEET_BROKER_HOST=0.0.0.0 CAFLEET_BROKER_PORT=9000 cafleet server\n```\n\n`cafleet server` launches the admin WebUI FastAPI app via uvicorn. It does not require `--session-id`; a `--session-id` flag is silently accepted if present. CLI commands (messaging, member lifecycle, `db init`, `session *`) do not need this server. If the WebUI dist directory is missing, startup emits a one-line warning to stderr and `/ui/` returns 404 until you run `mise //admin:build` (see the Build the WebUI section below).\n\n## CLI Usage\n\nThe unified `cafleet` CLI handles both server administration and agent operations.\n\nGlobal flags (placed **before** the subcommand):\n\n| Flag | Required | Description |\n|---|---|---|\n| `--session-id \u003cid\u003e` | Yes (for client + member subcommands) | Session identifier for agent routing (opaque string — new sessions get a UUIDv4, migrated sessions reuse a 64-char hex value). Required for `register`, `send`, `broadcast`, `poll`, `ack`, `cancel`, `get-task`, `agents`, `deregister`, `member *`. Silently accepted (and ignored) on `db init` / `session *`. |\n| `--json` | No | Emit JSON output. |\n| `--version` | No | Print `cafleet \u003cversion\u003e` and exit 0. Bypasses the `--session-id` requirement. Sourced from the installed package metadata via `importlib.metadata`. |\n\nConfiguration via environment variables:\n\n| Variable | Required | Description |\n|---|---|---|\n| `CAFLEET_DATABASE_URL` | No | SQLite database URL. Default builds `sqlite:///\u003cpath\u003e` from `~/.local/share/cafleet/registry.db` with `~` expanded at load time. When setting this env var yourself, use an absolute path (SQLAlchemy does not expand `~` in SQLite URLs). |\n\nThe `--agent-id` option is a per-subcommand option required by most agent commands. CLI commands access SQLite directly -- no running server is required.\n\n### Server Administration\n\n| Command | Description |\n|---|---|\n| `cafleet db init` | Apply Alembic migrations to bring the schema to head (idempotent) |\n| `cafleet session create [--label TEXT] [--json]` | Create a new session + bootstrap the root Director + Administrator in one transaction (must run inside tmux); prints the session_id, the Director's agent_id, and the Administrator's agent_id |\n| `cafleet session list` | List non-soft-deleted sessions with agent counts |\n| `cafleet session show \u003cid\u003e` | Show details of a single session |\n| `cafleet session delete \u003cid\u003e` | Soft-delete a session (stamps `deleted_at`, deregisters all agents, removes placements; tasks preserved; idempotent) |\n\n`cafleet db init` must be run once before the server starts. It handles six database states: missing file (creates it), empty schema, at head (no-op), behind head (upgrades), ahead of head (error), and legacy tables without Alembic version (error with manual instructions).\n\n### Agent Commands\n\nAll commands below require the global `--session-id \u003cuuid\u003e` flag (placed before the subcommand). The `--agent-id` column indicates whether the per-subcommand `--agent-id \u003cuuid\u003e` flag is also required.\n\n| Command | `--agent-id` | Description |\n|---|---|---|\n| `cafleet --session-id \u003cid\u003e register` | Not required | Register a new agent; returns an agent ID |\n| `cafleet --session-id \u003cid\u003e send --agent-id \u003cid\u003e` | Required | Send a unicast message to another agent in the same session |\n| `cafleet --session-id \u003cid\u003e broadcast --agent-id \u003cid\u003e` | Required | Broadcast a message to all agents in the same session |\n| `cafleet --session-id \u003cid\u003e poll --agent-id \u003cid\u003e` | Required | Poll inbox for incoming messages |\n| `cafleet --session-id \u003cid\u003e ack --agent-id \u003cid\u003e` | Required | Acknowledge receipt of a message |\n| `cafleet --session-id \u003cid\u003e cancel --agent-id \u003cid\u003e` | Required | Cancel (retract) a sent message before it is acknowledged |\n| `cafleet --session-id \u003cid\u003e get-task --agent-id \u003cid\u003e` | Required | Get details of a specific task/message |\n| `cafleet --session-id \u003cid\u003e agents --agent-id \u003cid\u003e` | Required | List agents in the session or get detail for a specific agent |\n| `cafleet --session-id \u003cid\u003e deregister --agent-id \u003cid\u003e` | Required | Deregister this agent from the broker |\n| `cafleet --session-id \u003cid\u003e member create --agent-id \u003cid\u003e` | Required | Register a member agent and spawn its tmux pane (Director only). `--coding-agent claude\\|codex` selects the backend (default: `claude`) |\n| `cafleet --session-id \u003cid\u003e member delete --agent-id \u003cid\u003e` | Required | Deregister a member and close its pane (Director only) |\n| `cafleet --session-id \u003cid\u003e member list --agent-id \u003cid\u003e` | Required | List members spawned by this Director |\n| `cafleet --session-id \u003cid\u003e member capture --agent-id \u003cid\u003e` | Required | Capture the last N lines of a member's pane (Director only) |\n| `cafleet --session-id \u003cid\u003e member send-input --agent-id \u003cid\u003e` | Required | Forward a restricted keystroke (`--choice {1,2,3}` or `--freetext \"\u003ctext\u003e\"`) to a member's pane (Director only); see [docs/spec/cli-options.md](docs/spec/cli-options.md#member-send-input). Director-side workflow is AskUserQuestion-delegated — see [`skills/cafleet/SKILL.md`](skills/cafleet/SKILL.md) \"Answer a member's AskUserQuestion prompt\" for the canonical three-beat shape |\n\n## API Overview\n\n### WebUI API\n\nThe admin WebUI is available when the server is running (`cafleet server`, or `mise //cafleet:dev` from a repo clone). CLI commands do not use the server.\n\n### Message Lifecycle\n\n| Task State | Meaning |\n|---|---|\n| `input_required` | Message queued, awaiting recipient pickup (unread) |\n| `completed` | Message acknowledged by recipient |\n| `canceled` | Message retracted by sender before ACK |\n| `failed` | Routing error (returned immediately to sender) |\n\n## Tech Stack\n\n- **Python 3.12+** managed with uv\n- **Server**: FastAPI + SQLAlchemy + Alembic + Pydantic + pydantic-settings (WebUI only)\n- **CLI**: click (direct SQLite via `broker` module)\n- **WebUI**: Vite + React 19 + TypeScript + Tailwind CSS 4\n\n## Project Structure\n\n```\ncafleet/                    # Repository root\n  cafleet/                  # cafleet package (server + CLI)\n    src/cafleet/\n      broker.py             # Single data access layer (sync SQLAlchemy)\n      server.py             # Minimal FastAPI app (WebUI only)\n      cli.py                # Unified CLI (db, session, agent, member commands)\n      config.py             # Settings via pydantic-settings\n      db/                   # SQLAlchemy models, engine, Alembic env\n      alembic/              # Alembic migration scripts (versions/)\n      alembic.ini           # Alembic config (bundled into wheel)\n      tmux.py               # tmux subprocess helper (member lifecycle)\n    tests/\n    pyproject.toml\n    mise.toml\n  admin/                    # WebUI SPA (Vite + React + TypeScript + Tailwind CSS)\n  docs/\n    spec/                   # API and data model specifications\n      data-model.md\n      webui-api.md\n      cli-options.md\n  ARCHITECTURE.md           # System architecture and design decisions\n```\n\n## Development\n\n```bash\n# Clone the repository\ngit clone https://github.com/himkt/cafleet.git\ncd cafleet\n\n# Install dependencies\nmise //cafleet:sync\n\n# Install the `cafleet` CLI as an editable uv tool\n# Source edits under cafleet/src/cafleet/ take effect with no second reinstall.\n# Re-run this task if you previously installed `cafleet` without `--editable`.\nmise //cafleet:install\n\n# Initialize the database schema (one-time)\ncafleet db init\n\n# Run tests\nmise //cafleet:test\n```\n\n### Build the WebUI\n\nThe broker serves the SPA at `/ui/`, but the build is a separate manual step so backend-only contributors are not forced to install bun. Run these two commands in order:\n\n```bash\n# 1. Build the SPA into cafleet/src/cafleet/webui/\nmise //admin:build\n\n# 2. Start the broker — it serves the freshly built SPA at http://127.0.0.1:8000/ui/\n#    Pick whichever invocation matches your workflow:\nmise //cafleet:dev           # from a repo clone; runs `uv run uvicorn cafleet.server:app --host 127.0.0.1 --port 8000`\ncafleet server               # packaged launcher (same FastAPI app, --host/--port flags)\n```\n\nBoth `cafleet server` and `mise //cafleet:dev` run the same `cafleet.server:app` FastAPI app on uvicorn without `--reload`; they are independent entry points. `mise //cafleet:dev` calls uvicorn directly (no delegation to `cafleet server`) so contributors can restart manually between edits.\n\nIf step 1 is skipped, the server still starts; `create_app()` emits `warning: admin WebUI is not built. /ui/ will return 404. Run 'mise //admin:build'.` to stderr and `/ui/` 404s until you run `mise //admin:build`. Note: the server is only needed for the WebUI — CLI commands work without it.\n\n**Release maintainers**: run `mise //admin:build` before any `uv build`. The wheel only includes whatever is currently sitting in `cafleet/src/cafleet/webui/`, so a stale or missing build will produce a wheel without the SPA. After building, verify the wheel contents with `unzip -l dist/cafleet-*.whl | grep webui/index.html`.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhimkt%2Fcafleet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhimkt%2Fcafleet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhimkt%2Fcafleet/lists"}