Skip to content

Repository files navigation

🛰 Fleet

Parallel-agent compatibility for Claude Code

Open many Claude Code windows on one project and have each work independently but collaboratively. A small committed .fleet/ folder turns every window into a self-registering agent — and edits to a file another live agent has claimed are hard-blocked by the harness, so windows never silently clobber each other.

CI  License  Bash  Claude Code  No daemon  Platform

Fleet — three live agents on one project; an edit to a file agent-1 claimed is hard-blocked at the PreToolUse hook (exit 2)

Why

Open a second Claude Code window on the same repo and the two will happily edit the same file seconds apart — silently overwriting each other's work.

Fleet makes that impossible: a claimed file is denied at the PreToolUse hook with exit 2 — a real block, not a warning — with no daemon, no server, no ports.

✨ Features

🛡 Collision-proof by construction

  • Hard block, not a warning — editing a file a live foreign agent claimed returns exit 2; the edit never lands.
  • Atomic claims — a claim is an atomic mkdir of a lock dir (the one POSIX op that's both atomic and fail-if-exists), so two windows opening in the same second can't both win.
  • Self-healing — dead windows are reaped by heartbeat timeout (stale_after_s, default 180 s); nothing stays locked forever.

🤝 Coordination, automatically

  • Self-registering agents — each window becomes agent-N on SessionStart, silently coexisting with your other hooks.
  • Live roster in context — every turn injects who's live and what they hold, so the model claims before editing on its own.
  • Agent-to-agent messagingmsg / inbox to negotiate handoffs between windows.

🧰 Zero infrastructure

  • Just files + 4 hooks — no daemon, no server, no ports, no dependencies beyond Bash + Claude Code.
  • Drop-inbash install.sh /path/to/project copies .fleet/ and wires the hooks.

🌳 Two isolation modes

  • Shared tree (default) — all windows edit one working copy; claims + the guard prevent same-file collisions.
  • Worktree mode — each window gets its own git worktree (true file isolation) with Fleet coordinating intent across them.

🚦 Push-safe under parallelism

  • A sibling window's uncommitted work can't false-red your push. A pre-push gate that lints/tests the whole repo reads the filesystem — i.e. every live window's uncommitted work at once — so one window's mid-build files (an untested new component, a half-edited config) could block another window's push even when its commit was perfectly green. .fleet/bin/isolated-gate.sh fixes it: when the shared tree is dirty, it validates the pushed commit in a clean ephemeral git worktree (with the project's heavy deps symlinked in) — bypassing the sibling's work, never touching the primary tree or stashing anyone's changes. Clean tree → runs in place (fast path).
  • One line to adopt — wrap your existing gate in your pre-push hook: exec .fleet/bin/isolated-gate.sh -- bash <your gate>.

🎯 Per-window goal stacks

  • Each window keeps its OWN anchor goal. The goalstack anti-drift anchor keyed only by project, so parallel windows shared one stack — Window B's goal leaked into Window A's re-injected "active goal" line. Now goalstack keys per window (by the Claude Code session id): each window has its own goal + parent stack, so an interjection in one window never rewrites another's focus. A brand-new window inherits the last shared goal as a baseline (single-window continuity across session turnover is preserved); live windows each read their own — never cross-contaminated. Ships as .fleet/bin/goalstack; fleet.sh goal set|show|note|pop drives it.

📟 Wake on message — real agent-to-agent handoff

  • A msg can actually WAKE the recipient, instead of sitting unseen until they next happen to run. A file append can't wake an idle agent (it's blocked on its stdin pipe, not polling the inbox), so Fleet closes the loop two ways. Live windows: fleet.sh wake-cmd prints a one-line watcher to hand to the Monitor tool — a new DM then re-invokes that window to act (the SessionStart nudge reminds each agent to arm it once). Closed windows: the optional, opt-in wake-dispatcher.sh headless-resumes an offline session to process its inbox — with a process-liveness alive-guard (never races a live transcript), rate-limit + cooldown + kill-switch, and dry-run by default (--selftest proves every guard bites). And SessionEnd now preserves UNREAD DMs so a handoff to a window that closes first is never lost. The dispatcher is the one part that steps beyond "just files + hooks" — it is never installed or started by init.

🧭 Dedicated-seat skills — one command each: coordinator · arm · brain · task-manager

  • /coordinator onboards a window as the dedicated git push/merge/integration coordinator: it orients on the repo + worktrees + live agents, arms the monitor, then integrates every lane's verified work — merge the frozen branch tip, run the pre-push gate, verify the push actually landed (authoritative ls-remote, never trust exit-0), de-drift every worktree, and DM the lane. /arm is the one-command re-arm of the wake-on-message monitor after a restart. /brain onboards a window as the fleet's knowledge-oracle + research seat: peers offload questions with fleet.sh brain ask "<q>" instead of pausing their own work; the Brain answers from any available memory/knowledge tooling + live web research, cites + honesty-bands, and captures durable findings. /task-manager onboards a window as the fleet's goals/tasks/status coordination seat: it keeps every agent lane's active goal current, surfaces stalls + reachability gaps + blockers from roster/monitors, routes work with fleet.sh msg, and gives the operator a clean "who's on what / what's next / what's stuck" readout (Fleet's own primitives are the substrate; any richer project board is optional). All four ship under .fleet/skills/ and install.sh drops them into your project's .claude/skills/. Full coordinator playbook: docs/COORDINATOR.md.
  • fleet.sh retire [<agent>] [--force] safely unloads an agent from the fleet on demand — the sibling of the automatic SessionEnd deregister. It works for any agent (agent-N / short / session-id; no arg = yourself), releases its claims, removes its record + wake breadcrumb, vacates the Brain seat if it held it, and announces a retire board event. Safe-by-default: it refuses to drop a claim over uncommitted (dirty) work, or to strand a foreign agent's unread DMs, unless you pass --force.

📸 A look inside

The hero above is a real session — three live agents, and a claimed file hard-blocking a foreign edit. A couple more, straight from the CLI:

doctor — health, config, and hook wiring at a glance

$ .fleet/bin/fleet.sh doctor
== fleet doctor ==
Fleet v0.1.0  (schema 1)
config:      claim_mode=prefix  block_mode=block  stale_after_s=180  state_location=local
live agents: 3    active claims: 1
tools:       jq ✓   python3 ✓   git ✓
hooks:       wired in .claude/settings.json (OK)
scripts:     all executable (OK)
stale agent files (will be reaped): 0

Worktree mode — true file isolation per window

$ .fleet/bin/fleet.sh worktree enable
$ .fleet/bin/fleet.sh worktree new checkout-ui   # this window gets its own git worktree

🚀 Quickstart

Prerequisite Notes
Claude Code the windows you open become agents
Bash + git POSIX shell; git only for worktree mode
# drop Fleet into any existing project
bash install.sh /path/to/your/project      # copies .fleet/ + wires the hooks
# …or, after copying .fleet/ in yourself:
bash .fleet/bin/fleet.sh init

Then reopen your project windows (hooks load at startup) and open as many as you like — each is now a coordinating agent.

.fleet/bin/fleet.sh roster                 # who's live, what they hold
.fleet/bin/fleet.sh claim src/api "auth"   # reserve an area before editing
.fleet/bin/fleet.sh release src/api        # done
.fleet/bin/fleet.sh msg agent-1 "ping"     # message another window
.fleet/bin/fleet.sh inbox                  # read messages
.fleet/bin/fleet.sh wake-cmd               # print the Monitor-tool watcher that WAKES you on a new DM
.fleet/bin/fleet.sh worktree enable        # opt into per-window git worktrees

One window drives integration — run /coordinator in it to become the git push/merge coordinator (it orients, arms the monitor, and lands every lane's verified work); /arm re-arms your wake-on-message monitor after a restart. Both are installed into .claude/skills/ by install.sh — see docs/COORDINATOR.md.

🏗 Architecture

Four Claude Code hooks over a plain-file state dir — no process to run:

  Claude window ─┐
   (SessionStart)│   register.sh ──▶ ┌───────────────────────────┐
   (UserPrompt)  │   awareness.sh ──▶│   .fleet/state/  (gitignored)
   (PreToolUse)  │   guard.sh ──────▶│   ├── agents/<id>.json  (roster + ♥ heartbeat)
   (SessionEnd)  │   deregister.sh ─▶│   ├── claims/<path>/    (atomic mkdir locks)
                 │                   │   ├── board.jsonl       (intent log)
   edit blocked ◀┘   exit 2   ◀──────│   └── inbox/<agent>/    (messages)
                                     └───────────────────────────┘
  • register.sh (SessionStart) — registers the window as agent-N, silently.
  • guard.sh (PreToolUse(Edit|Write|…)) — denies edits to files a live foreign agent claimed (exit 2).
  • awareness.sh (UserPromptSubmit) — heartbeat + injects the live roster each turn.
  • deregister.sh (SessionEnd) — releases this agent's claims, drops it from the roster.

Full config, recovery, and caveats (notably: avoid iCloud/Dropbox-synced folders): .fleet/README.md.

📂 Project layout

install.sh              # one-shot installer (copies .fleet/ + wires hooks into a target repo)
.fleet/
  bin/fleet.sh          # the CLI (roster · claim · release · msg · inbox · board · worktree · doctor)
  bin/{register,guard,awareness,deregister}.sh   # the four hook handlers
  bin/lib.sh            # shared helpers (atomic claim, heartbeat, JSONL)
  config.json           # stale_after_s · claim_mode · block_mode · state_location
  state/                # runtime coordination state (gitignored — per machine)
.claude/settings.json   # registers the four hooks
CLAUDE.md               # the Fleet protocol stanza injected into each agent

🔒 Security

No secrets, no network, no ports — Fleet is plain local files + shell hooks. Runtime state (.fleet/state/) is gitignored (per-machine). Report issues: SECURITY.md.

License

MIT © 2026 convenientlymike

Many windows, one project — no clobbering.

About

Parallel-agent compatibility for Claude Code — open many windows on one project; edits to a file a live agent has claimed are hard-blocked at the hook. Just files + 4 hooks, no daemon.

Topics

Resources

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages