Releases: jitokim/fleetops
Release list
v0.4.0-alpha — press n with only iTerm2
What's new
If iTerm2 is your whole setup, n works now.
v0.3.0-alpha gave iTerm2 loops the action keys — approve, resume, inject, stop, kill. But you still couldn't start one. Every spawn path went through a multiplexer, and fleetops only knows orca, tmux and cmux. So an iTerm2 user had a cockpit that could watch and steer loops it had no way to create.
Press n and you get a loop in a fresh iTerm2 window. No tmux, no orca, no cmux.
Worktrees are git's job again. Choosing [w] used to require orca, because worktree creation went through orca worktree create. fleetops now runs git worktree add itself — so [w] works on any backend, including none.
It also fetches before branching. Picking origin/main only settles which branch; if your local ref is a week old you get a week-old base, which is the same stale-base bug one layer down. If the fetch fails — offline, expired token — the spawn still happens and says the base may be stale rather than pretending otherwise.
You can tell it what to run. Optional ~/.fleetops/settings.json:
{ "spawn": { "command": ["claude", "--agent", "team", "--dangerously-skip-permissions"] } }An argv array, not a command string — the wrapper most people want to point at is a shell function, which no spawned window has. Write the flags out instead. --demo ignores this entirely, and so does the headless re-drive, which also serves loops you started yourself and has no business inheriting your spawn preferences.
Also fixed
- A window that isn't running claude is no longer reported as a spawned loop. A bare shell satisfied every check the spawn path made — real window, real tty, delivery succeeded — so fleetops now asks the OS whether claude is actually on that tty, and refuses if it can't tell.
- Refusals stopped naming backends you don't have. Pressing
kon an unreachable loop used to answer "no orca/tmux/cmux" on machines with none of them installed, which reads as "go install one". It now says what would actually help, and no longer tells you to install hooks for a loop whose process already exited. - A timed-out spawn no longer prints as a failure. If the window request times out, iTerm2 may well have made the window anyway — so it says the outcome is unknown and tells you to go close the stray one, instead of inviting you to press
nagain and make a second.
Known limits
- Spawning takes focus. iTerm2 has no non-activating window creation, and re-selecting the cockpit window afterwards was measured and does not bring it back. Creating a loop will move you to it.
- Spawn needs the cockpit to be running inside iTerm2. Launching fleetops from Terminal.app on a machine that also has iTerm2 reports unavailable rather than guessing.
- iTerm2 reassigns session ids across a restart. A loop that survived an iTerm2 restart keeps running, but in-place actuation stops reaching it until the loop is restarted — it falls back to the headless re-drive for
r/iand refusesk/p/a. It refuses rather than typing into the wrong window, but it does not currently tell you why. Being tracked. - A rejected
spawn.commandfalls back toclaudesilently.
Reasoning, measurements and what remains unverified are in docs/adr-vendor-independent-actuation.md.
v0.3.0-alpha — iTerm2 gets its keys back
What's new
If you run Claude Code in iTerm2, the cockpit's action keys now actually work.
Before this release, iTerm2 users got a half-cockpit. ↵ (attach) could bring a loop's window to the front, and r/i quietly detoured through a headless re-drive — but p (stop), k (kill) and a (approve a gate) had no path at all. You watched your fleet and then reached for the mouse.
Now all five act in place, in the terminal you're already looking at:
| key | what it does for an iTerm2-hosted loop |
|---|---|
a |
approve a gate |
r |
resume a stalled loop |
i |
inject a prompt |
p |
stop the current turn |
k |
kill (two presses) |
No multiplexer required. This talks to iTerm2 itself, so a plain macOS + iTerm2 setup — no tmux, no orca, no cmux — is now a first-class host.
It refuses rather than guesses. Before typing anything, fleetops re-reads the target session's own tty and compares it against what the hooks recorded. If they disagree — the classic case being a loop running inside tmux inside iTerm2 — it refuses instead of typing into the wrong layer.
It tells you when it doesn't know. If a send times out, fleetops can't tell whether your keystroke landed. It says exactly that and declines to retry, rather than silently sending your prompt twice.
Also fixed
- Text was being cut one column short for anyone on a Korean/Japanese/Chinese locale. fleetops measured the ellipsis and status glyphs as two columns wide while the renderer and iTerm2 both drew them as one, so every truncated transcript line lost a character. The width policy is now pinned instead of inherited from your
$LANG. (#44) dandxare documented. The previous release split the old dismiss key intod(hide, persists across restarts) andx(delete), but said so in neither--helpnor the README — wheredstill advertised the old "until restart" behaviour.
Known limits — please read before trusting it
a(approve) is not fully verified. We measured that a real Enter byte leaves for the session, but not that Claude's gate prompt accepts it as "take the default." Every other backend rests on the same assumption, so this isn't iTerm2-specific — but it isn't proven either.- iTerm2 has marked AppleScript deprecated in favour of its Python API. If
writeever goes away,r/ifall back to the headless re-drive andk/p/ago dead — they have no fallback. The exposure is one adapter. - Spawning new loops (
n) still needs a multiplexer. - Verification of the tmux-inside-iTerm2 hazard itself, and behaviour under a denied Automation grant, remain unrun. The defenses are implemented and tested; the hazards are not empirically confirmed.
Full architectural reasoning, including which AppleScript mechanisms were rejected and why, is in docs/adr-vendor-independent-actuation.md.
v0.2.0-alpha — attach anywhere
Still experimental. fleetops sends real keystrokes and can kill real processes. Read the README's "Known rough edges" before pointing it at anything you care about.
Attach to a loop in a plain terminal
Until now, ↵ (attach) only worked if your loop happened to live inside a multiplexer fleetops had a handle on — orca, tmux, or cmux. Start claude in a normal iTerm2 window and fleetops could see the loop perfectly well, but pressing attach just shrugged and told you to cd there yourself.
That gap is closed for iTerm2. fleetops now records which terminal app hosts each session, and raises the right window/tab directly. No multiplexer required, and nothing about how you launch loops has to change — start claude however you already do.
If you're on a terminal fleetops doesn't know how to raise yet, it still degrades to the copy-pasteable hint rather than pretending. Attach never hard-fails, and it will never claim success it didn't achieve.
Attach picks the right backend now
fleetops used to attach through whichever multiplexer was installed first, not whichever one actually hosted your session — so with orca installed, attaching to a tmux-hosted loop could fail outright. It now probes for the backend that can genuinely find your session's surface.
Hiding a loop finally sticks
d used to hide a loop only until you restarted fleetops — dead and finished loops kept crawling back into the list.
dnow hides for good, remembered across restarts.x(new, press twice to confirm) also drops the session's registry entry, for loops you're done with entirely.
Neither one touches your conversation history. Your ~/.claude transcripts are never deleted.
Also in this release
- Injecting into the right session when several share a directory. Previously ambiguous — fleetops now targets the exact session, falling back to a headless re-drive when it can't act in place, and says so honestly instead of failing silently.
- The fleet list is readable at a glance — loops show their display name or goal rather than an opaque id.
n(spawn) lets you choose where. The wizard now shows the target directory and lets you change it: a new worktree, this directory, a typed path, or the selected loop's directory.
Upgrading
go install github.com/jitokim/fleetops/cmd/fleetops@latest
fleetops hooks installhooks install is worth re-running: the host-terminal info that powers the new attach is recorded when a session starts, so loops already running when you upgrade won't have it. Start a fresh loop to try it.
No breaking changes. Session records written by older versions still load as-is.