The Project Switcher
How Daintree ranks and bands your workspaces, what each row's status line means, and what happens to memory when you switch away.
Opening the switcher
The switcher is the one surface that answers "which workspace". Click the project button in the toolbar for the anchored dropdown, or press Option-Command-P Control-Alt-P Control-Alt-P for the wider modal.
Both forms list every registered workspace: neither is scoped or capped. The search box is labelled Search workspaces… and the list is a single listbox named Workspaces, because what you are searching now includes scratch workspaces alongside projects.
The toolbar button itself carries a badge dot when something outside the current project needs you: amber, unpulsed, when background projects are waiting for input (labelled by project count, because the question is "how many places need me?") and a pulsing accent dot when background agents are simply working, labelled by agent count.
The seven bands
Browsing the switcher (that is, with the search box empty) groups workspaces into seven bands in a fixed order. The order is the point: actionable work first, then explicit intent, then non-actionable system activity, then habit.
| Band | What lands here | Order within it |
|---|---|---|
| Current project | The project this window is showing. | One row. |
| Needs attention | Agents blocked, waiting on input, or finished and not yet reviewed. | Severity tier (blocked, then waiting, then ready-for-review) and oldest first inside each. |
| Pinned | Projects you pinned. | Alphabetical. |
| Running | Live agents, with nothing asking for you. | Most agents first, then the freshest transition into working. |
| Snoozed | Agents you deliberately quieted, and nothing else running. | Frecency. |
| Other projects | Everything else, the residual catch-all. | Whatever you picked; see Sorting. |
| Unavailable | Projects whose directory is no longer at the stored path. | Alphabetical. |
Two of those placements are deliberate and worth stating outright. Pinned sits above Running: an explicit pin is a stronger signal than the operational fact that something is executing, so a pinned project with a running agent stays in Pinned: its status line still says "Agent running". And Running requires live agents; a project holding only leftover processes falls through to Pinned or Other, where its status line says "Process running".
Snoozed is its own band rather than a line inside Other, because the two mean opposite things. Other is the residual nothing-is-happening set; a snoozed project has live agents you chose to quiet. Sorting them together is what made a project with three snoozed agents read as dormant.
Empty bands are skipped. Type into the search box and the bands collapse into one ranked list.
The bands are a view over one flat array: the same array the arrow keys walk. There is never a second, narrower list underneath, which is what keeps the keyboard cursor and the row Enter commits to from drifting apart.
What a row says
Each row shows the project's emoji, its name, and at most one status line. The line is composed from the most urgent thing true about that workspace, and it stops there: rows deliberately collapse to a single line rather than stacking facts.
| State | What the line reads |
|---|---|
| Directory gone | Directory not found |
| Agents waiting on you | Agent needs input / 3 agents need input, plus N blocked and the oldest wait age when there is one |
| Agents blocked on an error | Agent blocked / 2 agents blocked |
| Finished, not yet seen | Ready for review · finished 12m ago, or 3 agents ready for review · 4m–2h ago |
| Agents working | Agent running / 4 agents running |
| Agents snoozed | Agent snoozed · until 3:40 PM, stated once, never counting down |
| Finished and acknowledged | Agent finished · 20m ago, muted |
| Bare processes only | Process running / 3 processes running |
| Auto-closed to reclaim memory | Suspended to free memory |
A project with nothing running has no status line in the switcher at all. The underlying "Opened 13h ago" fallback exists, but the switcher declines it: on twenty rows it is twenty timestamps nobody reads, and in a frecency-ordered band a bare time-ago is easily misread as the sort key.
Where a project's directory name alone wouldn't disambiguate it, a fragment of the path is appended as a hint. A plain basename is not, because that is already the name shown beside it.
Blocked agents are a subset of waiting agents, so a project with three waiting and one blocked reports both: collapsing it to "Agent blocked" would hide two agents that are still asking you for something.
Sorting the Other projects band
The Other projects header is itself the sort control. It offers three modes:
| Mode | Orders by |
|---|---|
| Most used (default) | Frecency, then last opened, then name. |
| Recent | Last opened, then name. |
| A to Z | Name. |
The visible trigger only appears once the band holds four or more rows: below that there is too little order for the control to be worth advertising. The preference still applies, and right-clicking the header reaches the same radio group at any size. The Welcome Screen's project list honors the same setting, so the two surfaces never disagree about order.
The setting is scoped to this band alone. Needs attention, Pinned, Running and Unavailable each encode a decision of their own: asking for A-to-Z in the residual band is not asking for your blocked agents to be alphabetized.
How frecency works
"Most used" ranks on a frecency score: one exponentially decayed number per project that folds frequency and recency together.
- The half-life is four days. A score is decayed from the moment it was last written, every time it is read, never compared as a stored snapshot, because that would be comparing numbers frozen on different dates.
- A counted open adds 1.0.
- A project you have registered but never opened starts at 0.5: below one deliberate open, above the long-dead numerical tail. Registering it is itself evidence you care, and that prior decays away like any other access.
- Increments are debounced to one per project per five minutes. Bouncing rapidly between two projects is navigation style, not engagement; counting every hop would let the pair you are toggling bury everything else. The "last opened" timestamp still updates on every switch.
There is no new-project boost beyond that 0.5 prior, and frecency does not order the switcher as a whole, only the Other band in Most used mode, plus tie-breaks in search.
Searching
Typing collapses the bands into one ranked list that mixes projects and scratch workspaces. Projects are scored on both name and path; scratch workspaces are scored on name only, since their paths are UUIDs under Daintree's own directory and a fragment of one disambiguates nothing.
Scoring rewards exact substrings, word-boundary hits and consecutive runs, and penalizes gaps, so a match has to be anchored to the words it came from and stay near them. On an exact score tie, projects sort ahead of scratches; beyond that, projects fall back to frecency and scratches to last opened.
Scratch workspaces in the switcher
While browsing, scratch workspaces live in their own collapsible Scratch section below the project list, with a count badge on the header. Empty, it reads "No scratch workspaces yet. Create one for a quick one-off task."
Scratch rows carry the same agent-activity status line project rows get (the same sentences and the same tones) minus the states a scratch has no concept of: there is no missing directory, no auto-parking, and no path hint. In search results a scratch row is tagged with a muted · Scratch after its name.
Right-clicking a scratch row offers Rename scratch, Save as project… and Delete scratch…; right-clicking the section header offers Delete all scratch workspaces, which is also a visible button. Full details are on Scratch Workspaces.
The row context menu
Right-click any project row:
| Action | What it does |
|---|---|
| Open in new window | Opens the project in its own window. Hidden for the current project and for a missing one. |
| Pin project / Unpin project | Moves the project into (or out of) the Pinned band. |
| Copy path | Copies the project's directory path. |
| Move or rename project… | Opens the relocation dialog. See Moving or renaming a project. |
| Stop all agents | Terminates every running session in the project, behind a confirmation. Only shown when something is running. |
| Free memory | Releases the project's renderer and terminals without removing it. See below. |
| Close project | On the current project: closes it and returns the window to the Welcome Screen. |
| Remove project | On any other project: drops it from the list. Files on disk are untouched. |
| Locate moved project | Only on a missing project. Opens the reattach flow. |
Keyboard
Inside the palette, arrow keys walk the list and the footer hints react to the modifiers you hold: ↵ Switch becomes Command-Enter Control-Enter Control-Enter New window while the meta key is down. A highlighted scratch row drops the new-window hint, because a scratch has no second window to open. The footer also carries All agents, which opens Pilot, and Right-click for more.
Command-Backspace Control-Backspace Control-Backspace on a highlighted project row opens the same remove confirmation the context menu does.
Two global shortcuts touch projects directly:
| Action | Shortcut |
|---|---|
| Open project switcher | Option-Command-P Control-Alt-P Control-Alt-P |
| Switch to last project | Option-Command-= Control-Alt-= Control-Alt-= |
| View all agents (Pilot) | Option-Command-O Control-Alt-O Control-Alt-O |
Switch to last project goes to the project this window was in before the current one, with no palette and no overlay. Running it again returns you to where you started, which gives you Alt-Tab-style toggling between two projects. All three bindings are remappable in Settings > Keyboard Shortcuts.
Freeing memory
A background project is not free. Its terminals keep running and its renderer stays resident, which is exactly what makes switching back instant, and exactly what costs RAM. Two mechanisms give that memory back.
Free memory
Free memory, on a background project's context menu, reclaims all three resident tiers at once: it gracefully stops the PTYs in a way that preserves their sessions, destroys the cached renderer, and shuts down the workspace-host process. The project stays in your list, marked closed, and reopening it is non-destructive: the panel layout survives.
It refuses to run on a project that is on screen in any window, not just the focused one: "Cannot free memory for a project that's open in a window. Switch that window to another project first." Running it on an already-closed project is a no-op rather than an error.
Auto-close idle projects
Daintree can also do this on its own. Settings > General > Auto-close idle projects sweeps background projects that have been idle past a threshold and hold zero terminals, closing them the same way Free memory does. Their switcher rows then read Suspended to free memory, and reopening restores them.
| Behavior | Value |
|---|---|
| Default | Off: it is a structural lifecycle change, so it is opt-in. |
| Default threshold | 15 minutes (presets: 15m, 30m, 1h, 2h; range 15 minutes to 24 hours) |
| Sweep interval | Every 5 minutes, after a 2-minute quiet period at startup |
| Never touched | Any project active in any window, any project with a live terminal, and any project running the Daintree Assistant |
The terminal gate is absolute: one live terminal means the project is skipped, because agent scrollback is not something to destroy silently.
Warm and cold switching
Each background project is backed by a live Chromium renderer held in memory. Switching to a project that still has one is a warm switch: the cached view is re-attached and repainted, and reveals in roughly 60 ms. Switching to one that doesn't is a cold switch: a 100–500 MB renderer has to load, and React's boot takes seconds.
Rather than freeze on the outgoing project for that whole boot, a cold switch reveals the incoming view as soon as its themed first-paint skeleton is in the DOM (around 150 ms, well before React mounts). The skeleton is an opaque themed cover over the view's own themed background, so there is no flash, and perceived cold-switch latency drops from seconds to a few hundred milliseconds.
Background projects also stop doing work you can't see: git-status polling, fetches and PR checks are all paused while a project is hidden, and hidden cached views drop out of full-rate terminal work.
Cached project views
The cap on how many project views stay warm lives at Settings > Terminal > Performance > Cached project views, as a radio group from 1 to 5. Changes apply immediately; eviction happens on the next switch if you dropped the cap below what is currently cached.
The cap counts the active view, so N means N − 1 warm background views. On first launch the default scales with total system memory, because every extra warm view converts a cold switch into an instant one and capable machines can afford the rotation:
| Total system RAM | Default cached views |
|---|---|
| 64 GiB or more | 5 |
| 32 GiB to under 64 GiB | 4 |
| 16 GiB to under 32 GiB | 3 |
| Under 16 GiB | 2 |
| RAM can't be read | 2 |
The hardware default only applies when no preference is stored, or when the stored value is out of range. Once you set it yourself, Daintree leaves it alone.
The Reset to hardware-recommended defaults button on the same Performance tab applies to panel limits, not to Cached project views. They sit next to each other and are easy to mix up. Cached project views has no reset of its own.
What gets evicted
When the cache is over its cap, Daintree evicts least-recently-used first, with two protections:
- A view with an active agent is evicted only as a fallback, once no idle view is left.
- A view with a live Daintree Assistant backend is never evicted, at any pressure level. The reclaim here is the renderer teardown, and the Assistant's own process lives outside the renderer, so admitting the view would buy no measurable memory and cost every running sub-agent.
Eviction destroys the UI layer, not the work. Terminals, PTYs and agent processes live outside the view and keep running; the next visit to that project is simply a cold switch.
Available system memory can also lower the effective cap below what you configured. Above the warning threshold your setting stands; between the warning and critical thresholds the cap steps down one view at a time, one per pass, so warm switching degrades gradually; below the critical threshold the cache collapses to the active view alone. This ladder is decoupled from the resource profile: it reacts to memory, not to battery or thermal state.