Skip to main content

Fleet

Arm a set of agent terminals and broadcast to all of them at once, with per-target variable resolution, confirmation gates, and bulk shortcuts for accept, reject, interrupt, restart, and kill.

Updated
Reviewed

What Is Fleet?

Fleet is Daintree's coordinated multi-terminal mode. You arm a set of agent terminals, and from then on every action in the primary terminal repeats across all of them: live keystrokes, command submissions, and bulk operations like accept, reject, interrupt, restart, and kill. The armed set is sticky. It stays around until you disarm or exit, so Fleet is a mode you enter, not a one-shot shortcut.

Only agent terminals are eligible. Dock terminals, browser panels, dev preview panels, and trashed or backgrounded panels cannot join a fleet, and the armed set auto-prunes when an eligible panel goes away. The model is three steps: arm a set, broadcast a draft, review the results.

Note
Fleet works across worktrees. When your armed set spans more than one worktree and the default scoped mode is active, the Fleet Deck view activates so you can see everything that is receiving keystrokes.

Starting a Fleet

There are several ways to arm terminals. The arming ribbon appears at the top of the content area once two or more terminals are armed, and that is the moment Fleet becomes visible.

Arming Individual Terminals

The fastest path is to focus a terminal and toggle it into the fleet with a keyboard shortcut. The shortcut works globally, so you can arm terminals from anywhere in the app.

ActionShortcut
Arm or disarm focused terminalCmd+J
Arm or disarm focused panel (in the panel grid)X
Arm all eligible terminals in current worktreeCmd+Alt+A
Tip
Arm order matters. Bulk actions run in the order terminals were armed, so the first armed terminal is the primary and its input bar becomes the broadcast source.

The Fleet Picker

The Fleet Picker is the cold-start surface for selecting several terminals at once. Click the Zap icon in the sidebar to open it. A centered modal appears with the heading "Select terminals to arm", a fuzzy search input, and a list of every fleet-eligible terminal grouped by worktree.

Search matches against terminal title, worktree name, branch, file path, and recent scrollback content, so you can find a target by what is happening inside it, not just what it is called. Each worktree group has a parent checkbox, with an indeterminate state when only some of its terminals are selected.

The picker's keyboard model (substitute Ctrl for Cmd on Windows and Linux):

KeyAction
TabMove focus from search into the list
SpaceToggle the focused terminal
/ Move between rows
Cmd+ASelect all visible matches
Cmd+Shift+IInvert the current selection
Shift+ClickRange select from the last clicked row
EscClear search on first press, close on second

Footer controls show the current selection count and offer Select all, Select agents (adds every terminal currently in a working, waiting, or directing state), and Deselect all. To the right of those, a segmented Replace or Append control determines what happens on confirm. Replace clears the current armed set and arms only the picker's selection. Append adds the picker's selection to the current set.

Note
Replace is the default and resets to Replace every time you reopen the picker. Use Append when you want to grow an existing fleet without losing what you already have.

Preset Selections

Once two or more terminals are armed, the arming ribbon's selection menu offers one-click presets that arm matching terminals additively. The built-in presets cover the common coordination patterns:

  • All waiting (this worktree): every terminal in this worktree currently waiting for input
  • All waiting (all worktrees): every waiting terminal anywhere in the project
  • All working (this worktree): every terminal currently running an agent task
  • All working (all worktrees): same, but project-wide
  • All in this worktree: every eligible terminal in the active worktree

There is also a sidebar shortcut. When a sidebar filter is active, an Arm N matching button appears alongside the worktree filter controls. It dispatches the same arm-matching action the presets use, so you can build a fleet out of whatever filter view you are currently looking at.

The Arming Ribbon

The arming ribbon is the amber bar that appears across the top of the content area once the armed count reaches two. It has a 2px amber left-edge stripe and slides in with a short spring entrance. From left to right, it holds an exit button, the count chip with worktree dots, the broadcast progress counter, a cancel button (visible only during a large batched broadcast), the selection menu, and an exit button labelled with the exit chord for the current OS.

The Count Chip

The count chip shows how many terminals are armed, an "in fleet" label, a row of small worktree-color dots marking which worktrees the armed set spans, and an exited count if any of the armed terminals are no longer running. Click the chip to open a popover that lists every armed terminal in arm order. Each row has a focus button, which jumps to that terminal, and a disarm X.

An Add panes… button at the bottom of the popover switches the popover content to an inline picker so you can extend the fleet without leaving the chip. The Escape behavior is layered. The first press clears any search, the second returns the inline picker to list mode, and the third closes the popover.

Exiting Fleet Mode

The exit chord is Cmd+Esc on macOS and Ctrl+Esc on Windows and Linux. The ribbon's Exit button shows the chord for the current OS as a hint. A bare Esc on any ribbon-owned control (the exit button, count chip, or selection menu trigger) also exits, as long as there is no pending confirmation. The X button on the left edge of the ribbon disarms everything immediately.

After exiting, focus returns to the last terminal that was armed, and a brief ring animation pulses around its panel so you can see where you ended up.

Broadcasting Commands

Fleet has two broadcast paths. Raw input mirroring sends every keystroke from the primary terminal's input bar to all armed peers as you type. Pressing Enter then submits the resulting draft as a structured payload, with per-target variable resolution and a confirmation gate for anything risky.

Live Keystroke Mirroring

When two or more terminals are armed and you start typing in the primary terminal's input bar, an amber Mirroring to N peers pill (the drafting pill) appears in that input bar. Every keystroke in the primary is replicated live into the input bars of all armed peers. Backspaces, arrow keys, and selections all travel through.

Note
Typing in a follower pane does not broadcast. The primary is the only origin. This is a deliberate escape hatch for when you need to send something to one armed terminal without disarming the others first.

Submitting with Enter

Pressing Enter in the focused primary sends the composed draft to every armed peer. If the draft contains no {{variables}} and trips no safety gates, the broadcast goes out immediately. The progress counter on the ribbon shows completed/total while the fan-out is in flight. The one exception is a small fleet where the broadcast completes within the Doherty threshold, in which case the counter is suppressed to avoid flicker.

Broadcast history is per-project. Press and in the primary input bar to walk previous broadcasts the same way you walk shell history.

Per-Target Preview and Overrides

The drafting pill is clickable only when the current draft contains at least one {{variable}}. Click the pill to open the Fleet broadcast preview popover. Each row in the popover is one armed target. It shows an include checkbox, the terminal's title, the raw draft with amber spans on the variables, and an editable textarea holding the resolved payload that target will actually receive.

Edit a textarea to set a per-target override. Uncheck the include checkbox to skip that target for the current broadcast. The pill grows a divergence dot whenever any override, skip, or unresolved variable is pending, so you can tell at a glance that the fan-out is no longer a uniform send. Pressing Esc inside an open textarea reverts that target to the resolved default and blurs the field.

Per-target overrides and skips are ephemeral. As soon as a broadcast leaves the ribbon, the override store clears, so the next broadcast starts from resolved defaults again.

Tip
The drafting pill only becomes clickable when your draft has variables. For plain text with no {{...}}, the broadcast goes straight to every peer with no preview, since there is nothing to diverge on.

Recipe Variables in Fleet Broadcasts

Fleet broadcasts resolve a small set of variables per target, using each terminal's worktree context. This is the "mail merge" path. You write one draft, and every armed terminal substitutes its own values before anything reaches the agent.

VariableResolves ToExample
{{issue_number}}GitHub issue number linked to this target's worktree#412
{{pr_number}}Pull request number linked to this target's worktree#418
{{number}}Issue number if set, otherwise PR number#412
{{worktree_path}}Absolute path to this target's worktree directory/Users/you/proj/.worktrees/feature
{{branch_name}}Branch name (falls back to worktree name if no branch is set)feature/issue-412

Variables that cannot be resolved for a given target (no linked issue, no worktree association, an unknown variable name) resolve to an empty string and surface as an unresolved warning in the preview popover. The drafting pill's divergence dot counts unresolved variables, so a partially-resolvable broadcast triggers the divergence confirmation path described below.

Broadcast Safety Gates

A broadcast confirmation triggers whenever the draft, or any per-target resolved payload, crosses one of Daintree's safety thresholds:

  • The payload is multiline.
  • The payload exceeds 512 bytes of UTF-8.
  • The payload matches the destructive pattern: rm -rf, sudo, git clean -f, DROP TABLE, TRUNCATE TABLE, chmod -R, mkfs, dd if=, or a fork-bomb literal.
  • Any individual per-target payload (after override resolution) crosses the same byte threshold or matches the destructive pattern.

There are two confirmation paths depending on whether the broadcast is uniform or divergent.

Warnings-Only Confirmation

When the broadcast trips a warning but every target receives the same payload, the ribbon body is replaced with an inline confirmation strip: Send to N (reason), with Cancel and Send broadcast buttons. The reason text names the gate that fired ("multiline", "long", "matches destructive pattern"). Enter confirms, Esc cancels.

Divergence Confirmation

When per-target overrides, skips, or unresolved variables are pending, the broadcast opens a full confirmation dialog instead of an inline strip. The dialog shows a scrollable list of every target with the exact payload it will receive, an "Edited" badge for any target with an override, a skip indicator for unchecked targets, and a warning chip on targets with unresolved variables.

The divergence path exists so the per-target preview is never glossed over. If you set an override or skip, you confirm against the actual list, not a single summary line.

Cancelling an In-Flight Broadcast

Daintree batches large broadcasts so the renderer stays responsive. When a payload exceeds 100 KB and the target count exceeds five, the fan-out runs in batches of five with a microtask yield between batches. While a batched broadcast is in flight, the ribbon shows a Cancel button next to the progress counter. Cancelling stops the remaining batches from going out and dismisses the progress counter.

Fleet Deck

Fleet Deck is the multi-column grid that takes over the content area when your armed set spans multiple worktrees. It exists so you can see every terminal that is receiving keystrokes, instead of broadcasting blind to panes that live in worktrees you are not currently viewing.

Fleet Deck activates automatically the first time you start typing into a primary terminal whose armed peers cross worktree boundaries, as long as the Fleet scope mode is set to its default of "scoped". You can also enter it explicitly with the Focus selection action in the ribbon's menu, which dispatches the fleet.scope.enter action.

Inside Fleet Deck, the content grid is a CSS grid with 4px gaps, a fixed column count, and a 360px minimum panel width. Each panel's title gets a worktree prefix so you can tell siblings apart, for example feature-auth - Claude Code. The panels are follower-only. Their input bars are locked, and the maximize, minimize, and add-tab actions are disabled. The primary terminal stays the input source.

Exit with fleet.scope.exit, or by exiting Fleet mode entirely. On exit, the content area returns to whichever worktree was active before Fleet Deck took over, using a race-safe token so a mid-flight worktree switch does not strand you on the wrong screen.

Note
Fleet Deck only auto-activates in the default scoped mode. In legacy mode (set via the fleetScopeMode setting), broadcasts still reach armed terminals in other worktrees, but the content area does not switch, so you will not see them updating. Scoped mode makes broadcasting safer by removing invisible receivers.

Quick Actions

Once a fleet is armed, a set of bulk action shortcuts operates on every eligible armed terminal at once. Each action reads the agent state of each target, so it only applies to terminals that are in a state to receive it.

ActionMacWindows / LinuxActs OnConfirmation
AcceptCmd+YCtrl+YWaiting armed agentsNever
RejectCmd+NCtrl+NWaiting armed agentsFive or more waiting
InterruptEsc Esc (double-tap)Esc Esc (double-tap)Working, running, or waiting armed agentsThree or more targets
RestartCmd+Shift+RCtrl+Shift+RAll live armed terminalsAlways (with session-loss warning)
KillCmd+Shift+KCtrl+Shift+KAll live armed terminalsAlways
TrashCmd+Shift+BackspaceCtrl+Shift+BackspaceAll live armed terminalsFive or more targets

Interrupt is bound as a 350ms double-tap on Esc rather than a standard chord. A chord would collide with the escape stack the rest of the app relies on for closing popovers and dialogs, so the double-tap handler runs separately to avoid that LIFO interference.

Confirmation prompts replace the ribbon body with a single-line strip: a description like Reject 6 agents? or Restart 4 agents? (3 with active sessions), plus Cancel and confirm buttons. Confirmation will not fire while focus is in a text input or an xterm surface, so an active typing session never gets a confirmation prompt stolen out from under it.

Tip
Cmd+N / Ctrl+N falls through to the action palette when no armed terminals are waiting for input. If you press Reject and nothing happens, check the count chip. None of your armed targets are in a waiting state.

Cluster Attention Pill

The Cluster Attention Pill lives in the ContentDock toolbar above the panel grid. It appears only when two or more agent terminals share a notable state, so you don't have to scan for agents that need the same action. Only one pill shows at a time. When several clusters exist, the pill picks the highest-priority one: prompt beats error beats completion, with ties broken by size, then recency.

ClusterAppears whenColor
PromptTwo or more agents are waiting with a promptAmber (clock icon)
ErrorTwo or more agents exited with a non-zero codeRed (alert icon)
CompletionTwo or more agents completed within the last 30 secondsGreen (check icon)

Each pill carries an arm button that arms every member of the current cluster in one click, and an X button that dismisses it. Dismissals are per-signature and session-only: the same cluster stays hidden, but a new cluster, with different members or timestamps, surfaces again. Completion clusters dissolve on their own after 30 seconds if you leave them alone.

Saved Fleets

Common armed sets can be saved and recalled later. Saved fleets live in projectSettings.fleetSavedScopes, scoped to the current project, and split into two sections in the ribbon's selection menu: Pinned (snapshot fleets) and Smart-Sets (predicate fleets).

Snapshot Fleets

A snapshot stores a frozen list of terminal IDs. On recall, Daintree arms every stored ID that still exists and drops any that are no longer eligible (terminals you have since trashed or replaced). If every stored ID is gone, the row is dimmed and recall is guarded, so you can see at a glance that the fleet has gone stale.

Smart-Sets (Live Rules)

A Smart-Set stores a predicate instead of an ID list. The predicate has two fields: a scope (current worktree or all worktrees) and a state filter (all, working, waiting, or finished). On recall, the predicate re-evaluates against the current set of panels and arms whatever matches right now. Smart-Sets never auto-delete and never go stale, since the result is recomputed every time.

The built-in presets in the selection menu (All waiting, All working, and the rest) are also predicate fleets. Daintree dedupes them out of the Smart-Sets list to avoid showing the same rule twice.

Saving and Managing Saved Fleets

The selection menu has an inline save form. Enter a name, toggle between Snapshot and Live rule, and for Live rules pick a scope and state filter. Save writes the entry to project settings and adds it to the matching section in the menu.

Each saved fleet row shows a live pane count: current matches for predicates, currently-recallable IDs for snapshots. Delete is always confirmation-gated.

The relevant actions are fleet.saveNamedFleet, fleet.recallNamedFleet, and fleet.deleteNamedFleet. They are dispatchable from both the action palette and the ribbon. The action palette is the only way to recall a saved fleet from a cold start, since the ribbon does not appear until two or more terminals are armed.

Failure Handling

Broadcasts can fail for two distinct reasons, and Daintree treats them differently.

Permanent PTY errors (EPIPE, EIO, EBADF, ECONNRESET, ENOTCONN, ENXIO, EINVAL) auto-disarm the failing target. The terminal's PTY is gone or invalid, so retrying would do nothing. No failure chip is recorded, since it would auto-dismiss immediately.

Transient errors leave the armed set intact and surface a FleetFailureBanner in the ribbon area. The banner names the failed targets and offers a Retry failed button, which re-dispatches the last broadcast payload to the still-failed IDs only. Re-entrancy is guarded, so you cannot stack retries on top of each other.

Raw keystroke broadcasts cannot be replayed. There is no persistent "last payload" for a stream of keystrokes, so if the failure was raw input, the retry chip is suppressed since there is nothing to send. For structured Enter submissions, retry works against the captured payload.

Note
Unknown errno values are treated as permanent, which is the safer default. The target is disarmed rather than retried, on the assumption that an unrecognized error is more likely a dead process than a transient hiccup.

Keyboard Shortcuts

The complete Fleet shortcut reference:

ActionMacWindows / Linux
Arm or disarm focused terminal (global)Cmd+JCtrl+J
Arm or disarm focused panel (in the panel grid)XX
Arm all eligible in current worktreeCmd+Alt+ACtrl+Alt+A
Accept waitingCmd+YCtrl+Y
Reject waiting (palette fallthrough if none)Cmd+NCtrl+N
Restart allCmd+Shift+RCtrl+Shift+R
Kill allCmd+Shift+KCtrl+Shift+K
Trash allCmd+Shift+BackspaceCtrl+Shift+Backspace
Interrupt (double-tap, 350ms window)Esc EscEsc Esc
Exit Fleet modeCmd+EscCtrl+Esc
Note
The full keyboard shortcut reference for every part of Daintree, Fleet included, lives at Keyboard Shortcuts.

Related

  • Recipes: saved workspace presets, plus the recipe variable namespace that the Fleet broadcast variables mirror
  • Worktrees: the worktree context that powers per-target variable resolution
  • Keyboard Shortcuts: the global shortcut reference, profiles, and chord sequences
  • Terminals & Panels: what counts as a fleet-eligible terminal and how the panel grid behaves
  • AI Agents: running multiple agents side by side, which is the workflow Fleet was built for