Worktrees
Creating, managing, and understanding worktrees in Daintree.
Worktrees
Daintree uses Git worktrees to run multiple agents on the same repo in parallel. Each worktree gets its own branch and working directory, so agents can make changes without stepping on each other.
Creating a Worktree
Open the New Worktree dialog from the + button in the sidebar, or right-click any worktree card and select New Worktree. The dialog supports three creation modes: creating a new branch, using an existing local branch, or checking out a PR branch from GitHub.
New Branch (Default)
This is the default mode. A New Branch / Existing Branch toggle appears at the top of the dialog (hidden when opening from a PR). In New Branch mode, you fill in the following fields:
- Link Issue (optional) — a searchable issue selector. Picking an issue auto-generates the branch name in
{type}/issue-{number}-{slug}format, with the branch type prefix detected from the issue's labels or title keywords. - Base Branch — a searchable dropdown with fuzzy matching. Branches are grouped into "Recent" and others. Branches already checked out in another worktree show an in use badge; clicking one navigates to that worktree instead of selecting it. Defaults to the current branch on dialog open, or
main/masterif none is current. - New Branch Name — a freeform input field. As you type, an autocomplete popover suggests known branch type prefixes:
feature/,bugfix/,chore/,docs/,refactor/,test/,release/,ci/,deps/,perf/,style/, andwip/. Common aliases are normalised automatically (featbecomesfeature,fixorhotfixbecomesbugfix). If the branch name already exists, Daintree auto-increments it with a numeric suffix (e.g.feature/login-1) and shows a green hint. - Worktree Path — auto-generated from the branch name using your configured path pattern. You can edit it manually or use the folder picker button. If the path already exists on disk, it is auto-incremented in the same way.
- Create from remote branch — a checkbox that tells Daintree to base the new worktree off the remote tracking branch. Auto-selected when the chosen base branch is remote.
- Assign to me — appears when a GitHub issue is linked and a GitHub account is configured. Assigns the issue to you on creation. This preference is remembered across sessions.
- Run Recipe (optional) — a dropdown for selecting a recipe to run after the worktree is created. Shows your project's default recipe if one is configured.
. or -, cannot end with a space or ., and cannot contain \, :, or ... The prefix component is validated separately from the rest of the name.Existing Branch
Click the Existing Branch tab in the mode toggle to switch to this mode. Use it when you want to create a worktree for a branch that already exists locally, such as resuming interrupted work or picking up a branch created outside Daintree.
The dialog simplifies: the base branch picker and branch name input are replaced by a single Select Branch dropdown. This dropdown lists local branches that are not already checked out in another worktree. Remote-only branches and branches already in use are excluded. A search box at the top of the dropdown lets you filter by name.
Once you select a branch, the worktree path is generated automatically (with the same auto-increment logic if the path already exists). The Create button stays disabled until you make a selection. On submit, Daintree uses the existing branch as-is rather than creating a new one.
Switching between New Branch and Existing Branch mode resets the branch selection, search query, and any validation errors.
PR Checkout
You can create a worktree directly from a pull request using the Create Worktree button in the GitHub Integration panel's PR list. This opens the dialog in PR Checkout mode: the title changes to Checkout PR Branch, the mode toggle is hidden, and a banner at the top shows the PR number and title.
On open, Daintree automatically resolves the PR's branch through the following sequence:
- Checks whether the branch exists as a remote tracking branch (
origin/{headRefName}). If found, selects it. - Checks whether the branch exists locally. If found, selects it.
- If neither exists, runs
git fetch origin pull/{number}/head:{headRefName}to fetch the branch from GitHub. - After the fetch completes, re-checks for the local branch and selects it if available.
If the branch still cannot be resolved after fetching, the Create button is disabled and an amber warning banner appears:
{headRefName} from the remote. You can try running git fetch origin manually and reopening this dialog.You will need to fetch the branch manually and reopen the dialog before you can create the worktree.
Branch Prefix
Daintree can automatically prepend a namespace prefix to new branch names. Configure this in Project Settings > Worktree Setup > Branch Prefix. Three options are available:
- None (default) — no prefix is added.
- Username — reads your
git config user.name, converts it to a lowercase slug (e.g.alice), and prepends it asalice/. - Custom — lets you enter any string you like (e.g.
myteam/orfeature/).
A live preview shows what a branch name will look like with the prefix applied. The configured prefix is pre-populated into the branch name input when the dialog opens. If an issue is linked, the prefix is prepended to the auto-generated branch name from the issue.
This namespace prefix is separate from the branch type prefix (feature/, bugfix/, etc.) that comes from issue labels or the autocomplete popover. The two stack together, so with a username prefix of alice/ and a type prefix of feature/, the resulting branch name would be alice/feature/issue-42-add-login.
This is a per-project setting. There is no global branch prefix option.
Error States
If something goes wrong during worktree creation, Daintree shows a banner in the dialog with a friendly message. Common errors include:
| Condition | Message |
|---|---|
| Branch already checked out elsewhere | "This branch is already open in another worktree." An Open Worktree button appears so you can navigate directly to it. |
| Directory creation failed | "Cannot create directory — check permissions or available disk space." |
| Invalid branch name | "The branch name contains invalid characters." |
| Worktree path conflict | "A worktree already exists at this path." |
For any other git error, the raw output is shown directly. A collapsible "Show details" section reveals the full git output when the friendly message differs from the raw error.
Bulk Worktree Creation
When you want to spin up worktrees for several GitHub issues or pull requests at once, bulk creation handles the entire batch in a single operation. Select the items you need, and Daintree creates a worktree for each one with per-item progress tracking, automatic retry for transient errors, and step-level recovery if something goes wrong partway through.
To start, open the Issues or Pull Requests panel from the toolbar. Hover over any row to reveal its checkbox, then check the items you want. Once the first item is checked, all rows show their checkboxes for the rest of the session. A floating action bar slides up from the bottom showing the selection count and a Create Worktrees button. See GitHub Integration for more on the GitHub panel.
Selection shortcuts
Shift-click extends the selection as a range from the last checked item to the clicked one. A Select all button appears in the header when a search is active, and for issues there is also a Select unassigned option. Press Escape to clear the selection. Note that the selection resets when the dropdown closes.
#42 for a single issue, #42,#43,#44 for multiple, or #40-45 for a range. Select what you need from the results, then click Create Worktrees.The plan view
Clicking Create Worktrees opens a dialog showing every selected item with its proposed branch name. Before execution begins, you can configure two optional settings:
- Assign to me (issues only) — assigns each issue to your GitHub account during creation. This toggle is remembered across sessions.
- Run Recipe — picks a recipe to run on each new worktree after creation. Defaults to your project's configured default recipe, or the last recipe you selected for bulk creation if you've changed it. See Recipes for more on recipes.
Items that cannot be created are shown at reduced opacity with a skip badge explaining why:
- Closed or Merged — the issue or PR is no longer open
- Has worktree — a worktree already exists for this item
- No branch info — the PR lacks branch metadata (typically fork PRs)
If every selected item is skipped, Daintree shows a toast and the execution view never appears.
Branch naming
For issues, Daintree generates a branch name from the issue's labels and title. Labels like bug or hotfix produce a bugfix/ prefix, feature or enhancement produce feature/, and docs produces docs/. If no label matches, the title is scanned for keywords, falling back to feature/. The full pattern is {prefix}/issue-{number}-{slug}, for example feature/issue-42-add-dark-mode. If the branch name already exists, Daintree appends a numeric suffix automatically.
For pull requests, Daintree uses the PR's existing source branch directly.
Execution and progress
Once you click the confirm button, each item moves through a series of stages: creating the worktree, spawning terminals (if a recipe is selected), assigning the issue (if enabled), and verifying terminal health. The dialog shows a spinner, checkmark, or warning icon for each item as it progresses, along with a progress bar tracking the overall batch.
Daintree processes up to three items concurrently. The old limit of two has been raised now that git worktree add --no-track sidesteps the .git/config.lock contention that used to block parallel operations. Non-transient git errors still fail fast, so lock collisions do not turn into silent retries.
Automatic retry
Transient errors are retried automatically up to two times per item with exponential backoff (starting at 3 seconds, capped at 30 seconds). A small retry badge appears on the item row when this happens. Errors that trigger automatic retry include git lock file conflicts, rate limits, and network timeouts. Non-transient errors like validation failures or missing resources fail immediately without retry.
Post-batch verification
When a recipe is selected, Daintree waits briefly after all items complete and then checks whether any spawned terminals crashed immediately after launch. Items with crashed terminals are downgraded from succeeded to failed, so you can use Retry Failed to re-spawn those terminals without recreating the worktree.
Completion
When the batch finishes, a toast notification shows the result: either all worktrees created successfully, or a count of successes and failures. The dialog footer shows a Done button that closes the dialog and switches to the last successfully created worktree. If any items failed, a Retry Failed button appears alongside it.
Moving an Agent to a New Worktree
If you're working with an agent in the main worktree and decide the work should be isolated to its own branch, you can create a new worktree and move the agent there in one step. The agent restarts fresh in the new worktree with your recent terminal output injected as context, giving it enough background to continue without you having to re-explain the task.
This option is available on agent panels only (Claude Code, Gemini CLI, Codex CLI, OpenCode). Cursor agent panels, plain terminals, browser panels, notes, and dev-preview panels don't have it.
How to access
- Right-click the agent panel header and select Move to New Worktree… from the context menu
- Open the three-dot menu (•••) in the panel header and select Move to New Worktree…
What happens
- Daintree captures the terminal's visible output buffer (up to 20,000 characters, with escape sequences stripped).
- The standard New Worktree dialog opens. Choose a base branch and enter a new branch name, the same as creating any worktree.
- On confirm, the panel moves to the new worktree: its working directory and worktree association update, and the agent restarts fresh in the new directory.
- Once the agent reaches an idle state, the captured history is injected as an opening prompt so the agent has context on what was done previously.
The transferred history is what was visible in the terminal output, not structured session data. It's a best-effort handoff: the agent gets enough context to continue, but may not have full recall of earlier reasoning. If the terminal buffer is empty when you trigger the action (for example, on a panel that was just launched or restarted from scratch), no history is injected and the agent starts completely fresh.
The new worktree also starts without node_modules and other untracked files. Re-run your setup steps manually, or use lifecycle scripts to automate this.
Switching Worktrees
Click a worktree card in the sidebar to switch to it, or use Cmd+Alt+1 through 9 to switch by position. When you switch worktrees:
- The panel grid shows terminals belonging to that worktree
- Active worktree terminals get higher refresh rates
- Background worktree terminals drop to 1 FPS to save resources
Deleting a Worktree
Delete a worktree from its context menu in the sidebar. You can optionally delete the associated branch. The main worktree (your repository root) is protected and cannot be deleted. If a worktree has running processes or a stuck teardown, the context menu also offers Force Delete to remove it immediately with a shorter timeout.
Comparing Worktrees
When multiple agents are working in parallel across different worktrees, you'll want to see how their changes diverge. The cross-worktree diff opens a side-by-side comparison between any two worktrees, showing every file that differs between their branch tips (based on each branch's latest commit; uncommitted changes are not included).
There are a few ways to open it:
- Right-click a worktree card in the sidebar and select Compare Worktrees... — the worktree you clicked is pre-selected as the left (base) side
- Three-dot menu on any worktree card header — same behavior, same pre-selection
- Action palette — search for "Compare Worktrees" to open the modal without any pre-selection
Selecting worktrees
The modal has two dropdowns at the top: Left (base) and Right (compare), separated by a "vs" label. The main worktree appears first in both lists, followed by the rest alphabetically. Each dropdown disables whichever worktree is already selected on the other side, so you can't compare a worktree against itself. The comparison runs automatically as soon as both sides are selected.
File list
A sidebar on the left shows all changed files with a count header (e.g. "12 files changed"). Each file has a single-letter status badge:
| Badge | Color | Meaning |
|---|---|---|
| A | Green | Added — file exists only in the right worktree |
| D | Red | Deleted — file exists only in the left worktree |
| M | Amber | Modified — file changed between the two branches |
| R | Blue | Renamed — file was moved or renamed |
| C | Purple | Copied — file was duplicated from another path |
Click any file in the list to load its diff in the main panel.
Diff panel
The right side of the modal shows a split diff view with syntax highlighting. Additions on the right (compare) branch appear in green; deletions appear in red. Binary files and files larger than 1 MB display a placeholder message instead of the diff content.
The left side is the base for the comparison. If you want to see what another agent added, put your worktree (or main) on the left and the agent's branch on the right. Additions that exist only on the right side show up as green lines.
The comparison uses a direct branch-tip diff rather than a merge-base comparison, so it shows the complete difference between the two snapshots regardless of shared history. For staging and committing changes within a single worktree, see Review Hub.
Worktree Cards
Each worktree appears as a card in the sidebar showing:
- Branch name with a badge for the main worktree (moves to the secondary row when an issue is linked)
- Mood indicator (stable / active / stale / error)
- GitHub issue title as the card headline when linked, plus PR badges showing PR state
- Git status — modified file count, insertions, and deletions
- Last commit message summary
- Terminal list — all panels in this worktree with their type and state
- Quick actions — launch agents, run recipes, open in editor
Local git state on worktree cards (commits, branch switches, and file changes) stays current through file-system event watching rather than polling. Daintree monitors git-internal files and the working directory for changes, so the sidebar reflects new commits and branch switches within roughly 300 milliseconds. During sustained file writes from an agent, updates arrive within one to two seconds. A 30-second polling fallback kicks in automatically if the OS-level watcher is unavailable, which primarily occurs on Linux when the inotify watch limit is exceeded.
On Linux, if you notice sidebar updates lagging by around 30 seconds, the inotify watch limit may be exhausted. Check Daintree's log file (Settings > Troubleshooting > Open Log File) for a watcher initialization warning. To fix this, raise the limit with sudo sysctl -w fs.inotify.max_user_watches=524288. To make the change permanent, add fs.inotify.max_user_watches=524288 to /etc/sysctl.d/99-inotify.conf and run sudo sysctl --system.
Cards can be collapsed by double-clicking anywhere on the card header. Collapsed cards take up less vertical space in the sidebar while still showing key status information. Double-click again to expand.
Lifecycle Stage Chip
A small triangular chip in the top-left corner of each worktree card tells you the next action needed for that worktree. The chip is color-coded and appears only when there is something actionable.
| Color | State | Meaning |
|---|---|---|
| Amber | Waiting | An agent in this worktree is waiting for your input. |
| Green | Complete | The worktree has a linked issue, an open PR, no uncommitted changes, and no active agents. It is ready for review. |
| Purple | Cleanup | The PR has been merged. This worktree has served its purpose and the branch can be deleted. |
When multiple conditions apply, the chip shows the highest-priority state. Priority order is: cleanup, then complete, then waiting. A merged PR always takes precedence over other signals because the lifecycle action (delete the worktree) is the most definitive.
The chip only appears on secondary worktrees. The main/primary worktree never shows a lifecycle chip. The chip is also absent while git data is still loading for a worktree.
When a worktree reaches the "complete" state (green chip), you can open the Review Hub from its context menu to stage, review, and push the changes.
Quick State Filter
A row of pill-shaped tabs sits above the worktree list, letting you filter worktrees by their current agent state. The bar is visible whenever you have at least one secondary worktree.
Four tabs are available: All, Working, Waiting, and Finished. The Working, Waiting, and Finished tabs each show a count in parentheses (e.g. "Working (3)") that updates reactively as agent states change. Counts exclude the main worktree.
The active tab gets an accent-tinted background to make the current filter obvious. Clicking a tab that is already active resets the filter back to All, so you can toggle a filter on and off with the same button.
Each filter maps to specific worktree states:
- Working shows worktrees with at least one agent session in the working or running state
- Waiting shows worktrees where an agent is waiting for input (amber chip)
- Finished shows worktrees with the complete (green) or cleanup (purple) chip
The quick filter composes with the search bar and the advanced filter popover using AND logic. All three can be active simultaneously, narrowing the list progressively.
Collapsed Cards
When a worktree card is collapsed, the full card body (git status, last commit, terminal list, quick actions) is hidden. What remains in the header row is the branch name and a set of compact session state indicators.
These indicators show the count of agent sessions in each non-idle state, using small colored icons:
| State | Icon | Color | Animated |
|---|---|---|---|
| Working | Spinning circle | Orange | Yes |
| Directing | Interacting circle | Blue | No |
| Waiting | Hollow circle | Grey (amber when prompting) | No |
| Running | Play icon | Blue | No |
| Completed | Checkmark circle | Green | No |
Each indicator is rendered as an icon followed by a count (e.g. a spinning circle and "2" for two working sessions). Hovering the indicator group shows a tooltip with the full summary, such as "3 sessions: 2 working, 1 waiting". Idle sessions are never shown in the collapsed view.
If all sessions are idle or there are no active sessions, no indicators appear and the collapsed card shows just the branch name. For full definitions of each session state, see AI Agents.
Primary Worktree Card
The main worktree card (typically your main, master, or develop branch) uses an enhanced two-row header layout that gives you a project-level overview at a glance.
The top row shows the project name (not the branch name), a sprout icon, a collapse button, and the actions menu. The second row (visible when expanded) shows the branch label, an upstream sync indicator, and an aggregate summary of all secondary worktrees.
Upstream sync
When your main branch is ahead of or behind the upstream remote, the second row shows commit counts: ↑N in green for commits ahead and ↓N in amber for commits behind. Hovering shows the full text (e.g. "3 commits ahead, 1 commit behind upstream").
Aggregate summary
When your project has secondary worktrees, the second row also shows a compact summary: a branch icon with the total worktree count, followed by colored state counts for working and waiting worktrees. The finished count only appears when there are no working or waiting worktrees. This prevents a mixed signal where finished worktrees might distract from active work that still needs attention.
Card body
When expanded, the primary worktree card body shows two summary rows:
- Worktree and agent counts with the same aggregate breakdown as the header (worktree count, working, waiting, finished)
- GitHub health pulse showing CI status, open PR count, and open issue count. This row only appears when GitHub health data is available, which requires a configured GitHub token. See GitHub Integration for token setup.
Search and Filters
At the top of the worktree sidebar, a unified search bar lets you find worktrees by name or branch. Type to search with a 200ms debounce, so the list filters as you type without flickering on every keystroke.
Press Escape to clear the search text and any active filters in one action. If the search is already clear, pressing Escape again blurs the input. The X button at the right edge of the search bar also clears both text and filters.
Advanced filter popover
The filter icon button inside the search bar opens a popover with detailed filter and sort options. When any non-search filter is active, a small green dot badge appears on the filter icon.
| Category | Options |
|---|---|
| Sort by | Date created, Recently updated, Alphabetical, Custom order |
| Group by type | Toggle on/off (groups worktrees by branch type prefix) |
| Status | Active, Dirty (uncommitted changes), Stale, Idle |
| Branch Type | Feature, Bugfix, Refactor, Chore, Docs, Test, Release, CI, Deps, Perf, Style, WIP, Main, Detached, Other |
| GitHub | Has Issue, Has PR, PR Open, PR Merged, PR Closed |
| Sessions | Has Terminals, Working, Running, Waiting, Completed |
| Activity | Last 15 min, 1 h, 24 h, 7 days |
The "Custom order" sort option is hidden when "Group by type" is enabled. A "Clear all filters" button appears at the bottom of the popover when any filter is active.
Visual Hierarchy
When you have many worktrees open, Daintree dims idle and stale cards to keep active work visually prominent. This muting reduces the visual weight of cards that do not currently need your attention.
A card is muted when it is idle or stale, has no waiting agents, is not the currently selected worktree, and is not being hovered or focused. Muted cards use a dimmer text color for branch names and other labels.
The key exception: any card with a waiting agent is never muted, even if the worktree is otherwise idle or stale. Waiting demands your attention, so these cards always render at full visual weight. The currently selected card and any card you hover over are also never muted.
Fleet Arming
Fleet arming is persistent multi-cursor selection for agent terminals. You pick the agents you want to act on, and then every Accept, Reject, Interrupt, Restart, Kill, or Trash (plus anything you broadcast through the Fleet Composer) fans out to all of them at once. The armed set survives until you explicitly disarm it, so you can arm a group, read their output, and decide what to do next without losing the selection.
Only agent terminals are eligible: Claude Code, Gemini CLI, Codex, OpenCode, and similar. Plain terminals, browser panels, notes, and dev-preview panels cannot be armed. Terminals that move to trash or background are pruned from the armed set automatically, and the entire set clears when you switch projects.
Arming Terminals
Every worktree card has an Active Sessions accordion listing its agent terminals. Arming happens there:
- Click a terminal tile to toggle its armed state.
- Shift-click a tile to extend the selection from the last armed terminal to the clicked one.
- Drag a marquee across tiles to arm a rectangular region. The drag activates once you move past a 4px threshold, so click-to-arm is not affected.
- Press Cmd+Shift+B to arm every eligible agent in the current worktree (
terminal.armDefault).
Armed terminals get a 2px accent-green outline, a subtle green-tinted background, and a numeric badge showing their arm order (1, 2, 3…). Arm order matters because broadcasts and quick actions run against terminals in that order.
The Fleet Arming Ribbon
Whenever at least one agent is armed, a full-width ribbon slides in above the panel grid. It stays visible until the armed set is empty. Reading left to right, the ribbon contains:
- An armed count label in accent color (e.g. "3 agents armed").
- State preset pills for Working, Waiting, and Finished. Click a pill to replace the current selection with every terminal in that state; Shift-click to extend the existing selection instead.
- Quick action buttons for Accept, Reject, Interrupt, Restart, Kill, and Trash. Each button disables itself when its eligibility rule is not met.
- Hint text reading "Esc to disarm · ⌘Esc Esc to interrupt · Shift-click to extend".
- An X button on the right that disarms everything.
Quick Actions
The six quick actions run against all armed agents in arm order. Each has a default keyboard shortcut and a confirmation threshold that kicks in above a certain count or for inherently destructive actions.
| Action | Shortcut | Eligibility | Confirmation |
|---|---|---|---|
| Accept | Cmd+Y | At least one armed agent in waiting state | Never |
| Reject | Cmd+N | At least one armed agent in waiting state | 5 or more waiting agents |
| Interrupt | Cmd+Esc Esc (double-tap, 350ms) | At least one agent in working, running, or waiting state | 3 or more eligible agents |
| Restart | Cmd+Shift+R | At least one live armed terminal | Always, with a session-loss warning when any agent has an active session |
| Kill | Cmd+Shift+K | At least one live armed terminal | Always |
| Trash | Cmd+Shift+Backspace | At least one live armed terminal | 5 or more live terminals |
When confirmation is required, the ribbon body swaps to a single-line prompt ("Interrupt 4 agents?") with Enter to confirm and Esc to cancel. Confirmation does not fire while focus is inside a text input, textarea, or terminal (xterm) surface, so you will not interrupt a fleet by pressing Enter mid-sentence. If armed agents exit or become ineligible while the prompt is showing, the confirmation collapses on its own.
Interrupt is special: rather than a single chord, it fires on a Cmd+Esc double-tap within a 350ms window. This keeps the normal Esc stack (clear confirmation, then disarm) working without colliding with a global interrupt shortcut.
panel.palette (the command palette) when no waiting agents are armed. This is intentional, since Reject is a no-op without a waiting agent, but it means pressing Cmd+N with a fleet of working agents armed opens the palette rather than doing nothing visible. The hint is in the ribbon's disabled state: if the Reject button is grayed out, Reject will not fire.Pressing Esc clears a pending confirmation first, and only disarms the fleet once no confirmation is active. If you want a faster bailout, the X button on the ribbon disarms everything in one click.
Fleet Composer
Directly below the ribbon sits the Fleet Composer: a textarea that broadcasts whatever you type to every armed agent. It is only visible while the fleet is armed, and it preserves its draft as long as the armed set is non-empty. The placeholder reads "Broadcast to N armed agents (Enter to send)".
- Enter sends the message.
- Shift+Enter inserts a newline.
- Cmd+Enter force-sends, bypassing any safety confirmation.
- Arrow Up / Arrow Down walk through broadcast history for this project.
- Esc with a non-empty draft clears the draft first; with an empty draft, it bubbles up to the escape stack and disarms the fleet.
- Cmd+Shift+F (
terminal.focusFleetComposer) focuses the composer from anywhere.
The composer watches for three conditions and shows an amber confirmation strip when it sees any of them: a multi-line message, a payload larger than 512 bytes, or a destructive command pattern (rm -rf, sudo, git clean -f, DROP TABLE, truncate table, chmod -R, mkfs, dd if=, and fork bombs). The strip offers a Cancel button and a Send anyway button; Esc dismisses it and Cmd+Enter forces the send.
Broadcasts support per-terminal template substitution using double-brace syntax: {{branch}}, {{worktree_path}}, {{worktree_name}}, {{project_root}}, and {{endpoint}}. Each armed agent receives a version of the message with its own values substituted in.
{{branch_name}}, {{issue_number}}, {{pr_number}}, and similar. Copying a template across systems will leave unresolved placeholders in the output.{{branch}}" or "open the README at {{worktree_path}}/README.md" work across a fleet without hand-editing per agent. Unresolved placeholders are left as-is, so typos fail loudly instead of silently.Cluster Attention Pill
The Cluster Attention Pill is a proactive nudge that lives in the ContentDock toolbar above the panel grid. It only appears when two or more agent terminals share a notable state, which saves you from scanning for agents that need the same action. Only one pill is visible at a time; if several clusters exist, the pill picks the highest-priority one (prompt beats error beats completion, with ties broken by size then recency).
| Cluster | Appears when | Color |
|---|---|---|
| Prompt | Two or more agents are waiting with a prompt | Amber (clock icon) |
| Error | Two or more agents exited with a non-zero code | Red (alert icon) |
| Completion | Two or more agents completed within the last 30 seconds | Green (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 (different members or timestamps) will surface again. Completion clusters dissolve automatically after 30 seconds if you leave them alone.
For a consolidated list of fleet shortcuts alongside every other keybinding, see Keyboard Shortcuts. For more on the underlying agent states, see AI Agents.
Remote Compute
Remote compute is a per-worktree lifecycle hook system. You describe how to provision, pause, resume, tear down, check the status of, and connect to an external compute resource (Docker container, cloud VM, Akash lease, remote sandbox) and Daintree runs those scripts in response to worktree actions. The compute lifecycle is bound to the worktree lifecycle, so spinning up a branch spins up its environment, and deleting the worktree tears it down.
This builds on top of the project-level lifecycle scripts. Lifecycle scripts cover local setup (install dependencies, copy env files); remote compute covers anything that lives off-machine.
Configuration
Resource configuration lives in .daintree/config.json. Daintree checks three locations in priority order and the first valid file wins, with no merging between levels.
~/.daintree/projects/<sanitized-root>/config.json— user-level override, kept outside the repo. Handy for personal resources that should not be committed.<worktreePath>/.daintree/config.json— worktree-level config, scoped to a single branch.<projectRootPath>/.daintree/config.json— repo-level config, the usual location for team-shared environments.
.daintree/config.json at the worktree root and override the repo default without touching shared files.A legacy .canopy/config.json is auto-migrated to .daintree/config.json on first load. The migration is cached per-path, so polling does not re-migrate on every check.
The config file has two shapes. Use the singular resource key for a single environment:
{
"resource": {
"provision": ["docker compose up -d"],
"teardown": ["docker compose down -v"],
"resume": ["docker compose start"],
"pause": ["docker compose stop"],
"status": "docker compose ps --format json | jq -s '{status: (if length > 0 then \"running\" else \"stopped\" end)}'",
"connect": "docker compose exec app bash",
"timeouts": { "provision": 300, "teardown": 300 },
"statusInterval": 60,
"provider": "docker"
}
} Or use the plural resources key for multiple named environments:
{
"resources": {
"docker-local": { "provision": [...], "teardown": [...], ... },
"akash": { "provision": [...], "teardown": [...], ... }
}
} When resolving which environment to use, Daintree walks the following order: the exact environmentId requested, then resources["default"], then the first entry in the map, then finally the singular resource key.
Lifecycle Phases
Six lifecycle phases map to six actions available in the action palette and the worktree card context menu. Execution order is provision → (use) → pause ↔ resume → teardown, with status as a side-channel poll and connect as an interactive handoff.
| Phase | Action | Default timeout | Confirmation |
|---|---|---|---|
| provision | worktree.resource.provision | 300s | None |
| teardown | worktree.resource.teardown | 300s | Confirm |
| resume | worktree.resource.resume | 120s | None |
| pause | worktree.resource.pause | 120s | Confirm |
| status | worktree.resource.status | 120s | None |
| connect | worktree.resource.connect | n/a (opens a terminal panel) | None |
Each timeout is configurable per-phase in the timeouts block of the resource config. On timeout, Daintree sends SIGTERM to the process group and escalates to SIGKILL after 5 seconds on Unix, or taskkill /F /T on Windows. The last 8192 bytes of output are captured and shown in the notification.
The status command must output JSON with at minimum a status field. Additional fields like endpoint and meta are parsed and stored alongside the worktree. Setting statusInterval (in seconds) enables automatic periodic polling. Daintree re-runs the status command at that cadence and updates the resource badge on the worktree card.
Teardown failure does not block worktree deletion. This is deliberate: if the remote is unreachable or already gone, you should still be able to delete the worktree locally and clean up the remote separately.
PATH (a reduced system path), HOME, TERM=dumb, CI=true, NONINTERACTIVE=1, GIT_TERMINAL_PROMPT=0, and DEBIAN_FRONTEND=noninteractive. Your full interactive shell environment is not inherited, so anything you rely on from .zshrc or .bashrc (custom aliases, tool paths, secrets) needs to be set explicitly in the script or via the config.Template Variables
Lifecycle commands support two substitution syntaxes. Double-brace {{variable}} maps to fleet and runtime values; single-brace {variable} maps to filesystem-derived slugs. All substitutions are shell-escaped automatically (single-quote wrapping on Unix, double-quote on Windows), except {branch-slug} which is sanitized to [a-z0-9-] and left unquoted. Unresolved placeholders are left in place so typos are obvious. See Security > Lifecycle Command Injection for the threat model and escaping rules in full.
| Syntax | Variable | Value |
|---|---|---|
{{...}} | {{branch}} | Branch name on the worktree |
{{...}} | {{worktree_path}} | Absolute path to the worktree directory |
{{...}} | {{worktree_name}} | Short name of the worktree |
{{...}} | {{project_root}} | Absolute path to the project root |
{{...}} | {{endpoint}} | Last known endpoint from the status command |
{...} | {branch-slug} | Branch name sanitized to lowercase [a-z0-9-] |
{...} | {repo-name} | Repository folder name |
{...} | {base-folder} | Base folder of the worktree path |
{...} | {parent-dir} | Parent directory of the worktree |
In addition to template substitution, Daintree injects the following environment variables into every lifecycle command:
DAINTREE_WORKTREE_PATH— absolute path to the worktreeDAINTREE_PROJECT_ROOT— absolute path to the project rootDAINTREE_WORKTREE_NAME— short name of the worktreeDAINTREE_BRANCH— branch name (when present)DAINTREE_RESOURCE_PROVIDER— value ofresource.providerfrom the configDAINTREE_RESOURCE_ENDPOINT— endpoint returned by the last status callDAINTREE_RESOURCE_STATUS— raw output of the last status command
Named Environments
Named environments let a single project support multiple compute targets. For example, a cheap local Docker setup for day-to-day dev and a remote Akash lease for longer-running integration runs. Define them under resources in the config, or manage them visually in Project Settings > Resource Environments (Worktree Setup tab).
The settings GUI supports multiple named environments with a dropdown selector, an icon picker (Server, Cloud, Container, CPU, Globe, Rocket, Database, Terminal, Box, Layers), per-environment command lists with drag-to-reorder, an in-UI variable reference, and a Default Worktree Mode selector that controls what is pre-selected in the New Worktree dialog.
Settings are stored under resourceEnvironments, activeResourceEnvironment, and defaultWorktreeMode in either ~/.daintree/projects/<sanitized-root>/settings.json (user-local) or <projectRoot>/.daintree/settings.json (in-repo mode), matching the storage mode you picked for the project.
Auto-Provision
Auto-provision fires only when a worktree is created with Remote mode selected in the New Worktree dialog. It does not run on app launch, project open, or when you switch modes on an existing worktree. This keeps remote spin-up predictable: provisioning is always the result of an explicit choice at creation time.
Once the resource is provisioned, the worktree card shows a resource status badge driven by the most recent status command output. The badge color reflects the JSON status value: green for healthy/running states, amber for paused or transient states, red for unhealthy or error states.