{"id":50859331,"url":"https://github.com/nicknisi/fleet","last_synced_at":"2026-06-14T20:30:45.272Z","repository":{"id":360801644,"uuid":"1251775321","full_name":"nicknisi/fleet","owner":"nicknisi","description":"A terminal dashboard for managing multiple AI agent sessions in tmux","archived":false,"fork":false,"pushed_at":"2026-06-09T02:49:14.000Z","size":304,"stargazers_count":8,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-09T04:26:45.498Z","etag":null,"topics":["ai","ai-agents","claude-code","tmux"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/nicknisi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-05-27T22:47:11.000Z","updated_at":"2026-06-09T02:49:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/nicknisi/fleet","commit_stats":null,"previous_names":["nicknisi/fleet"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/nicknisi/fleet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicknisi%2Ffleet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicknisi%2Ffleet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicknisi%2Ffleet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicknisi%2Ffleet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nicknisi","download_url":"https://codeload.github.com/nicknisi/fleet/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nicknisi%2Ffleet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34337551,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-14T02:00:07.365Z","response_time":62,"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":["ai","ai-agents","claude-code","tmux"],"created_at":"2026-06-14T20:30:44.665Z","updated_at":"2026-06-14T20:30:45.260Z","avatar_url":"https://github.com/nicknisi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# fleet\n\nA terminal dashboard for managing multiple AI agent sessions in tmux.\n\nFleet watches your Claude Code sessions, shows which agents need attention, and lets you send prompts — all from a single pane. It replaces a sprawl of bash scripts with a compiled Bun binary and a Claude Code plugin that hooks into the event system automatically.\n\n\u003cimg width=\"3744\" height=\"2402\" alt=\"capture_20260527_180922\" src=\"https://github.com/user-attachments/assets/0ad4ea10-948f-4e64-a509-9ad7ca2a2db4\" /\u003e\n\n## Install\n\n### Homebrew\n\n```bash\nbrew install nicknisi/formulae/fleet\n```\n\n### From source\n\n```bash\ngit clone https://github.com/nicknisi/fleet.git\ncd fleet\nbun install\nbun run build\n# Binary is at dist/fleet — add to your PATH\n```\n\n### Claude Code plugin\n\nFleet hooks into Claude Code's event system to track agent state in real time. Install the plugin after building:\n\n```bash\nfleet install\n```\n\nThis does three things:\n\n1. Registers Fleet as a Claude Code plugin (hooks fire automatically in all new sessions)\n2. Adds a second tmux status row showing all active agents\n3. Adds a `run-shell` line to your tmux.conf (marked `# fleet-managed`)\n\nNo `settings.json` editing required.\n\nTo remove everything cleanly:\n\n```bash\nfleet uninstall\n```\n\n## Usage\n\n### TUI Dashboard\n\n```bash\nfleet                   # Launch dashboard (preview auto-opens on wide terminals)\nfleet --preview         # Force preview pane on\nfleet --no-preview      # Force preview pane off\n```\n\nThe dashboard shows every Claude Code pane grouped by urgency. Agents that need you sort to the top. Plain shell panes are hidden — you're here for the agents.\n\nEach row leads with the tmux session name. When Claude Code has auto-named a session (the descriptive title it generates per task), Fleet shows that name in the detail column so you can tell sessions apart at a glance. Filtering with `/` matches on session name, Claude name, and project path.\n\n### Keybindings\n\n| Key                        | Action                                 |\n| -------------------------- | -------------------------------------- |\n| `j` / `k` or `Up` / `Down` | Navigate sessions                      |\n| `Enter`                    | Switch to selected session             |\n| `n`                        | Jump to next waiting agent (cycles)    |\n| `p`                        | Toggle preview pane                    |\n| `s`                        | Send prompt to selected session        |\n| `i`                        | Enter passthrough (preview mode)       |\n| `y`                        | Approve permission prompt (preview)    |\n| `/`                        | Filter sessions by name or project     |\n| `x`                        | Kill selected session (confirms first) |\n| `?`                        | Help overlay                           |\n| `q` or `Esc`               | Quit (or clear filter)                 |\n\nYou can also **click** a session row to select it; clicking a `ready` agent acknowledges it in place (see [Acknowledge](#agent-states)).\n\n### Agent States\n\nFleet tracks seven states, sorted by urgency. The icon and color tell you what's happening at a glance:\n\n| Icon | State       | Meaning                                                         |\n| ---- | ----------- | --------------------------------------------------------------- |\n| `⚠`  | **waiting** | Tool approval needed (`[y/n]` prompt)                           |\n| `?`  | **asking**  | Agent asked you a question (`AskUserQuestion`)                  |\n| `◉`  | **working** | Thinking or running tools                                       |\n| `●`  | **ready**   | Turn ended — your move (finished, or asked in prose); green dot |\n| `●`  | **idle**    | Up but no recent activity (blue dot)                            |\n| `■`  | **shell**   | No agent running (hidden by default)                            |\n| `○`  | **down**    | No live process (hidden by default)                             |\n\n**asking vs. ready:** A turn that ends — whether the agent finished the task or asked you something in prose — looks identical at the hook layer (both are a plain `Stop`). Fleet can't tell them apart, so both land in **ready** (\"your move\"). The dedicated **asking** state is only reachable through structured signals the agent emits: the `AskUserQuestion` tool and MCP elicitation dialogs. Either way both sort into the attention tier, so nothing that needs you gets buried.\n\n### Send Mode\n\nPress `s` to send a prompt to the selected agent. Fleet auto-selects the first sendable session if the current one is busy or waiting for approval.\n\n**State gating:** Fleet refuses to send to sessions with permission prompts (won't accidentally approve), sessions asking questions (won't answer for you), or dead sessions. Use `--force` in the CLI to override the busy check.\n\n### Kill Session\n\nPress `x` to kill the selected session's pane. Fleet asks you to confirm (`y`) before closing it — any other key cancels.\n\n**State gating:** Same philosophy as send. Fleet only reaps sessions that are finished, idle, or already dead. It refuses to kill a working agent, one waiting on a permission prompt, or one asking a question — so you don't discard work or a pending decision by reflex.\n\n### Preview Pane\n\nPress `p` to toggle a live `tmux capture-pane` view of the selected session. Shows actual terminal output so you can verify state visually. Opens automatically on terminals wider than 120 columns.\n\nThe preview shows:\n\n- Live pane content (ANSI color preserved)\n- State badge and current tool\n- Listening ports (e.g., `⌁3000`)\n- Context-aware quick actions based on agent state\n\n**Resize the split:** Drag the divider between the session list and the preview with the mouse, just like dragging a tmux pane border. The divider highlights while you drag and the split clamps between 20% and 80%.\n\n### Quick Actions\n\nWhen the preview pane is open, Fleet shows context-aware actions at the bottom of the preview based on the agent's current state:\n\n- **waiting** — `y` to approve, `n` to deny the permission prompt\n- **asking** — `i` to answer inline via passthrough, `s` to send a prompt\n- **ready/idle** — `i` for passthrough, `s` to send the next prompt\n- **working** — `i` for passthrough (watch and interact)\n\n### Passthrough Mode\n\nPress `i` from the preview to enter passthrough mode. Every keystroke is forwarded directly to the agent's tmux pane — the preview updates live so you can see the result without leaving Fleet. Press `Esc` to exit back to the dashboard.\n\nThis is the power feature: approve prompts, answer questions, type commands, and watch the output — all without switching panes. The footer shows `● LIVE` when passthrough is active.\n\n## CLI Commands\n\nFleet also works as a non-interactive CLI for scripting and tmux integration.\n\n| Command                                   | Description                                                                        |\n| ----------------------------------------- | ---------------------------------------------------------------------------------- |\n| `fleet status [--tmux] \u003csession\u003e`         | Query agent state. `--tmux` outputs a tmux format string for status bars.          |\n| `fleet status --statusline`               | Render a full multi-agent status line for tmux's second row.                       |\n| `fleet next`                              | Switch to the next waiting agent pane (cycles through PERMIT \u003e QUESTION \u003e DONE).   |\n| `fleet switch \u003cpane-id\u003e`                  | Acknowledge a ready agent and switch to it (used by the statusline click binding). |\n| `fleet ack \u003cpane-id\u003e`                     | Acknowledge a ready agent in place (clear it from the attention tier, no switch).  |\n| `fleet send \u003csession\u003e \u003cprompt\u003e`           | Send a prompt to a session. Refuses unsafe states unless `--force`.                |\n| `fleet doctor`                            | Check tmux version, plugin installation, status directories, hook health.          |\n| `fleet reconcile [--dry-run] [--verbose]` | Remove orphan status files for dead panes, fix stale working states.               |\n| `fleet install`                           | Register Fleet as a Claude Code plugin + add second tmux status row.               |\n| `fleet uninstall`                         | Remove plugin registration + tmux status row.                                      |\n| `fleet statusline --inject`               | Manually add the second tmux status row.                                           |\n| `fleet statusline --remove`               | Manually remove the second tmux status row.                                        |\n\n### Tmux Status Bar Integration\n\nFleet supports two levels of tmux integration:\n\n**Second status row (recommended):** A dedicated row showing all agents that need attention, with clickable entries. Set up automatically by `fleet install`, or manually:\n\n```bash\nfleet statusline --inject\n```\n\nEach entry is clickable (tmux 3.2+). **Left-click** an agent name to switch to that session; **right-click** to mark it read in place without switching. When any agent is ready, a `✕ clear` chip appears at the end of the row — click it to dismiss every ready agent at once. Only agents whose turn it is for you appear: PERMIT (tool approval), QUESTION (a question to answer), and DONE/ready (finished, waiting on your next move). Working and idle sessions stay out of the bar — they don't need you to act, so they'd just be noise. Watch those in the dashboard instead.\n\n(After upgrading Fleet, re-run `fleet statusline --inject` to pick up the right-click binding and clear chip.)\n\n**Status-right icon (lightweight):** A single icon in your existing status bar:\n\n```\nset -g status-right '#(fleet status --tmux #{session_name})'\n```\n\nShows a colored icon when agents in the current session need attention. Empty otherwise.\n\n### Tmux Keybindings\n\n```\nbind-key y display-popup -E -w 90% -h 70% \"fleet\"\nbind-key n run-shell \"fleet next\"\n```\n\n## Architecture\n\nFleet has two halves that talk through the filesystem:\n\n```\nHooks (bash, fast)              TUI (Bun, interactive)\n──────────────────              ──────────────────────\nClaude Code fires events   ──\u003e  Reads status files\n  Notification                   Reads JSONL event logs\n  PreToolUse                     Scrapes pane content\n  Stop / SubagentStop            Fuses into 7-state model\n  SessionEnd                     Renders dashboard\n       │                                │\n       ▼                                ▼\n  ~/.cache/claude-status/       Single-string ANSI frames\n    {pane_num}.status            (no flicker, no framework)\n    {pane_num}.events.jsonl\n```\n\n### Three-Layer State Engine\n\nFleet doesn't trust any single signal. It fuses three layers for high-confidence state:\n\n1. **Hook signals** (Layer 1, ~0ms) — Claude Code hooks write JSON status files on every event. Fast but can disagree with reality.\n\n2. **JSONL event stream** (Layer 2) — Each hook appends to a per-pane event log. The TUI reads only the last event. Key insight: a `stop_reason` of `tool_use` means the agent is about to run another tool (BUSY), while `end_turn` means actually done.\n\n3. **Pane scraping** (Layer 3, ~50ms) — `tmux capture-pane` as the visual arbiter. Detects permission prompts (`[y/n]`), question dialogs (`Enter to select`), the working token counter (`(1m 11s · ↓ 3.4k tokens)`), and idle prompts. The scraper is authoritative only for what it can read unambiguously off the screen — `PERMIT` and `QUESTION` always win. For working-vs-idle it defers to the hooks: a scraper miss (Claude's spinner animates between capture frames) can't downgrade a fresh `working` hook to idle, but a scraped idle prompt does clear a stale permission.\n\n**Freshness invariant:** A state transition is only accepted if its timestamp is newer than the current state's timestamp. Prevents out-of-order hook deliveries from causing flicker.\n\n**Verify on switch:** When you navigate to a pane (Enter or click), Fleet scrapes it immediately and updates the status file. Stale states get corrected the moment you look at them.\n\n**Acknowledge:** Once you've seen a `ready` agent it drops to `idle` and leaves the attention tier (and the statusline). Ways to acknowledge:\n\n- **Click it in the dashboard** — acknowledges in place, so you can clear several finished agents without leaving Fleet.\n- **Switch to it** (Enter, or left-click its statusline entry) — acknowledges, then takes you there.\n- **Right-click its statusline entry** — acknowledges in place, without switching.\n- **Click the `✕ clear` chip** at the end of the statusline — acknowledges every ready agent at once.\n- **`fleet ack \u003cpane\u003e`** — from the CLI, for scripting or bulk-clearing.\n\nA ready agent's completion can come from two independent places: the hook status file (`done`/`completed`) or an event-derived turn-end (a `Stop`/`SubagentStop` the status file may not reflect yet — the bar shows `ready` from the event stream while the file lags at `idle`). Acknowledgement retires both: it flips a ready status file to `idle`, and when the event stream shows a completion it appends an `Acknowledged` event so the derived `ready` can't re-assert. It survives Fleet restarts with no separate store. So: green `ready` = needs your eyes; blue `idle` = seen, nothing pending.\n\n**Decay:** `ready` never auto-decays — a finished turn is waiting on you and stays until you act on it (switch to it, send a prompt, or it starts working again). Only `working` times out to `idle`, after 3 minutes, so a crashed turn doesn't spin forever.\n\n### Hook Details\n\nThe Claude Code plugin (`hooks/`) fires on five events:\n\n- **Notification** — Splits into three sub-types: `permission_prompt` → permit, `elicitation_dialog` → question, `idle_prompt` → ready\n- **PreToolUse** — Agent is running a tool (working). The `AskUserQuestion` tool is the exception — it means the agent is asking _you_, so it maps to **asking**, not working.\n- **Stop** — Agent stopped. `tool_use` stop reason = still working. `end_turn` = turn over (ready). Background tasks suppress completion. 3-second grace period.\n- **SubagentStop** — Subagent finished; parent keeps working\n- **SessionEnd** — Cleanup status and event files\n\nEach hook script sources `hooks/lib.sh` which handles status file writes, JSONL event appends, and tmux notifications (with self-notification suppression).\n\n### Performance\n\nThe TUI separates cheap and expensive operations:\n\n- **Every 500ms:** Re-read `.status` files + one `tmux list-panes` call + JSONL last-line read. No subprocesses beyond that.\n- **Every 5s:** Refresh git branches (`git rev-parse` per unique path), port detection (`lsof`), and pane scraping (`tmux capture-pane` per pane, ~50ms each).\n- **On keypress:** Zero subprocess calls. Just redraws from cached state.\n- **On switch:** Scrapes the target pane and corrects the status file before switching. Stale states are fixed the moment you navigate to them.\n- **During send/filter:** All refresh timers pause. The event loop is yours.\n- **JSONL reads:** Only the last line is parsed (not the entire file).\n\n## Agent Configuration\n\nFleet reads agent directories from (in priority order):\n\n1. `~/.config/fleet/agents.json` (new format)\n2. `~/.config/agent-status/agents.conf` (legacy format)\n3. Hardcoded fallback: `~/.cache/claude-status` + `~/.cache/pi-status`\n\n### New format (`agents.json`)\n\n```json\n{\n  \"agents\": [\n    { \"name\": \"claude\", \"statusDir\": \"~/.cache/fleet/claude\" },\n    { \"name\": \"codex\", \"statusDir\": \"~/.cache/fleet/codex\" }\n  ]\n}\n```\n\n### Legacy format (`agents.conf`)\n\n```ini\n# name=directory\nclaude=$HOME/.cache/claude-status\npi=$HOME/.cache/pi-status\n```\n\n## Development\n\nFleet is a zero-dependency Bun project.\n\n```bash\nbun install              # Install dev dependencies\nbun run dev              # Run without compiling\nbun run build            # Compile to standalone binary (dist/fleet)\nbun test                 # Run tests (166 tests, ~50ms)\nbun run typecheck        # tsc --noEmit\nbun run lint             # oxlint\nbun run format           # oxfmt\nbun run format:check     # oxfmt --check\n```\n\n### Testing\n\nTests are collocated (`*.test.ts` next to source). The state engine, ANSI utilities, TUI model, and CLI commands are unit-tested. Tmux-dependent code has integration-style tests that gracefully degrade outside tmux.\n\n```bash\nbun test                 # 166 tests, ~50ms\nbun test src/state/      # State engine only\nbun test src/terminal/   # Terminal primitives only\nbun test src/tui/        # TUI model only\nbun test src/cli/        # CLI commands\n```\n\n## Demo video\n\nhttps://github.com/user-attachments/assets/2a9ce8db-767e-4260-a0e4-0a61562acef7\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnicknisi%2Ffleet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnicknisi%2Ffleet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnicknisi%2Ffleet/lists"}