Skip to content

Latest commit

 

History

History
413 lines (266 loc) · 72.6 KB

File metadata and controls

413 lines (266 loc) · 72.6 KB

Usage

agent-manager

Sessions run inside tmux (am_* namespace), so they survive the manager quitting. Inside a session, Ctrl+Q detaches back to the manager when your terminal and tmux leave it available; Ctrl+\ is an alternate under the same rule. Ctrl+R opens the session's diff review and F3 opens its directory in your editor. In a full-screen attach, the session footer also shows an inner tmux prefix followed by d when configured. When nested inside another tmux, send the inner prefix shown in the footer, then press d. If both tmux servers use the same prefix, invoke the outer tmux's send-prefix binding; if the outer tmux otherwise captures the inner prefix, configure it to forward that key. agent-manager --version prints the version.

Agent sessions live on a private tmux server named agentmgr, so they never mix with the tmux you run yourself and a kill-server on your own socket leaves them alone. To reach one from a plain shell, name that server: tmux -L agentmgr ls, then tmux -L agentmgr attach -t am_<id>.

Keys

Tell your agent what you want to review in Agent Manager. Your agent will set up the repository or worktree, target and scope for you to view in the review panel. You can also tell your agent to manage sessions and terminals in Agent Manager; it can set them up and control them for you.

Key Action
n New session (name, tool, the model, effort and profile the tool offers, directory, worktree toggle and the base it starts from, optional starting prompt, group picker)
T New terminal tab: a shell under the selected agent, or in the selected group
o Open the selected row's directory in your editor
f Fork the selected conversation into a named session in the same group and directory
g New group (name, parent, default path, worktree default, worktree base)
enter Focus session in place (keys go to the agent, list stays) / fold group
click Focus the session (the full-screen layout selects the row)
double click Fold the group / focus the session in the full-screen layout
drag ⠿ Reorder the row, drop it into another group, or nest a terminal under an agent
[…] / right click The row's actions
click its row / mouse back Focused: back to the list
A Attach session full screen (Settings can swap it with enter)
. Mark a finished session idle without entering it
ctrl+q / ctrl+\ Inside a session: back to the manager when the terminal and tmux leave the key available. ctrl+r, F3 and this pair move in the keybindings row of Settings, and so does every key of the list itself (see Key bindings)
tmux prefix, then d Inside a full-screen attach: back to the manager when the prefix reaches the inner tmux
F3 Inside a session: open its directory in your editor
→ Step into the row: focus the session, or open the group. In beta; Settings (s) can turn the pair off
← Step out: close the group, or — focused, with the caret at the start of the agent's prompt — back to the manager. This needs the tool's prompt marker (its activity_cutoff) on the caret's row, so a CLI without one keeps ← entirely; anywhere else in the prompt it moves the caret as usual
K / J (or shift+↑ / shift+↓) Reorder session or group among its visible siblings
m Move a session to a group, a terminal into a session, or a group under another group
r Rename session / edit tool; edit group name and default path
y Copy the selected session's newest reply to the system clipboard, without attaching
x Kill the selected session, or every live session under a group: frees the RAM their agents hold, and the rows stay for v
X Kill every live session in view
v Revive a dead session, or every dead session under a group. A group asks first when more than one session under it is dead
V Revive every dead session in view, asking first when more than one is dead
R Restart the selected session on an empty context: same name, group, directory and tool
a / u Archive / restore a session or group. Archive kills the process and keeps the last preview; restore resumes it
d Delete session, or a group + its entire subtree
space Quick prompt: answer the selected session, or spawn an agent in the selected group
ctrl+r Review the selected session's changes: full-screen whole-file diffs, with c to comment a line and C to send the comments to the agent
F Fold / unfold every group
s Settings (default tool, theme, theme follows OS, background, list density, sessions layout, header and computer stats visibility, review layout, after quick send, session keys, ←→ step in/out, mouse, spawn in worktree, fetch on spawn, coordination, notifications, notify on finish, editor, keybindings, CLIs, report a bug, suggest a change, and the version row that updates in place)
| Resize the split: ←→ nudge the divider, enter commits, esc cancels
t Toggle archived view
w Filter to sessions that need attention (waiting, finished, errored); press again to show all
M Messages (the update notice with a release's highlights and thanks, maintainer messages, and tips, where x dismisses one for good). New notices open automatically. While any message is left, the key legend leads with messages, and a click on it reopens them in either layout. The welcome message points at Settings for a bug or an idea.
e Hide / show empty groups
/ Search the list by name. The field takes an accent fill while it is open, and the query lights up inside every matching session and group name
? The key map for the current screen. From the list it shows every group; from review it shows only review bindings. It scrolls (↑↓/jk, pgup/pgdn, g/G) and / searches it down to one line.
q Quit (sessions keep running)

Navigation is keyboard-driven, but the mouse works too. The manager claims mouse reporting so the wheel stays inside the app and cannot scroll the TUI out of view: a notch moves the session cursor in the list and scrolls the diff, and in a focused session it walks that pane's scrollback, where click-drag also selects pane text and copies it. In the split, a click on a session row focuses it when the button comes back up, so a press that slides off the row does nothing. In the full-screen layout a click selects and a double click focuses. A double click on a group row folds or unfolds it. With search open a click only selects. With the quick prompt open, clicks work as they do in the list, and one that leaves the list closes the prompt. While a session is focused beside the list, a click on another row focuses that one, and a click on the focused session's own row returns to the list. The mouse back button returns in either layout. Every row carries a ⠿ handle beside its name. Drag it to reorder the row among its siblings, onto a group row to move it into that group, onto a session in another group to land just before it, or, for a terminal, onto an agent to nest under it. The row a drop would land on is tinted and the footer names the move. Letting go stores it, esc puts everything back, and holding the drag on the top or bottom row of a long list scrolls it. A click on the handle without a drag keeps the row lifted for the arrow keys, with enter to drop and esc to put back. The […] at the end of every row opens the row's actions, each with its key beside it: attach, prompt, copy the last reply, review, fork, new terminal, editor, rename, move, restart, archive, kill and delete, trimmed to what the row can do. Hovering or the arrow keys highlight an entry, and a click or enter runs it, as does letting go over an entry after pressing […] and dragging onto it. A right click opens the same menu in terminals that pass right clicks to the app. iTerm2 keeps right clicks for its own menu unless its pointer settings pass them on. With the mouse toggle off, rows paint no handle and no […]. A press on the sessions/sidebar divider drags it without arming resize mode from the keyboard first. While any message is left, a click on messages in the key legend opens Messages, the same as M, in either layout. Settings has a mouse toggle, on by default; turning it off releases mouse reporting everywhere except a focused session, which hands the terminal back native click-drag text selection over the rail and the content column, and the wheel with it: the diff stops scrolling and the host terminal can scroll the manager out of view again. Clicking a web link in a focused pane opens it in your browser, in either layout: the mouse claim keeps the terminal's own opener out of reach, so the manager does the opening itself, joining a link the pane wrapped across rows back together first. In a focused agent that tracks the mouse, a click off a link passes straight through to its own clickable UI while a drag still selects and copies; hold alt to pass a whole drag through instead, for the agent's own text selection or sliders. See #110. Over SSH a clicked link is printed on the plain terminal instead, with the manager stepped aside, so your own terminal can open or select it. The manager also sends it to your clipboard with OSC 52, which lands where the terminal accepts clipboard writes. Enter brings the manager back. Inside tmux the check follows the client that attached last. Copies over SSH take the same OSC 52 route, except through an X11 display forwarded by ssh, where xclip or xsel reach your clipboard.

the mouse in the list: the wheel moves the cursor, a click focuses a session, a click on its row comes back, the ⠿ handle drags a row within its group and into another one, and […] opens the row's actions

Quick prompt

Press space to dock a prompt bar at the bottom of the sidebar. The target follows the cursor while the bar is open: ↑↓ still navigate the list from a one-row prompt, and in a taller one ↑ navigates from the top row and ↓ from the bottom row. Anywhere else they move the caret, so a prompt that wrapped or was pasted with line breaks edits like a multi-line editor.

  • On a session row, enter sends the typed text straight into the session's pane, so the agent gets it as a user message without you attaching. The bar clears and stays open, ready for the next answer; Settings (s) can make it close instead.
  • On a group row, enter spawns a new agent in that group and submits the prompt at startup, using the group's default path. This is the shortest path to a fresh agent: space, type the task, enter, with no form and no name to invent. The spawn tool starts on the tool of the last session you created in this run, and on the Settings default before that. tab (or alt+m) steps it forward (claude ↔ opencode ↔ any configured tool), shift+tab steps it back one. The target row shows what the new agent launches with on its right: the tool, its model, effort and profile, and the worktree, and a click on one does what its key does. ctrl+l swaps the bar for a sheet listing the models the tool reports, where typing filters, enter picks and esc hands the prompt back. ctrl+x steps the reasoning effort through the levels that model takes, and ctrl+y steps the profile of a tool that has profiles, from the bar or the sheet (see Model, effort and profile). ctrl+t (or alt+w) toggles whether the new agent spawns into its own git worktree, starting from that last session's pick, or the Settings default before any. The target row reads ⎇ on or ⎇ off, or ⎇ no repo when the target directory cannot hold one. Answering an existing session ignores the toggle, since there is no new session to place in a worktree. The agent starts working on the prompt immediately.

ctrl+v pastes an image from the system clipboard as an [Image #1] chip at the caret. The image is saved under agent-manager-pastes in your temp directory, and on send each chip is swapped back for its path, so the paths reach the agent in the order and the places you pasted them. backspace next to a chip removes the whole chip, and an edit that swallows one releases its image. A clipboard holding text rather than an image pastes as text. Pasted images older than seven days are cleared at startup and once a day while the manager runs, so an agent can still open one from an earlier session while temp stays tidy.

esc closes the bar.

The new-session form's optional prompt field launches an agent the same way, and its ↑↓ move the caret between rows the same way, ↑ leaving the field only from its top row and ↓ only from its bottom row. It takes ctrl+v and its chips too, since a first task is often the screenshot that explains it: paste the design to match or the crash to read, and the agent opens the file on its first turn. Leaving the form without creating the session releases the images it was holding, the way closing the bar does. How the prompt reaches the agent depends on the CLI: most take it as a launch argument, and a persistent CLI that accepts none has it typed into its composer as soon as that composer is ready.

answering a working Claude Code session from the prompt bar, without attaching

Full-screen sessions

The "sessions layout" row in Settings (s) hands the session list the whole terminal: ← or → swaps it to full screen, the preview column steps aside, and every row takes the full width. The same keys return it to split, the default, and the choice is persisted like the split ratio.

the session list taking the whole terminal, one-line rows with the messages badge on the foot, then a session focused full screen

Session rows read the same way in either layout, and the list density setting picks their rhythm. Compact keeps a session to one line: mark, name and badges, then a state-picked value riding to the right of the name with state · tool · age against the right edge. That value is the agent's last message whenever it has said anything (the question it waits on, its progress, its result) behind a static ↳ washed in the state color, and the task it was given behind the accent's ❯ only while it has not. Comfortable unfolds each session to three lines: the name line, your last message behind a ❯ in the accent, and the agent's last message under it. A terminal keeps two of them, its name line and its last output, since no one is prompting a shell. Both messages come from the session's own record: the prompt echoed into its transcript (typed in an attach, the focus view, or the quick prompt, all the same) and the reply read from above its input box, recovered from the tool's session store when it scrolled away. A waiting session's message wears its state color at full strength, the other states at a wash of theirs; a working session that has not said anything yet animates a loader. Groups stay one line at any density. In the full-screen layout the meters condense to one labeled line above the key legend: cpu, mem, swap, disk, battery (when present), temperatures and net. The separate header and computer stats settings apply in either layout and return hidden chrome to the session list. Hiding computer stats removes the whole lower block, including its separator. Messages open automatically when new notices arrive. M reopens them, and so does a click on messages in the key legend while any message is left.

the same full-screen list at the comfortable density: every session on three lines, its task and the agent's last message under the name

Sessions size themselves to the layout that shows them: the full-screen layout pins their tmux windows to the whole terminal body, so an opened or attached session spans the full width, and switching back to the split re-pins the width. A pane that grew taller follows the box back down when its agent runs on the terminal's alternate screen (Claude Code, OpenCode) or keeps its scrollback through a height change (Grok). Any other pane keeps its height and is cropped on screen instead, because a height change makes Codex clear its whole scrollback.

Opening a session (enter, or →) takes the whole body too, through the same pipeline focus mode uses in the split: keys go to the agent, ctrl+r, F3 and the footer stay alive, and ctrl+q, the mouse back button, or ← with the caret at the start of the agent's prompt, returns to the list. A still hands the terminal over with a real tmux attach. Every footer that replaces the key legend for a moment, the quick prompt's, focus's, rename's, resize's, takes a single row in this layout and leaves the rest to the body; in the split those rows are held so the preview box beside them stays where it is. A line of its own says which session you are in, a hairline holding it off the header band above and the pane below: its state dot and name, the tool, the state and how long it has held it on the left, then its directory, its worktree branch, its own cpu and memory, how long ago it started, and a queued count when messages are waiting, against the right edge. The keys are in the footer under the pane. As the terminal narrows the readings give way one at a time, the least telling first, so what fits still shows.

Which CLIs you get offered

Every configured tool is offered when you create a session, which is more than most people run. Settings (s) has a CLIs row: enter opens a checklist, space or enter unchecks the tool under the cursor, esc saves, and the ones left checked are what the n form's tool picker and the quick prompt's tab cycle through. The last checked tool cannot be unchecked, since a picker with nothing in it could not create a session. It only narrows the pickers, so a session already on an unchecked tool keeps running and revives on that same tool. The last row, request CLI support, opens an issue for a CLI we do not ship rules for yet.

Model, effort and profile

The n form has model and effort rows under tool, and a profile row above them for a tool that has profiles. The quick prompt has the same choices on ctrl+l, ctrl+x and ctrl+y, and agent-manager spawn takes --model, --effort and --profile. Every value comes from the CLI itself. The manager asks it through the interface it offers programs, so a model that ships next week shows up in the list without an Agent Manager release.

Left alone, each row keeps the CLI's own default, which is whatever you set up in that CLI. Each CLI remembers the model, effort and profile you last picked for it, in the form and the quick prompt alike, and the next session on that CLI starts there. The model list filters as you type, the way dir suggests paths, the arrows scroll through every model the CLI lists, and only a listed model can be picked. Your last picks for that CLI come first. The effort row offers the levels the chosen model takes, or the levels of the model the CLI starts on while none is picked, and it leaves the form for a model that takes none. The choice is saved on the session, so restart (R), revive (v), a relaunch inside its pane and fork (f) all run on it.

the New Session form with Claude's model list open, twelve models read from Claude Code with a scroll bar beside them

CLI Asked through Model Effort Profile
Claude Code the Agent SDK initialize request yes per model ·
Codex codex app-server yes per model, with its default not supported
Grok Build ACP over grok agent stdio yes per model ·
Pi pi --mode rpc yes per model ·
Muse Code muse serve yes per model ·
OpenCode opencode serve yes not supported ·
Gemini CLI ACP over gemini --acp yes not supported ·
Hermes Agent hermes serve per profile, with the provider typed yes
Antigravity CLI not asked yet not supported not supported ·
Command Code nothing a program can ask not supported not supported ·

A row the CLI cannot fill reads "not supported". OpenCode and Gemini CLI take no effort when they launch. Codex's -p reads <name>.config.toml files that none of its interfaces list, so it has no profile row. Hermes says which models reason but not their levels, so its effort is typed, and only a model that reasons shows the row. Pi's list needs Pi 0.84.3 or later, because earlier releases save every model they are asked about as your default. When a CLI fails to answer, the model row says why, the session launches on the CLI's defaults, and the next form asks again once half a minute has passed.

The answer is kept under catalogs in the config directory for six hours, and an upgraded CLI is asked again at once. The manager asks from its own catalogs/work directory, because some of these interfaces open a session to answer, and that session must never land in a directory a real session works in. Gemini CLI records a short chat for that directory each time it answers.

Terminal tabs

T opens a shell tab: a session like any other (same list, same row keys, same enter, x, v and R) with your shell in the pane instead of an agent. On an agent, the new shell nests under that session, in that agent's group and directory. On a group, it lands in the group as an un-nested sibling, in the group's default path. On a nested shell it joins the same parent; on an un-nested shell it stays un-nested in that shell's group. Either way it opens in that shell's own directory, so a shell you have cd'd somewhere hands the next one the same place. A nested shell is named after the session it hangs under, terminal-review-done rather than terminal-0ab5, and the next one under that session counts up to terminal-review-done-2; a shell with no session over it keeps the generated name, and r renames any of them. Its status rests at idle throughout: turn tracking belongs to agents, and a shell has no turns.

The pane opens on $SHELL, so it is the shell you already use everywhere else.

Shells live in the tree with the agents they belong to, marked with ❯ where an agent carries its status dot. m on a terminal moves it onto an agent (nests under that session) or onto a group (un-nests into that group). A group's dots and counts describe its agents, so only agent work shows as in progress.

The keys that write into a pane refuse a shell. space and the review screen's C both paste their text and press Enter, so on a shell a sentence meant for an agent would run as a command. Both say the row is a shell and send nothing; enter the session (↵) to type there, where what you type is plainly a command. f says the same, since a shell has no conversation to fork.

agent-manager rename run in a T terminal renames that terminal's own row: the manager finds the row from the tmux pane the command runs in. r in the list is how you rename any other row.

Opening the editor

o opens the row under the cursor in your editor: a session's live working directory (wherever its shell or agent has moved to, not only where it started), the directory it was created in when the live one cannot be read, or a group's default path. It works on a terminal tab too — the shell you ran the build in is usually sitting in the directory you want open.

The editor row in Settings (s) picks it. ←→ steps through auto, each GUI editor found on your PATH (code, cursor, windsurf, zed, subl, idea), the editors $VISUAL and $EDITOR name, and custom, where ↵ opens a field for a command of your own. auto names the editor it resolves to: $AGENT_MANAGER_EDITOR, then the first of those GUI editors on PATH, then $VISUAL or $EDITOR. The environment comes last because it usually names the editor you set for git commit messages, and a project is better opened in a windowed one.

The line is run directly, never through a shell, so nothing in it is expanded and an .envrc that sets EDITOR cannot smuggle a command in behind it. A custom command takes arguments, and quotes group one that carries a space: code -n, open -a 'Visual Studio Code'.

Inside a session, attached or focused, F3 opens that session's directory the same way. It costs an attach its client, so the manager steps back into the session once a windowed editor is running, or once one that draws in the terminal exits. An editor that fails to start keeps the manager on screen, where you can read why.

Like ctrl+q and ctrl+r, the manager keeps F3 for itself inside a session, so a program running in there stops seeing it. Every other ctrl combination reaches the program, ctrl+o included: Claude Code shows more lines with it, Gemini CLI toggles copy mode, and in a terminal tab nano writes the file out. The keybindings row in Settings moves any of the three keys, or turns one off so the agent gets it (see Key bindings).

A known windowed editor (the six above, plus open and xdg-open) starts detached and the manager stays on screen, with the status line naming what opened. Everything else takes the terminal over the way an attach does and hands it back on exit — that way round because a terminal editor started detached would have nowhere to draw, while a windowed one launched this way only costs a repaint.

Worktree sessions

A session can spawn into its own git worktree instead of the shared working directory: the n form has a worktree field between dir and prompt (◂ on ▸ / ◂ off ▸, toggled with ←→), and the quick prompt's ctrl+t does the same for a group spawn. Settings (s) has a "spawn in worktree" row that sets the default both start from, and the last session you created in this run carries its pick over to the next one.

The worktree lives at <repo>-worktrees/<name> next to the repo, on a new branch am/<name>. Its starting point is the branch the repo's work merges into. In a repo with an upstream remote, which GitHub's fork layout makes the parent repository, that is upstream's default branch. Otherwise it is origin's default branch, then a local main or master in a repo with neither, and finally HEAD. The default branch's name comes from the remote's HEAD, which git clone records, and an upstream without one borrows origin's. A worktree that fails to create blocks the spawn with an error instead of falling back to a shared directory.

A group can name the starting point itself. The base row of the g form, and of a group's edit card (r on the group row), steps with ←→ through auto and the repo's branches. A group left on auto takes its parent's base, and with no base set anywhere up the tree the starting point is detected as above. The same base is what review's "vs target" compares that group's sessions against, until a target is picked in review. It suits a team that branches off develop, and a repo whose upstream remote is a mirror rather than the target of its pull requests.

While the n form or the quick prompt is set to spawn a worktree, the manager fetches the starting point's branch from its remote, that one branch alone, at most once a minute for the same directory, so the new branch starts from the remote's tip. The n form shows the starting point in a base row under worktree, marked (group) or (auto) by where it came from, and says while the fetch runs and when it failed. A spawn that beats the fetch, or one made offline, starts from the last fetch. Sessions created through create_session fetch before the worktree is made. The "fetch on spawn" row in Settings (s) turns the fetch off, for SSH keys that ask for a tap on every use, slow networks, or servers that count fetches, and worktrees then start from whatever the last fetch left.

A branch that starts from a remote other than origin, such as the parent of a fork, keeps tracking it, so git pull brings in the parent's changes, and git push sends it to origin, your fork. A remote.pushDefault you set routes pushes instead.

A directory that is not a git repo cannot hold a worktree, so the field reads unavailable (not a git repo) and the quick prompt's target row ⎇ no repo in place of on/off, the toggle says why when pressed, and the session spawns in that directory as a plain session. This is what a group path sitting above several repos does: the umbrella itself is not a repo, so its sessions launch in it directly.

Renaming a session (r, or the agent's own agent-manager rename) keeps its worktree directory at the spawn-time path and renames only its branch to am/<new name>. The directory keeps the slug it was given at spawn (for example claude-7a72) for as long as the worktree exists, even as the session name and am/ branch change. The stable directory keeps the running agent's working path valid, while the branch still carries the name that appears in review and the eventual PR. A branch that already exists reports that and keeps the session on its old name. A worktree you have renamed or removed by hand, switched to another branch, or left detached mid-rebase is left alone and the session still takes the new name.

Deleting (d) a session that holds a worktree removes the worktree and its branch when it is clean: no uncommitted changes, and no commits that exist nowhere else. Both the worktree's checkout and its am/ branch are checked, so a worktree switched to another branch or detached still keeps a branch that holds unsaved commits. The base is the starting point above, and a base of HEAD means the repo's own checkout. A branch whose changes are already in the base counts as merged, including after a squash or rebase merge whose remote branch was deleted and pruned. A branch that is itself the group's base is kept, since other sessions build on it. Spotting such a merge needs git 2.38 or later. Commits ahead of the base are also fine once they are pushed, since the base ref only moves on fetch and a merged branch would otherwise keep its worktree forever. The branch itself is kept in that case: a remote-tracking ref is a local cache, and holding the branch keeps those commits reachable even if the remote branch is gone. A dirty worktree is left in place and its path shown, so nothing is lost. Note that "clean" is judged by git status, so gitignored files inside the worktree (a .env, local config, build output) are removed along with the directory. Killing, archiving, and reviving a session never touch its worktree.

Killing and reviving sessions

x ends a session that is holding RAM you want back, and on a group row it ends every live session under it; X ends every live session in view. Each asks to confirm first, and what it ends is the tmux session, not the record: the row stays in the tree, marked dead, with its name, group, and conversation id intact.

ending every session under a group for the RAM, then reviving the whole subtree on its own conversations

v relaunches a dead session under its old id, keeping its name, group, and history. When the manager holds that session's own conversation id, revive resumes that exact conversation through the tool's resume_by_id_command: claude --resume {id}, codex resume {id}, opencode --session {id}, grok --resume {id}, gemini --resume {id}, pi --session {id}, hermes --cli --resume {id}, cmd --session {id}, muse resume {id}, agy --conversation {id}, omp --resume {id}.

Quitting the agent inside a session leaves the window alive on a shell, and v brings it back there: the manager types the launch command into that shell, so the agent returns in the pane it left, carrying the session id, the MCP registration and the hook settings a launch gives it. The pane names that key the moment the agent exits. Those values stay exported in the shell as well, so agent-manager rename and the rest of the subcommands keep working from it, and so does an agent you start there by hand.

The id arrives one of two ways: tools with a session_id_flag launch under an id the manager mints, and tools that mint their own are read back by a session_store capturer (codex, opencode, gemini, hermes, command-code, muse, antigravity, omp). The comfortable list density shows the id once it is known. Without one, a tool with a resume_picker_command opens its own session picker in the pane on revive (claude --resume, codex resume, cmd --resume, pi --resume, bare grok, gemini -i /resume, hermes --cli sessions browse, muse resume, omp --resume), so the user picks the conversation; opencode's and Antigravity's pickers exist only inside their TUIs, so revive launches them bare and the manager types /sessions or /resume at the composer once the caret rests there. A relaunched session binds the picked conversation's id back after the pane settles on it: the manager snapshots the tool's store right before the relaunch and binds only the single conversation whose activity outruns that snapshot on two consecutive polls, so a conversation merely written before the revive is never mistaken for the pick. Tools without a picker fall back to revive_command (claude --continue), which resumes the working directory's most recent conversation, and the manager says so in the status line, since sessions sharing a directory would otherwise land on the wrong one. On a group row v revives every dead session under it, and V revives every dead session in view. Each asks to confirm first when that is more than one session, and a single dead session comes back at once. Both revive what they can and name the first failure rather than stopping.

Restarting a session on an empty context

R keeps the row and drops the context: same name, group, tool, and working directory, a managed worktree included, launched on a conversation the agent has never seen. It is what you want when a session has piled up context you are done with, where reviving it would spend the budget re-reading history or land straight in a compact.

It asks to confirm first, and it works on a live session too: the running agent ends, then the fresh one launches. The conversation it was on is retired rather than resumed: the manager mints a new id for tools that take one (session_id_flag) and captures the new one for tools that mint their own (session_store). The retired conversation is left on disk untouched, and the row stops pointing at it, so a later v resumes the conversation the restart started rather than the context it dropped. The row changes hands only once the new agent is up, so a launch that cannot start (a tool gone from PATH, a directory that moved) leaves the session on the conversation it had, still there for v.

Forking sessions

  1. Select a session and press f.
  2. Enter a name.
  3. Press enter.

The fork uses the source session's tool, group, working directory, and conversation history.

Claude Code, OpenCode, Codex, Grok, Gemini CLI, Pi, Command Code, and Muse Code include default fork commands. The source session must have a captured conversation ID.

Muse forks only from inside the running source, so f types /fork into the source session and opens the fork Muse records in a new pane. The source has to be running and at rest with nothing typed at its prompt. Otherwise f says what to do first and types nothing.

A fork shares its source session's managed worktree. Agent Manager keeps the worktree until you delete the last session that uses it. You cannot rename the worktree while another session uses it.

Self-naming sessions

Sessions spawned without a custom name (every quick spawn, and the form with the name left blank) get a placeholder like claude-a1b2, and their first prompt opens by asking the agent to run agent-manager rename "<name>" once with a short name for the broad feature of the session (not a single subtask). The directive also tells the agent not to rename again unless you ask. When the first prompt cannot carry the directive (a /slash command, or no prompt at all), the manager sends it as its own message once the tool's input box appears in the pane and the turn it launched on has ended. The subcommand drops the name into a per-session file; the manager picks it up on the next poll and updates the sidebar row and the tmux status bar. The subcommand waits for that poll and answers with the name as applied, or with the reason the session keeps its name (a worktree branch the new name cannot take, for instance). With no manager open it reports the name as queued, to be applied when one opens. This works with any tool, since it only needs the agent to read its prompt and run one shell command.

Sessions you name yourself keep that name: the first prompt only notes that agent-manager rename is available later if you ask, and does not instruct the agent to rename now. You can still ask an agent to rename its session later, or run agent-manager rename yourself from a shell inside the session.

Declaring the repo under review

A session's working directory is often an umbrella folder holding many repos, so review can only guess which one the agent means. An agent that knows which repo it is working in can say so by running agent-manager review-repo <path> from a shell inside its session. The subcommand checks that the path is (or sits inside) a git repo, resolves it to the repo root, and drops it into a per-session file; the manager picks it up on the next poll and review opens on that repo the next time you open it. A path that is not inside a git repo is rejected, so a declaration is always a fact rather than a guess.

An agent can also declare what its branch diffs against by running agent-manager review-base <ref> from inside its worktree: the ref is validated in that repo, stored per session and repo, and the "vs target" scope uses it from then on. agent-manager review-base --clear returns to the group's base, or to automatic detection when the group has none. A stored ref that stops resolving surfaces as an error in review, and B opens a target picker (the repo's branches plus an auto entry) to set or clear it by hand.

Agents usually work in git worktrees, one branch per worktree, and those worktrees can live anywhere on disk. A declared path that is a worktree root is accepted wherever it lives, so one review-repo call names both the repo and the branch under review. Review resolves its target in a fixed order: a repo or worktree you picked by hand with r or b wins for as long as the manager is running, then the agent's declared repo, then the ranking (dirty working trees first, then most recent commit). When the picked or declared path stops being a git repo, review says so in the status line and r is there to pick the right one.

MCP: how agents discover these commands

Every session of an MCP-capable tool carries the agent-manager MCP server on spawn and revive, so its agent sees the whole workspace as native tools with descriptions telling it when to call each: its own session, the other agent sessions running beside it, the groups they are filed under, and the managed terminals. No per-project setup. Pi is the shipped tool whose agent has no MCP client, so those sessions rely on the subcommands alone. The server lives in the same binary (agent-manager mcp, stdio) and identifies the calling session through its environment.

Tool Action
rename Rename the calling session
review Declare the repo under review, the base ref and the diff scope, in one call
review_comment Mark a sent review comment handled after addressing it, or reopen it
report_issue File a bug report or feature request on the agent-manager repo for the user: a preview first, and posting only with the id that preview returned
list_sessions List every agent session with its id, CLI, group, directory, worktree branch and status
create_session Start another agent CLI on a named task, optionally in its own git worktree
read_session Read what another agent's screen currently shows
send_session Queue a message for another agent, delivered once it is at rest
message_status Check whether a message is queued, held, delivered, dropped or answered
wait_for_session Park until another session stops working, instead of polling it
revive_session Bring a dead session back, resuming the conversation it held
kill_session Stop a running agent, keeping its row and last screen
archive_session File a finished session out of the active list, or restore it
task The shared work list in one tool: action is list, create, claim, finish, release or delete
reserve_files Declare the files this session is editing, and see who else claims them
release_files Give those claims back
list_reservations See what every session is editing right now
list_groups List groups with their default directories, worktree defaults and session counts
create_group Add a group, nested with a slash path, to file a fleet under
delete_group Remove a group whose work is done; sessions still filed there move to the root rather than stopping
list_terminals List active managed terminals and their current directories
create_terminal Open a terminal under the calling session, or beside it when that session is itself a terminal, unless nest is false
send_terminal Submit a command or send exact keys to a running terminal
read_terminal Read the plain-text content currently visible in a terminal
close_terminal Close a finished terminal nested under the caller: kill the pane and delete the row

Coordination

The coordination row in Settings (s) sets how far agents go with the other sessions on their own. on request, the default, tells each new session that the others exist and that it lists, messages, spawns or waits on them, and creates or claims shared tasks, only when you ask. "Spawn an agent for this" and "wait for the api session, then deploy" keep working, since every tool stays registered. proactive has agents delegate a parallel workstream to a new session and check the shared task list before starting work, without waiting to be asked. The mode reaches an MCP client through the server's initialization instructions and the create_session and task descriptions, and Pi through a note on its first prompt and the agent-manager help text. A session is briefed when it starts, so the ones already running keep the mode they started with.

Spawning and steering other agents

create_session gives an agent the same spawn the n form gives a human: a name, a CLI, a group, a working directory, a first prompt and a worktree choice. A session created this way is a normal row in the list, and the manager picks it up on its next poll, so it attaches, revives, forks and reviews like any other.

Each field falls back the way the form does. The CLI defaults to the one the calling agent runs, the group and directory default to the caller's, an explicit group uses that group's nearest inherited default path, and an explicit directory wins over both. A name is the agent's to choose and should describe the work; leaving it empty generates a placeholder and asks the new session to rename itself, exactly as a promptless spawn from the form does. Passing worktree: true adds a git worktree and branch off the directory's repo, which is what keeps several agents working in one project from editing the same checkout; omitting it inherits the group's default, then the global setting. model, effort and profile launch the CLI on values it lists instead of its defaults, the way the form's rows do (see Model, effort and profile). A value the CLI does not list is refused with the ones it does.

read_session returns the target's current screen, and its last captured screen once the session has stopped. kill_session ends the process and leaves the row dead with its last screen, revive_session brings it back on the conversation it held, and archive_session files a finished row away or restores it. An agent that quit while its window stayed open is relaunched inside that pane, so the row keeps the screen its last life left there.

Messages between agents

send_session queues a message rather than typing it immediately. Several agent CLIs keep their input line drawn underneath an approval dialog, so a message written at that moment would answer the dialog instead of being read. The manager holds it and types it in on the first poll where the target is at rest: its input region is drawn, its status is not mid-turn, its own rules report no dialog on screen, and nobody has a line part way written at its prompt. Delivery is at most once, and a message the manager cannot prove reached the pane is retired as dropped rather than repeated, so its sender knows to send it again. The list shows ✉ and the waiting count on the recipient's row until that paste lands.

The gate is the recipient tool's own rules, because text typed onto a dialog picks an option: while one is on screen the queue waits, and message_status reports those messages as held, naming the session to go and answer. A question the agent left at a resting prompt trips no rule, and a message goes in there as an ordinary turn would, labelled as coming from another agent. held also covers a recipient the manager will never type into as things stand, a session archived or stopped since the message was queued, and says which it is.

The message arrives labelled as coming from another of the user's agent sessions, with the sender's name and the id to answer on. The sender's own text sits inside a fence the manager mints at delivery, so a message written to imitate that label reads as what it is: the sender wrote it before the fence existed and cannot reproduce it. A receiving agent treats it as an instruction from the same operator and does the ordinary work the message asks. Permission prompts and that session's CLI settings stay with the person at the keyboard. Commit, push, merge, publish, and delete wait for them too. The envelope names how to reply when something is still needed, and tells the recipient to stop when the work is done. Queue caps, a per-sender rate limit, a cap of eight messages between the same two sessions in ten minutes, a size cap on one message and a whitespace-insensitive fingerprint keep two agents from talking each other into a loop. message_status reports whether a message is queued, held, delivered, dropped or answered; answering a session acknowledges everything it sent.

Delivery needs the manager running, since its poller is what types the message in. A message queued while Agent Manager is closed waits until it opens again, and send_session says so in its result rather than implying the message landed. A target that could never be reached is refused outright rather than queued: a session that is not running, an archived one, which the poller skips, and a tool declaring no activity_cutoff, which leaves nothing to read readiness from.

Waiting and the shared task list

wait_for_session parks a single tool call until a session reaches one of the states that mean it stopped working, so an agent that spawned work does not read screens in a loop while it waits. A timeout returns the session's current state with reached false, because a timeout is an answer rather than a failure; outcome separates that from the session dying before it ever reached one of the awaited states. The wait counts from the caller's own handoff. While a message it sent is still queued, the session's state describes the turn before, so the wait carries on until that message goes in and its turn ends. When a dialog or an error holds the message, the wait returns that state straight away. It is an ordinary tool call, which is what makes it work with every MCP client.

The task list is the manager's shared to-do list, visible to every session, all of it behind the one task tool. create puts work on it, claim takes a piece (by id, or the oldest one nothing is blocking), and finish marks it done, which unblocks every task that depended on it. A claim is a single atomic write, so two agents racing for the same task cannot both win: the loser is told who holds it. A session that is deleted hands its claims back to the list rather than parking them forever.

File reservations

A worktree per session stops two agents overwriting one checkout, and that is the right answer whenever the work divides cleanly. It does not help when sessions deliberately share a checkout, and it defers the other kind of collision to merge time: two agents making incompatible decisions about the same interface find out only when the branches meet.

reserve_files declares the paths a session is about to edit. Overlap with a lease another session holds comes back as conflicts, naming the holder and what they said they were doing, so the two can settle it through send_session before either commits. The lease is advisory throughout: nothing is blocked, and an agent may edit anyway. It expires on its own, so a session that dies holding one never keeps the repo to itself, and release_files hands it back as soon as the edits land. An exclusive lease conflicts with any other lease on the same paths; two shared leases sit side by side. Matching compares a pattern against a literal path in both directions, so two patterns that each contain wildcards are only compared exactly, which is another reason these are a conversation starter rather than a lock.

Terminals

create_terminal nests under the calling session unless nest is false, and a call from a terminal opens the new shell beside it: under the same agent, or un-nested in the same group when that terminal is itself un-nested. It defaults to the calling agent's group and live pane directory. A group other than the caller's needs nest: false, since a nested terminal lives in its parent's group; that group then supplies its nearest inherited default path, and an explicit directory wins over both. close_terminal kills the pane and removes the row once the job is finished, and it reaches only the terminals nested under the calling session: a shell someone else opened, or one deliberately left un-nested, is the user's to close. send_terminal accepts exactly one of a command, which is pasted and submitted with Enter, or a sequence of tmux key names such as C-c, Up, and Enter. read_terminal returns the current screen rather than unlimited scrollback.

The server's MCP initialization instructions teach agents when to use these tools. They treat the other sessions the way the coordination mode says, and open a terminal for human-visible work such as SSH, when the user should be able to watch it, attach, or take over. They list and reuse a relevant running terminal first; create_terminal nests under the caller unless nest is false; they send the command and read its screen while the job runs; and they call close_terminal when that job ends, unless the terminal is being left for the user. One-shot local commands stay in the agent's normal tools. They also say to offer report_issue when the user hits a bug in the manager itself or asks for something it lacks. The same guidance is repeated in the individual tool descriptions for clients that expose tools but not server instructions.

Every one of these tools acts on the user's machine. Agents should treat send_terminal with the same care as typing into an attached shell, and treat create_session and kill_session as what they are: starting a real agent process that spends tokens, and interrupting one that may be mid-task. Inspect the target returned by list_sessions or list_terminals first, and read the result before continuing.

Registration is per tool. Claude gets a generated --mcp-config file. Codex gets -c mcp_servers... overrides. OpenCode gets an OPENCODE_CONFIG merge file. Grok, Gemini, and Command Code each get a one-time mcp add --scope user entry on their first launch, and Antigravity a one-time agy mcp add. Muse has no command that adds a server, so its first launch writes the entry into Muse's own settings file (see Configuration). Hermes gets its own one-time mcp add flow, which needs the MCP SDK its installer treats as optional: a Hermes still missing it refuses the spawn with a dialog offering the pip install mcp line for the Python that runs Hermes, read from hermes --version, so a Hermes session always carries these tools. A spawn whose CLI is not on PATH is refused the same way, with the vendor's portable installer for a built-in agent, or the package manager on this machine for anything else. When that command is the vendor's installer, c copies it and i runs it in a shell tab named after the CLI, where you can watch it and answer its prompts; when it exits 0 and puts the CLI on PATH the refused spawn goes ahead on its own, and a failure, or an installer that leaves the CLI somewhere PATH does not name, leaves the tab open with the output and the reason on the status line. A package-manager line stays a suggestion to read, since the package that carries a tool's name is yours to choose. The dialog also hands the mouse back to the terminal while it is up, so a drag over the command selects it.

Pi does not include an MCP client. Its sessions reach the same workspace through the subcommands: agent-manager --help lists them, from sessions, spawn, send and wait to the shared task list, file reservations, terminals and the review declarations. update needs no caller at all, and issue and feature use only the session id the launch exported. Every other subcommand acts as the session or terminal it runs in, resolved from that environment or, for a terminal tab that has none, from the tmux pane, so the same subcommands work from a shell you opened with T. Claude Code also gets the id in the env block of its generated --settings file, so a conversation that /background or the agent view moves into Claude's daemon keeps acting as its own session. A spawn from a terminal tab with no --tool runs the CLI picked in Settings.

sessions, groups, spawn, read and wait also run with no caller at all, so a script, a cron job or a CI step outside Agent Manager can open sessions that show up in your list. Such a spawn runs the CLI picked in Settings, in the root group and the directory the script runs in, unless --tool, --group or --directory says otherwise:

agent-manager spawn --tool claude --group "Sprint Manager" --worktree \
  --name ticket-123 --prompt "Fix TICKET-123 and open a pull request" --json

The MCP tools always act as the session that runs them, since several CLIs keep the server registered for their runs outside Agent Manager too.

Bugs and ideas

report_issue files a bug report or a feature request on the agent-manager repository for the user, from inside the session where the problem showed up. kind is bug or feature, title is the one-line title and body the report in markdown. The tool adds the context the issue forms ask for on its own: the agent-manager version, the operating system, the tmux version, and the CLI the calling session runs with the version it reports.

A call without preview_id composes the issue and returns a preview: the title, the labels, the body exactly as it would be posted, how filing would go, and an id for that exact combination. Nothing is posted by that call. The agent shows the preview to the user and calls again with preview_id set to that id once they approve. The id covers the issue, the route and the GitHub account, so an approval that was given for a preview posting nothing cannot file once gh appears or logs in as somebody else: filing is refused and the agent previews again. One approval posts one issue: a repeated call with the same id, as after a timed-out tool call, returns the issue already filed. Filing goes through the gh CLI as the user's own GitHub account when gh is installed and logged in, and the result is the new issue's URL. Without gh, the result is the repository's issue form prefilled from the preview, field by field, for the user to open in a browser; the preview says which route applies and why. The body is public, so secrets and private paths stay out of it.

Diff review

Press ctrl+r on a session to open a full-screen review of its repo: changed files with +/− counts on the left, the whole file on the right with syntax highlighting and changed lines tinted, so every edit reads in full context. The diff refreshes as the agent keeps editing.

Tell your agent what you want to review in Agent Manager. Your agent can declare the repository or worktree, target and scope together, so the panel opens on those changes when you press ctrl+r, without you configuring each picker.

Key Action
↑↓ / jk, ctrl+d / ctrl+u, pgup / pgdn Scroll the file
g / G Jump to top / bottom
J / K (or tab / shift+tab) Next / previous file
n / N Jump between changes
u Toggle unified and side-by-side
s Cycle the scope: uncommitted, vs target, last commit, staged
r Pick the repo when the session's directory holds several (type to filter)
b Switch review to another of the repo's worktrees, listed by branch
B Pick the target (merge-into branch) the "vs target" scope compares against
space Mark a file reviewed in the current scope
f Show code files only, hiding images, compiled assets and lock files from the list; press again to show them
c / d Write or drop a draft; mark feedback from a sent round handled or open
C Send the current drafts to the agent as one review prompt (enter or y confirms, esc cancels)
o / F3 Open the current file in your editor
? Review bindings only; esc returns to the review
esc / q Close the review

Each changeable value in the header wears its own key, so the scope, layout, repo, and target pills read as s, u, r, B legends at a glance.

review, side by side, with the changed lines tinted in full file context

Comments and reviewed-file marks are saved as you work and return after Agent Manager restarts. A reviewed mark belongs to the scope it was taken in: it records that you reviewed the exact diff that scope showed, so each scope keeps its own marks and cycling scopes leaves them untouched. C sends only the current drafts as one prompt and records them as the next numbered review round. Every sent comment stays inline as the session's review history, labelled with its review round and point number. Open comments keep the accent wash; handled comments settle into a subtle dark green wash, and d toggles the status locally.

Each point sent to the agent carries a stable comment id. After addressing it, an MCP-capable agent marks it handled with review_comment; an agent using shell commands runs agent-manager review-comment <comment-id>. Either can reopen it, with handled: false or --reopen. The status is stored immediately, so opening review shows the current state at once; a panel already open picks it up on its next refresh. The comment itself remains in place. The header names the latest review round and marks it changed when the scope or the code differs from what was sent. A comment whose original code can no longer be found is marked outdated instead of silently moving to an unrelated line.

review mode: scrolling a changed file, switching to unified, jumping to the next file, then a line comment sent back to the agent

Groups

folding the tree, creating a nested group, reordering, and archiving one

Groups are paths (backend/api/auth) forming a tree of unlimited depth. Sessions can live at any node, including the root. Create subgroups inline with g, reorder both groups and sessions with K / J (or shift+↑↓; the order persists), fold a subtree with enter on its row, fold or unfold the whole tree with F, hide or restore empty groups visually with e, and edit a group's name and default path with r. On a session, r renames it and tab cycles the tool. Quitting one CLI in a session's pane and starting another there moves the row onto that CLI on the next poll, with the status rules and the revive command that come with it. The move needs one answer: exactly one built-in CLI has to run the binary the pane is running, so a CLI whose process name is its runtime rather than itself (one installed as a node script, say) leaves the row where it is and tab sets it by hand.

Status

Each session's tmux pane is polled (default every 2s) to derive a status:

Mark Status Meaning
◐ working The agent is busy on a turn
◆ waiting Blocked on you: a dialog, a permission ask, or a plain-text question
● finished Turn ended — an alert that clears to idle once you enter the session, or on .
✕ errored The tool reported an error
○ idle Nothing running
✕ dead The tmux session is gone
◌ starting The pane is still launching

Every row carries its mark, and each state has its own color from the active theme, so a glance down the rail tells you who needs you. The key map (?) lists the marks under "the mark on a session row".

A session with messages from another agent waiting to be typed in wears ✉ and the count next to its name. The count clears when the manager delivers them.

A session stuck on the wrong mark is a rules question, and the rules are ours: they ship in the binary, so an upgrade is what moves them. Configuration has what to put in the issue and how to read the pane the poller reads.

w narrows the list to sessions that need attention (waiting, finished, errored). Press again to show every status. An ATTENTION badge sits over the list with the key that clears it, and the session counts follow the filter; folds open so matches are not hidden. The archived view (t) and hidden empty groups (e) label themselves the same way. The badges take whatever room the rail has: padded away from the entries on a tall terminal, tight against them on a short one, and yielding to the entries once the list is down to its last rows.

the session tree, with a waiting agent's permission prompt in the preview

Each row carries its status and tool inline, and a folded group keeps a count per status so a collapsed subtree still tells you whether anything needs you. Selecting a session shows the tail of its pane on the right, which is how a waiting agent's actual question reaches you without attaching. A session with no window left, archived or killed, shows the snapshot taken when it still had one.

Detection matches per-tool regex rules against the visible pane, analyzes the newest turn to tell finished from waiting, and treats streaming output (content changing between polls) as working. A turn that ends without any turn-summary line still resolves: when a working pane goes quiet, the turn counts as finished, or waiting when it ends on a question. Work that outlives the turn which started it (background agents) is matched by busy_line, so a turn-end summary keeps reading as working while that work runs. A background shell or monitor can run long after the agent is done with it, so a turn that leaves one running ends like any other. When the shell finishes and wakes the agent, the turn that follows reads working until it ends. A usage or rate-limit banner (limit_line) is errored, and so is a turn whose working marker outlives the footer the tool paints only while it runs (busy_footer): opencode leaves its bare ▣ Build · <model> row behind when a provider error ends the turn. Polling keeps running while you are inside a session, so statuses stay live. The selected session's pane tail renders in the preview panel, and moving the cursor fetches the preview immediately.

For Claude Code, built-in status detection reads hook events: sessions launch with a generated --settings file whose hooks write the lifecycle state (working, waiting, finished, idle) to a per-session status file that the poller reads first. A StopFailure of rate_limit writes errored. Pane rules still refine it — hooks cannot see a plain-text question, an Esc interrupt, or an error line, so a matching pane verdict upgrades the hook status — and they take over fully as fallback when the hook file is missing or stale.

Notifications

When a session's status flips to waiting or errored, the manager fires one notification with the session name, tool, and state — once per transition, never per poll — so you can look away from the list without missing an agent that needs you. This is tool-neutral: every configured CLI reaches the same notification path after the manager classifies its status. Clicking a notification puts the cursor on the session it named, and brings the terminal running the manager to the front on macOS and on Linux under X11. On macOS the banner is the manager's own: the first time one fires, macOS asks whether Agent Manager may send notifications, and from then on every banner carries the manager's name and icon, with waiting, finished, and errored getting the Funk, Hero, and Basso sounds. The manager keeps a small app bundle of itself under ~/Library/Application Support/agent-manager for this and refreshes it after an upgrade. Should that bundle ever fail to build, the older AppleScript notification still fires so the ping is not lost; macOS attributes that one to Script Editor, and clicking it opens Script Editor. Linux sends matching standard sound, icon, category, and urgency hints through notify-send; the desktop notification server uses the capabilities it supports and safely ignores the rest. Acting on a click needs a notify-send new enough to report actions, which is libnotify 0.7.9 and later; with an older one the banner still appears and the click does nothing. Where it is supported, the click raises the terminal window on X11 (through xdotool or wmctrl when installed) while Wayland compositors leave the window where it is. Under WSL the banner is a native Windows toast posted through PowerShell, which replaces the bell there; Windows attributes a toast to whoever posted it, so that click does not reach the manager and carrying it across is left for a follow-up. Inside Ghostty or cmux the state travels as an OSC 777 escape to the drawing terminal, which owns its presentation and sound and attributes it to the right window and workspace. Because that escape rides the terminal connection, it also reaches you when the manager runs on a remote host over SSH. A headless box without a desktop falls back to the terminal bell. Settings (s) has a notifications row that silences them (on by default) and a notify on finish row that adds finished transitions (off by default).

Stats

The header shows a fleet summary: per-status session counts, plus agents total usage: cpu N% · ram M% · X GB for every live agent's full process tree (shell, agent, and children). CPU is that tree's CPU time over the last poll as a share of total machine capacity (same 0–100% unit as the computer gauge). RAM is resident set as a share of installed memory, with absolute size beside it. The selected session's detail line uses the same scale for that session alone.

The Computer block in the sessions panel shows machine gauges:

  • CPU: whole-machine utilization (0-100%)
  • Memory: used/total. On macOS this matches Activity Monitor's Memory Used (resident RAM minus free, speculative, and reclaimable file cache). On Linux it is Total - MemAvailable, so file cache is not counted as used.
  • Swap: used/total of the current swap allocation (used/total * 100). On macOS the swap file grows under pressure, so the denominator is the live size from vm.swapusage, not a fixed partition.
  • Disk: fill of the root filesystem (used / (used + available)), with free space from the kernel's available figure
  • Battery: charge percent, with charging while plugged in and filling. On macOS it is the figure the menu bar shows. It stays hidden when the machine has no battery. Peripheral batteries that report no charge (a wireless mouse on Linux) are ignored.
  • Network: up/down rates on real NICs only (loopback, utun, bridges, and similar virtual interfaces are excluded)
  • Temperature: cpu, gpu and soc readings in °C, each the hottest sensor in its category, sampled every 5s. Apple Silicon draws no CPU/GPU line, so its dies report as one soc figure. A reading appears when the machine exposes that sensor.

On Windows under WSL2, agent-manager runs inside the Linux guest, whose /proc describes the VM rather than the machine. There the CPU, memory, disk, and agent-percentage figures come from the Windows host instead, sampled through PowerShell interop every 30 seconds; swap, network, and temperatures remain the guest's own. If interop is unavailable, the guest numbers show unchanged.

Themes

s opens Settings, where ↑↓ move between fields and ←→ change the focused one.

settings, with the theme picker and its palette swatches

Seventeen palettes ship. Ten dark: classic, solarized dark, catppuccin mocha, tokyo night, gruvbox dark, nord, dracula, rosé pine, monochrome, and kanagawa wave. Seven light: solarized light, catppuccin latte, tokyo night day, gruvbox light, rosé pine dawn, paper, and kanagawa lotus. The swatch strip beside the name previews the palette, and the theme applies as you step through it, so the picker is a live preview of the whole UI. The manager also sets the terminal's own text and background colors to the palette's, so the window has no seam against it and an agent's plain text reads on it, and restores the terminal's colors on exit. Your pick is saved with the rest of the state and restored on the next run.

theme follows OS resolves the palette at startup from the environment's light/dark preference: the OS setting on macOS and Linux desktops, and the terminal's own background elsewhere, including over SSH. A theme already on the detected side stays; only a mismatch switches. A palette that ships in both a dark and a light variant switches to its other half: solarized dark and solarized light, catppuccin mocha and catppuccin latte, tokyo night and tokyo night day, gruvbox dark and gruvbox light, rosé pine and rosé pine dawn, kanagawa wave and kanagawa lotus. Any other palette switches to classic or solarized light. Your manual pick is kept separately, so turning the toggle off returns to it, and stepping the theme picker by hand turns the toggle off. Agent panes render on the theme's own backdrop and text color, and the pane declares both to the agent inside it, so an agent that auto-detects its palette resolves to the same side the manager is drawing. A session already running keeps the palette it resolved at launch until it is restarted.

background picks what fills the cells behind the UI. theme, the default, gives every cell that would otherwise show the terminal's own colors the palette's background and text color, so the colors hold even on a terminal that ignores the color request. Cells with colors of their own, agent output included, keep them. terminal leaves those cells on the terminal's own colors, for a translucent or blurred window, where painted cells would read as a solid block inside the see-through padding. The row applies as you step it.

Updates

agent-manager update brings the installed binary to the newest release from a shell, without opening the manager. An install owned by a package manager (Homebrew, mise, Arch) hands the terminal to that manager's upgrade command; a direct install downloads the newest release, verifies its checksum, and swaps the binary in place. --json prints the outcome as a record instead of a sentence.

Reporting a bug or an idea

Settings (s) has a row that opens the bug report form and one that opens the feature request form in your browser. Over SSH they print the link on the plain terminal instead, the same way a clicked link does. From a shell, agent-manager issue "<title>" --body "<text>" drafts a bug report and agent-manager feature "<title>" --body "<text>" a feature request; both print a preview with the context the forms ask for (version, OS, tmux version, and the CLI of the session the command runs in) and post nothing. Rerun with --confirm <preview-id>, passing the id the preview printed, to file it: through gh as your own account when it is installed and logged in, otherwise the command prints the prefilled issue form URL to open. The id is refused once anything it covers has changed, so what gets filed is what you read. --json prints the preview or the filed result as a record. Agents reach the same flow through the report_issue MCP tool.