Projects and sessions
One window, a menu of codebases down the left, and every session nested under the project it belongs to. The arrangement matters: a tab bar makes twenty shells look alike, and the point here is that they don't.
The menu
Projects with live sessions sit at the top, favorites first and then alphabetically, so a project keeps its place; the rest wait under Other projects. Each wears a status rollup dot — a project shows its neediest session's face, so an amber dot four projects down is a real claim on you without anything being unfolded. The project you're in trades that dot for a fold chevron and a quietly lifted card with its sessions inside, where each session's own dot says more than a rollup can.
Add a codebase with ⌘⇧O, or from any
terminal anywhere with saggar . once
you've installed the command (Settings ▸ CLI). That path climbs
to the enclosing git root, so saggar . from
src/ means the repository — and a worktree
checkout counts as a project in its own right. The menu starts empty
and offers to set up repo scanning or add a project directly.
⌘L filters the menu. A project's
header carries its branch, its dirty and ahead/behind counters, and the
buttons to open it in your editor or reveal it in Finder.
The project landing also has Docs and Agent context tabs for the repository's Markdown, agent instructions, and skills. Worktree cleanup, Git actions, and listening-port ownership live alongside them. See Project workspace for those tools.
Sessions
⌘T opens a fresh session in the current
project, of the type you last picked from the + menu (
⌘N is always a plain shell). Underneath,
every one of them is a login shell — nothing is emulated and nothing is
wrapped: it is your
shell, your profile, your prompt, with three facts added to the
environment so tools can tell where they are running —
SAGGAR=1,
TERM_PROGRAM=saggar, and a per-session
SAGGAR_SESSION id.
Sessions stay alive while you look elsewhere — that is the difference between a manager and a switcher, and it is what makes a background agent's prompt something Saggar can notice. Placement survives relaunch too: quit with eight sessions across three projects and they return in the same working directories. Plain shells start fresh; supported agents with an installed handshake resume their exact conversations. See Agents for what each provider reports.
Closing is ⌘W. A closed session is
remembered by name for a while, so the palette can reopen it with how
long ago it went — the common case after a close is wanting it back.
Quick runs
⌘P runs a command without displacing the
session you're working in. Its terminal opens in a compact corner,
stays readable while the command runs, and closes when a successful run
settles. A failed run stays open with its exit code and an amber frame.
Choose Investigate with an agent from the failed run's menu to open an agent in the same project. Saggar hands over the run's terminal id rather than pasting a frozen transcript, so the agent can read the output as it stands and follow a job that is still printing. The same menu item sits on every terminal — a docked monitor, a split pane, a popped-out window, or an ordinary session — Investigate and fix a bug covers what the agent is handed.
Unassigned sessions
A session tied to no project at all is ⌘⇧T
(⌘⇧N for a plain shell), opened at home — the scratch shell you want before you know which
codebase the answer is in. The moment it
cds into a codebase already on the menu,
that project adopts it: the session reorganizes under the project in the
nav and from there it is an ordinary project session, branch chip,
history, scratchpad and all.
Smart collapsing
A menu that only grows is a menu you stop reading, so it tidies itself. Once every session in a project has gone fifteen minutes without output or input, the project folds its session list away and wears a status tally instead — a dot and a count per status, loudest first, so “one needs you, two working” reads off a folded row without unfolding it. The threshold is a picker in Settings ▸ Projects, and the whole behavior switches off there.
What it refuses to fold is as much of the rule as what it folds:
A project with a waiting prompt or a live command stays open however old its last byte is.
- The project you're currently in stays open.
A fold Saggar placed unfolds itself the moment that project wakes or starts needing you.
Unfold one by hand and it stays as you left it. The chevron is a decision, and the sweep never argues with it.
Reaching for a folded project opens it on the spot — a click, an open from the palette, a new session, a jump to one of its sessions — because asking for a project is asking to see it.
More than one at once
The menu answers watch many. Three other arrangements answer the cases it can't:
- Split view — ⌘D
A second live session beside the one you're in, for the drive a pair case: an agent and its dev-server logs, or a scratch shell for
git statuswhile a build churns.⌘Dsplits a fresh shell in the same directory; a menu row's Open beside current puts an existing session there. One pane owns the keyboard — drawn as a focus ring, moved with⌘]— and⌘⇧Wcollapses the split, returning the second session to the menu alive. Deliberately flat: one extra pane, one axis, no recursive pane tree.- Monitor dock — ⌘⇧M
The long-running watch you keep half an eye on:
npm run dev,test --watch,tail -f. It docks in the top-right corner, read-only, never taking focus, scoped to its project so it follows you across that project's sessions and hides when you leave. A running session can be moved there without a restart (Dock as monitor), and selecting a monitor promotes it to the main pane so you can interrupt or restart it. Pressing ↵ without typing docks a bare shell: the chord has already decided you want a monitor, and the field only says what it runs. Because the premise is that you're looking elsewhere, the dock lifts two things out of the scroll: the served URL, clickable, and the “ready in 340 ms” line that says it came up. Errors in the tail turn the frame amber with a count, and a dev server that dies rings the same cue any other failed session does.- A window of its own
Any session tears out into a separate window from the header's ellipsis. To compare two sessions rather than pair them, that is the move — the layout is yours, and the app doesn't try to own your screen.
Beside the session
⌘/ opens the project scratchpad beside the
session. It is a local markdown file at
<project>/.saggar/scratchpad.md, kept
out of Git so temporary notes and handoff context stay in this checkout.
Hovering a menu row raises a peek card on the row's trailing side: the session's name, project, and status, its transcript's last lines, its branch and agent. It is strictly for background sessions — one already on screen raises nothing, since a preview there would only cover the live shell with a staler copy of itself. The card takes no clicks, so the row underneath still opens on the first one.
Hovering a URL or a file path inside a terminal previews that instead: host and title for a link, with the page's Open Graph image and description filling in behind it; name, size, and first lines for a file; entries for a folder; an honest “not found” for a path that doesn't resolve. One click opens it in the browser, the default app, or Finder.
Git state
Each session, worktree, and project wears its branch alongside a state chip: how far it sits ahead of or behind its upstream, and how many files are dirty. Amber marks uncommitted work, which is also what makes “finished, but with uncommitted work” a rank of its own in triage. Saggar refreshes after commands finish and periodically for changes made elsewhere. Untracked directories count once, and ignored files stay excluded.
Ahead and behind counts compare your checkout with Git's locally
stored upstream reference. They don't check the remote automatically.
Choose Fetch or run
git fetch to update that reference.
An amber question mark, Upstream status unavailable,
means Git couldn't make the comparison. If fetching doesn't resolve it, check that the branch
still has a valid upstream.
Saggar also writes checkout-local files into each project
(.saggar/scratchpad.md and
.saggar/jobs/). When a repository's
.gitignore doesn't cover them, the
project landing offers one click to append the lines — and if git already
tracks a file, it says so and names the
git rm --cached that a fresh ignore rule
won't do for you.
Build and run native apps
Saggar recognizes Xcode workspaces and projects, and Android projects
with a Gradle wrapper. Open the command palette and search for build,
test, run, clean, format, logs, devices, or screenshots. Finite work
runs as a quick run; Simulator and adb log
streams dock as monitors.
Project Settings can keep an Xcode scheme, build configuration, Simulator or Android device, and Android application ID. These choices stay on this Mac. The latest task result appears beside the project and in its header. A failed result lists the source locations Saggar could parse, and choosing one opens the configured IDE at that line.
File paths printed in terminals also open at their line number. After Saggar sends you to an IDE, Back to session returns to the live session that started the excursion. Signing, visual layout, and deep debugger work still belong to Xcode or Android Studio; Saggar owns the build-and-attention loop around them.