# agent-manager

> agent-manager is a Go terminal UI on top of tmux that runs every AI coding agent side by side. See live status, send instant prompts, spawn isolated git worktrees, let agents delegate through MCP, open managed terminals, and review full-file diffs without leaving the list.

Every page of agent-manager.dev, concatenated as Markdown.

---

Source: https://agent-manager.dev/

---
title: "agent-manager"
description: "Run every AI coding agent side by side in one tmux TUI: live status, instant prompts, isolated git worktrees, agent-to-agent delegation, managed terminals, and in-place diff review."
canonical: "https://agent-manager.dev/"
last-updated: "2026-10-23"
section: "Site"
source: "agent-manager"
---

# agent-manager

Go · tmux · **Apache-2.0** · macOS, Linux, and Windows

# The fastest developer workflow for every AI coding agent.

**agent-manager** is a Go terminal UI on top of tmux that runs every AI coding agent side by side. Everything is one keypress: spawn one in a sentence, answer a blocked one without attaching, review its diff without leaving the list. Each session runs your own installed CLI as-is: your login, your config, your MCP servers, every feature it ships.

- [Install](/docs/install/)
- [How it compares](/compare/)

$ `brew install agent-manager`

[![YoanWai/agent-manager on Trendshift](https://trendshift.io/api/badge/trendshift/repositories/89312/daily?language=Go)](https://trendshift.io/repositories/89312?utm_source=trendshift-badge&utm_medium=badge&utm_campaign=badge-trendshift-89312) [![Live on Peerlist Launchpad](/assets/peerlist-launch-laurel.webp)](https://peerlist.io/yoanwai/project/agent-manager)

![Animated demo: five prompts send five fresh agents to work from quick prompt mode without the cursor leaving the tree, the first on Opus at high effort picked with ctrl+l and ctrl+x, each row named by the prompt it was given, then a session's diff opens in review.](/assets/demo.gif)

_Five agents sent to work, one key each, the first on its own model and effort. All of it without leaving the list._

Seen in the wild

## The launch escaped the launch post.

[All coverage →](/coverage/)

- [Hacker News](https://news.ycombinator.com/item?id=49107749): community · Show HN launch discussion
- [TLDR Dev](https://tldr.tech/dev/2026-07-31): newsletter · Launches & Tools
- [daily.dev](https://daily.dev/posts/github---yoanwai-agent-manager-terminal-ui-to-manage-ai-coding-agent-sessions-claude-code-opencod-mikayess2): syndication · Hacker News pickup and discussion
- [Product Hunt](https://www.producthunt.com/products/agent-manager): launch · Featured launch, August 2026
- [Peerlist Launchpad](https://peerlist.io/yoanwai/project/agent-manager): launch · Live on week 35, August 2026
- [Trendshift](https://trendshift.io/repositories/89312): trending · Reached the daily trending list
- [Claude Workshop](https://www.claudeworkshop.com/research/agent-manager-puts-ai-agents-in-tmux): field note · Reviewing agents where they run

## Every task gets its own agent, seconds apart.

**A sentence is the whole ceremony.** Hit `space` on a group row, type the task, press enter: that agent is already running, with your prompt embedded and the group's directory set. No form, no `cd`, no naming. The bar clears and stays open, so the next task goes to the next agent immediately, in a different project if you want. On a session row the same key answers an agent that is already working. When the work needs its own branch, `ctrl+t` in the same bar spawns the agent into a fresh git worktree.

### `space` One sentence, one agent

Type what it should do, press enter, repeat. They go out as fast as you can describe them, each already working, across as many projects as you keep open, and you never left the list to start them.

### `tab` A different tool and model per spawn

Cycle claude, opencode, codex, grok, gemini, agy, pi, cmd, hermes, muse or omp without leaving the bar. `ctrl+l`, `ctrl+x` and `ctrl+y` pick its model, reasoning effort and profile, read live from the CLI. The target row shows what the next enter will start, and each CLI remembers your last picks.

### `space` Answer in place

On a session row the same key sends your reply into that agent's pane as a user message, so a blocked agent never costs you an attach.

### `auto` They name themselves

A session starts as `claude-a1b2` and renames itself once the agent knows what the work is, so a screen of them still reads as a list of features.

### `mcp` Agents spawn agents

An agent can work the same list you do: spawn a session on its own task, read another one's screen, message it, and wait on one before taking its next step. Repo work takes a worktree each, and sessions sharing a checkout declare the files they are about to edit, so an overlap surfaces before either commits. [The tools they call](/docs/mcp/).

Six states, and for Claude Code they arrive from its own hook events rather than from guessing at the pane. [How detection works](/docs/status/).

**working**Mid-turn. Editing, running tests, thinking.

**waiting**Blocked on you. A permission prompt or a question.

**finished**Turn complete, nothing pending.

**errored**The agent reported a failure.

**idle**Alive at a prompt with nothing to do.

**dead**Session ended. The row and its history stay.

![Animated demo: space opens quick prompt mode on a finished Claude Code session, and the question goes straight into its pane, which turns from finished to working and starts answering.](/assets/demo-space.gif)

_The reply lands in the agent's pane as a user message, and on a group the same bar spawns. No attach, no tab switch._

![The sessions view: twenty-odd agent sessions grouped under backend, its api subgroup, frontend and infra, each with a colour-coded live status, a real Claude Code transcript in the preview on the right, and machine gauges at the bottom left.](/assets/screenshot-sessions.png)

_A folded group keeps its per-status counts, so a collapsed subtree still says whether anything under it needs you._

---

## However many you run, it still reads as a list of projects.

**Groups are paths, not folders.** `backend/api/auth` nests as deep as the work does, sessions live at any node, and everything you arrange stays arranged across restarts. Fold what you are not looking at, archive what is finished, and the fleet stops looking like a wall of processes.

### `↵` Fold, and still know

A folded group keeps a count per status on its row, so a collapsed subtree still says whether anything under it is blocked on you. `F` folds or unfolds the whole tree at once.

### `g` Nest as deep as you want

Make a subgroup inline while spawning, give it a default path, and every agent started there lands in the right directory.

### `K` `J` Order that sticks

Reorder sessions among their siblings or whole groups among theirs. `m` moves a session elsewhere, and the arrangement survives a restart.

### `a` Archive the finished ones

Park a session, or a group and its entire subtree, out of the view. Archive ends the process and keeps the last preview; `u` resumes it, and `t` shows what is parked. [Groups in full](/docs/sessions/).

The arrows walk the same tree sideways: `→` steps into the row under the cursor, focusing the session or opening the group, and `←` steps back out, closing the group or leaving a focused agent from the head of its prompt. The pair is in beta, and Settings has a row that turns it off.

The shells you keep next to the agents live here too: `T` opens a terminal under the selected agent, or in the selected group, for builds, Git, and one-off commands. A nested shell is named after the session it hangs under.

![Animated demo: a group folds down to one row keeping its per-status counts, the whole tree folds and unfolds at once, a session moves down and back among its siblings, a nested group is created, then a session is archived and found again in the archived view.](/assets/demo-groups.gif)

_A folded subtree still says whether anything under it is blocked on you._

---

## The mouse works across the whole manager.

**Every row, pane and seam answers to it.** The wheel moves the cursor, a click focuses a session, and a click on its row or the mouse back button brings you back. Drag a row by its `⠿` handle to reorder it or move it into another group, and the `[…]` at the end of every row opens its actions. The divider between the list and the preview drags, and a click on `messages` in the key legend opens them.

### Click to focus, drag to move

One click focuses a session, the same as `→`, and a double click folds a group. The `⠿` handle drags a row into place, and `[…]` or a right click lists everything the row can do.

### Drag to copy, click to open

In a focused pane a drag selects text and copies it, and a click on a link opens it in your browser. An agent that tracks the mouse itself still gets a plain click.

### The wheel stays inside

The manager claims mouse reporting, so a wheel notch moves the cursor, scrolls the diff, or walks a focused pane's scrollback, and never scrolls the manager away.

### Yours to turn off

Settings has a mouse row, on by default. Off hands the terminal back its native selection over the list and the preview. [Every key and click](/docs/keys/).

![Animated demo: 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.](/assets/demo-mouse.gif)

_Three real Claude Code sessions, driven by the mouse alone._

---

## Review mode, one key from the list.

**The manager becomes a reviewer.** `ctrl+r` takes the whole screen for the selected session's repo: changed files with +/- counts on the left, the entire file on the right, syntax highlighted with the changed lines tinted, and its own keymap while you are in there. It refreshes as the agent keeps editing, and `esc` puts you back on the row you came from.

A session's directory is often an umbrella folder holding a dozen repos, so the agent settles it rather than you: through the MCP tools it declares the repo or worktree it moved into and the branch its work merges into, both validated against git, and review opens there. Nothing declared? Dirty working trees rank first, then the most recent commit. [How it resolves](/docs/review/#which-repo).

![Animated demo of the review screen: a changed Go file scrolled side by side, the layout switched to unified, the next file opened, then a line comment sent back to the agent, which starts working on it.](/assets/demo-diff.gif)

_Fig. 6ctrl+r · review mode_

### `auto` It targets itself

The agent's declared repo and merge base win over the ranking; worktrees are found wherever they live on disk.

### `r` `b` `B` Or you take over

Pick the repo, the branch from its worktrees, or the target the diff compares against. Your pick wins for as long as the manager runs.

### `s` Four scopes

Uncommitted, versus the merge target, the last commit, or staged, so the same screen works before and after a commit.

### `u` Split or unified

Toggle the layout in place, cursor held on the same source line. `n` jumps to the next change.

Then the part the others on the [comparison](/compare/) only do in a browser:

**Comment a line, and the agent gets your notes.** `c` writes a note on the line under the cursor and they stay on the review screen until you send them. `C` flattens every note into one review prompt and delivers it into that agent's pane, so it starts on your feedback while you watch the diff update. [The review loop](/docs/review/#comments).

---

## Kill it for the RAM. Revive the same conversation.

The process and the record are separate things, so ending one never costs you the other.

### `x` Kill, keep the row

The tmux session ends and the RAM comes back. The row stays, marked dead, name and conversation id intact.

### `v` Revive, same thread

Relaunches on _that exact conversation_ through the tool's own resume command, not a fresh one.

### `↑` The row remembers

A killed session still shows the snapshot of its pane from when it had one, so the row says what the agent was doing when it stopped. [How sessions keep their history](/docs/sessions/).

### `f` Fork the thread

A named sibling continues the same conversation, so one copy explores an idea while the original keeps working. Each fork is a full session of its own, review included.

![Animated demo: x on a group ends every session under it and the rows stay, marked dead with their snapshots intact, then v and one confirm relaunch the whole subtree, each session back on the conversation it was holding.](/assets/demo-revive.gif)

_The process and the record are separate things, so the RAM comes back and the thread does not._

---

## The agents it knows.

Every spawn is the tool you already installed, launched with the config, login, and subscription it already has. `tab` cycles the tool the next spawn will use, and the `n` form or `ctrl+l`, `ctrl+x` and `ctrl+y` in the bar pick the model, effort and profile that tool lists. Live status ships for these:

Claude CodeOpenCodeCodex Grok BuildGemini CLIAntigravity CLIPiCommand CodeHermes AgentMuse CodeOh My PiCursor CLI

Each profile lives in the binary, rules included, so a release that fixes a CLI's new screen reaches everyone on the next update. Settings picks which ones the pickers offer, and a CLI outside the list is one prefilled request away. [The twelve, and how a new one gets in](/docs/agents/).

The manager registers its own MCP server into every MCP-capable session it spawns, so the agent can drive the workspace: spawn a teammate, send it a message, wait until it is done, and open a terminal the user can watch. [How registration works](/docs/mcp/).

- `create_session`Starts another agent on a named task, in its own worktree if the work needs one.
- `send_session`Queues a message for another agent, delivered once it is at rest.
- `create_terminal`Opens a shell under the caller for work the user should see.
- `review`Declares the repo, merge base and diff scope so review opens in the right place.

---

## Questions

### What is agent-manager?

agent-manager is a terminal UI written in Go that runs every AI coding agent as a live tmux session. Each row in its list is a real tmux session running a real coding CLI, coloured by what that CLI's pane is doing, so a fleet of agents is read, answered and reviewed from one screen.

### Which coding CLIs does agent-manager support?

Twelve, as of v0.43.0: Claude Code, OpenCode, Codex, Grok Build, Gemini CLI, Antigravity CLI, Pi, Command Code, Hermes Agent, Muse Code, Oh My Pi and Cursor CLI. Each one's launch command, resume and fork commands, MCP registration and status rules are built into the binary, so an upgrade brings the current version of all of them. Hermes, Antigravity, Oh My Pi and Cursor CLI have no fork. A CLI outside that list is a feature request, and the CLIs row in Settings opens one prefilled.

### Can each session run on its own model?

Yes, since v0.40.0. The new-session form has model and effort rows, plus a profile row for a CLI that has profiles, and quick prompt mode picks the same with ctrl+l, ctrl+x and ctrl+y. agent-manager spawn takes --model, --effort and --profile. The lists are read live from each CLI, so a model the CLI ships tomorrow shows up without an agent-manager update. Each CLI keeps your last picks, and the choice is saved on the session, so restart, revive and fork run on it.

### What does agent-manager cost?

Nothing. It is free and open source under the Apache-2.0 licence, with no account, no licence key and no paid tier. The coding CLIs it runs are billed by their own vendors.

### What licence is agent-manager under?

Apache-2.0, since v0.19.0 on 4 August 2026. Releases up to v0.18.0 shipped under MIT, which is why write-ups from July 2026 list MIT. The LICENSE and NOTICE files, GitHub's licence detection and the Homebrew and AUR packages all say Apache-2.0.

### Does agent-manager support the mouse?

Yes, since v0.38.0. The wheel moves the cursor, a click focuses a session (a double click in the full-screen layout), a row drags by its handle to reorder it or move it into another group, and the menu at the end of every row opens its actions. The divider between the list and the preview drags. In a focused pane a drag selects and copies text, and a click on a link opens it. Since v0.43.0 Settings, Review, Messages and the keys at the bottom take clicks too, and a click runs when the button comes back up. Settings has a mouse toggle that hands native selection back to the terminal.

### Can agent-manager run agents on another machine?

Yes, since v0.43.0. C adds an SSH connection to another Linux or macOS machine that runs agent-manager, and its groups, sessions and terminals show up in this list. You can attach, prompt, start, kill, revive or archive an agent there. It needs an SSH key that never asks for a password, and a host you have connected to once by hand. Rename, move, fork and review are not available on a session there yet.

### Which platforms does agent-manager run on?

macOS and Linux on both amd64 and arm64. Windows runs natively on psmux 3.4 or newer with PowerShell 7.3 or newer, and it still runs inside WSL2. macOS, Linux and WSL2 need tmux 3.1 or newer and git.

### How do I install agent-manager?

brew install agent-manager on macOS or Linux, from homebrew-core. On Windows, scoop bucket add yoanwai https://github.com/YoanWai/scoop-bucket, then scoop install yoanwai/agent-manager, which also installs psmux. There is also a Homebrew cask for the prebuilt binary, an install script you download from the release and read before it runs, an AUR package, mise, go install, and prebuilt binaries for every release.

### Can an agent drive agent-manager?

Yes. The binary carries an MCP server that registers into every MCP-capable session it starts, so an agent can spawn other agents, message them, wait on them, claim shared tasks, reserve files and drive terminals on the developer's own machine.

### Does agent-manager replace Claude Code, Codex or OpenCode?

No. It starts the CLIs you already installed, with their login, config, MCP servers and features unchanged. It reads each pane the way a human would, or Claude Code hook events, and colours the row by what that CLI is doing.

### How does agent-manager compare to herdr and plain tmux?

agent-manager is a Go TUI on tmux, so sessions stay alive if you quit it and you answer or review from the list. herdr replaces tmux with its own runtime. Plain tmux keeps sessions alive but cannot tell which agent is blocked. The feature table is at https://agent-manager.dev/compare/.

### How is agent-manager different from a Claude Code tmux session picker?

A picker such as tmux-claude-hatch or claude-tmux lists the Claude Code sessions in tmux and jumps to one. agent-manager runs twelve coding CLIs, answers a session without attaching, revives a dead one on its own conversation, and reviews its diff with line comments that go back to the agent. The pickers are lighter, and they find Claude sessions you started by hand. The comparison, lazyclaude and ccmux included, is at https://agent-manager.dev/compare/#claude-code-tmux.

### Can I run several coding agents at once?

Yes. That is what it is for. Each agent is its own tmux session in one list, grouped by project, with live status so you can see which is working, which is waiting on you, and which has finished.

### Does agent-manager send my code or prompts to a cloud service?

No. It is a local binary. Sessions run on your machine as tmux sessions. Nothing about your sessions, prompts, code or repositories is sent anywhere. The coding CLIs you start talk to their own vendors; agent-manager does not proxy them.

---

## Quit. They keep working.

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. Close the manager, close the laptop: they are still there when you come back.

**Not here yet:** cost tracking. If that is what you came for, the [comparison](/compare/) says which project does it.

- [Install it](/docs/install/)
- [Read the docs](/docs/)
- [Source ↗](https://github.com/YoanWai/agent-manager)

---

Source: https://agent-manager.dev/compare/

---
title: "How it compares"
description: "agent-manager against herdr, Agent of Empires, agent-deck, Vibe Kanban, claude-squad, plain tmux and the Claude Code tmux managers, feature by feature."
canonical: "https://agent-manager.dev/compare/"
last-updated: "2026-10-25"
section: "Site"
source: "agent-manager"
---

# How it compares

Compare

# Where this one wins, and where it doesn't.

Every cell comes from that project's own docs, including the rows where they beat us.

|  | agent-manager | herdr | Agent of Empires | agent-deck | Vibe Kanban | claude-squad | plain tmux |
| --- | --- | --- | --- | --- | --- | --- | --- |
| Where it lives | terminal | terminal | terminal + browser | terminal + web | browser | terminal | terminal |
| Live agent status | yes | yes | yes | yes | yes | yes | no |
| Status from hook events | yes | yes | yes, on by default | yes, on by default | not documented | pane matching | no |
| New agent in one keypress | yes, `space` or `n` | command or API | `n`, then a dialog | yes, `n` | `ctrl+k`, then `n` | yes, `n` | new-session, by hand |
| Sessions name themselves | yes, the agent renames it | no | yes, from the first turn | yes, from the tool's own name | yes, from the task text | you name it | numbered by default |
| Quick prompt, one keypress from the list | yes, `space` | CLI and socket API | `tab`, keys relayed | yes, `o`, one line | open the workspace first | at creation only | send-keys, by hand |
| Spawn a new agent with the prompt embedded | yes, on a group row | start, then prompt | plugin API only | `launch -m`, or the `n` dialog | yes, per workspace | at creation | no |
| Retarget mid-sentence, bar stays open | yes | no | no | no | no | no | no |
| Paste a clipboard image into the prompt | yes, `ctrl+v` | yes, `alt+v` | yes, in the browser | no | picker and drag | no | no |
| Whole-file diff review | yes | no | hunks, split or unified | no | yes, in the browser | one unified diff | no |
| Line comments back to the agent | yes, in the terminal | no | web, structured sessions | no | yes, in the browser | no | no |
| Resume a dead session | same conversation | snapshot and resume | yes, Claude | yes, `session restart` | not documented | pause, resume | no |
| Folded group keeps per-status counts | yes, any depth | one rolled-up icon | a total count | running and waiting | sidebar groups by status | flat list | no |
| Agent drives the manager over MCP | yes, spawn, message, terminals, review | socket API instead | forwards yours | attaches yours | yes, its own server | no | no |
| Agents declare the files they are editing | yes, advisory leases that expire | worktree isolation only | worktree isolation only | worktree isolation only | worktree isolation only | worktree isolation only | no |
| Worktree creation | yes, `n` / `ctrl+t` | yes | yes | yes, deepest here | yes | yes | no |
| Opens pull requests | no | not documented | not documented | merges locally | yes | yes, `p` pushes | no |
| Mouse and Windows | native Windows and WSL2, the mouse on every screen | both | mouse, WSL2 only | mouse, WSL2 only | both | mouse, WSL for tmux | mouse only |

## Where each one fits

### plain tmux one or two agents

The honest baseline, and what this is built on. It keeps sessions alive, but it cannot say which agent needs you, because it has no idea what runs inside a pane.

### herdr replace the terminal

Its own Rust runtime instead of tmux, so panes and workspaces are native. Mouse-first, a socket API, a plugin marketplace, and Windows in beta.

### Vibe Kanban work in a browser

A board where each issue gets a workspace, a branch and a terminal, and its diff review sends line comments back to the agent too. The project is [sunsetting](https://www.vibekanban.com/blog/shutdown).

### claude-squad straight to a pull request

Every session gets its own worktree and branch, so parallel agents cannot collide, and `p` commits and pushes it.

### agent-deck worktrees and spend

The deepest worktree support of any of them, monorepo sparse-checkout included, plus a cost dashboard with budgets, session forking and Docker sandboxing.

### Agent of Empires reach your agents from a phone

The same tmux foundation as this one, plus a web dashboard that renders the real terminal in a browser, git worktrees, and Docker sandboxing. Its diff comments are web-only; the TUI diff sends you to `$EDITOR`.

### this one the friction is the problem

One key spawns an agent, one key answers a blocked one, one key opens review mode. Then `C` sends your line comments into that agent's pane. Of the tools on this page, only ccmux also does that in the terminal, through hunk.

## Claude Code session managers for tmux

Search for a tmux manager for Claude Code and most of page one is a picker. A picker is a popup that lists the Claude sessions in your tmux and jumps to one, and several do that well. The difference is what you can do from the list without jumping.

|  | agent-manager | tmux-claude-hatch | claude-tmux | lazyclaude | ccmux |
| --- | --- | --- | --- | --- | --- |
| What it is | a TUI on its own tmux server | a tmux plugin with popups and an fzf picker | a popup TUI | a popup TUI on its own tmux server | a TUI, a CLI and a local daemon |
| Coding CLIs | twelve built in | Claude Code only | Claude Code only | Claude Code only | nine built in, plus your own |
| Live status from | hook events and pane rules | Claude’s own session files | pane text | hook events | hooks, logs and pane text |
| Answer an agent without attaching | yes, `space` | no, it copies the pane target | no | yes, permission popups and `msg send` | yes, `ccmux send` or the preview |
| Finds Claude sessions you started by hand | no, its own sessions only | yes, any pane | yes, any tmux session | no, its own sessions only | yes, any pane |
| Worktrees | yes | no | yes, and pull requests through `gh` | yes | yes, also from a PR or an issue |
| Diff review, comments back to the agent | yes, whole files | no | no | one edit at a time, at approval | yes, through hunk |
| Revive a dead session on its conversation | yes, `v` | no | no | yes, `sessions resume` | restarts while the pane lives |
| Mouse | every screen | the fzf picker | no | the wheel | click and right click |
| Licence | Apache-2.0 | MIT | AGPL-3.0 | MIT | MIT |
| Latest release | v0.43.2, 24 Oct 2026 | v1.5.0, 28 Sep 2026 | v0.4.0, April 2026 | v0.2.0, 13 Apr 2026 | v1.4.2, 21 Sep 2026 |

### tmux-claude-hatch one popup per project

craftzdog’s plugin, formerly tmux-claude-session-manager. It lives in your own tmux through TPM, reads status from Claude Code’s session files with no hooks to install, lists every Claude in any pane, and rings a bell on the window that needs you.

### claude-tmux sessions to a pull request

A Rust popup that lists the tmux sessions running Claude Code, reads their state from the pane, and creates worktrees and pull requests through `gh`.

### lazyclaude approve from anywhere

Permission prompts from every session pop up over whatever you are doing, with the proposed edit as a diff and one key to approve. It also runs a manager and workers in worktrees. Its last release was in April.

### ccmux many agents, found where they run

The closest to this one: several CLIs, worktrees cut from a pull request or an issue, review through hunk whose notes go back as one prompt, and desktop notifications with approve and reply buttons. A daemon on localhost keeps the state.

**Not here yet:** cost tracking and pull requests. The cards above name who has each.

---

Source: https://agent-manager.dev/coverage/

---
title: "Community and coverage"
description: "Articles, field notes, community discussion and discovery pages covering agent-manager."
canonical: "https://agent-manager.dev/coverage/"
last-updated: "2026-10-23"
section: "Site"
source: "agent-manager"
---

# Community and coverage

Community & coverage

# agent-manager, out in the wild.

Articles, field notes and community discussion from outside the project. Discovery indexes and automated syndication are listed separately, because a listing is not a review.

## Articles and field notes

Original writing that goes beyond repeating the repository description.

- [TLDR Dev](https://tldr.tech/dev/2026-07-31): Included Agent Manager in the newsletter’s Launches & Tools section. · newsletter · 31 Jul
- [Developers Digest](https://www.developersdigest.tech/blog/agent-manager-tmux-tui-claude-code-codex-opencode): A walkthrough of the tmux TUI for running Claude Code, Codex and OpenCode side by side. · article · 30 Jul
- [Developers Digest](https://www.developersdigest.tech/blog/multi-agent-cli-orchestration-tools-compared-2026): Compared with Pane and Golutra as multi-agent CLI orchestration tools. · comparison · 30 Jul
- [AI Heartland](https://ai-heartland.com/tool/agent-manager-tmux-tui/): Installed v0.17.0, checked the release checksum and tested behavior without tmux. · field test · 日本語 · 2 Aug
- [Lab Memo](https://labmemo.com/agent-manager-tmux-tui-claude-code-codex-opencode-grok-integration-2026/): A technical analysis of status detection, MCP, review and the tmux architecture. · analysis · 日本語 · 31 Jul
- [AI创业内参](https://www.xopcx.com/articles/article-2026-07-31-agent-manager-tmux-tui): A long-form guide built around a complete multi-agent working day. · guide · 中文 · 31 Jul
- [Claude Workshop](https://www.claudeworkshop.com/research/agent-manager-puts-ai-agents-in-tmux): A review-focused field note on watching panes and sending comments back to Claude Code. · field note · 31 Jul
- [Codex Workshop](https://www.codexworkshop.com/research/agent-manager-gives-codex-a-tmux-dashboard): A Codex-specific look at durable sessions, prompts and repository review. · field note · 31 Jul
- [Moltbook](https://www.moltbook.com/post/ab43c407-e47d-4514-a678-081079ed2d5c): An essay arguing that coding agents should be managed as processes rather than browser tabs. · community essay
- [quidproquo](https://quidproquo.cc/posts/daily/2026-08-26-tool-agent-manager-en/): Tool-of-the-day walkthrough of install, review mode, MCP tools and comparison with Pane and Golutra. · article · 26 Aug
- [Open Intelligence](https://medium.com/open-intelligence/stop-hunting-through-terminal-tabs-for-your-coding-agents-14da2f675eeb): A walkthrough of the thin-layer design, the live status tree, quick prompts, revival and diff review. · article · 27 Sep
- [heyaican](https://heyaican.com/en/projects/agent-manager/): An architecture teardown of the status poller, private tmux server, review loop and release process, read from the source. · teardown · 1 Oct
- [Julie Ng](https://github.com/julie-ng/autonomous-agents-setup/blob/HEAD/stale/session-management.stale.md): An evaluation note comparing herdr and agent-manager for an autonomous-agent setup, naming agent-manager the frontrunner. · field note · Aug
- [open-seed](https://github.com/shaunlmason/open-seed/blob/HEAD/docs/research/01-terminal-tui-orchestrators.md): A survey of terminal orchestrators with a section on agent-manager’s topology, status detection and worktree conventions. · survey · 22 Aug
- [capture-board](https://github.com/qdriven/capture-board/blob/HEAD/references/analysis/projects/agent-manager.md): A Chinese single-project analysis that positions agent-manager as the session layer beside a task board. · analysis · 中文 · 18 Aug

Most of this writing dates from launch week in July 2026 and describes releases up to v0.18.0. Since then the licence moved from MIT to Apache-2.0 in v0.19.0, the mouse arrived in v0.38.0, the built-in CLIs grew to nine with Muse Code in v0.39.0, and `brew install agent-manager` installs from homebrew-core. The [questions on the home page](/#faq) carry the current answers.

---

## Community signal

Where developers discussed it, rather than where an editor reviewed it.

- [Hacker News](https://news.ycombinator.com/item?id=49107749): The Show HN launch discussion. · community · 30 Jul
- [Trendshift](https://trendshift.io/repositories/89312): Reached Trendshift’s daily trending list. · trending · 30 Jul
- [Product Hunt](https://www.producthunt.com/products/agent-manager): A featured launch, with the maker thread on review mode and running several agents at once. · launch · 11 Aug
- [Peerlist Launchpad](https://peerlist.io/yoanwai/project/agent-manager): Live on the week-35 Launchpad, under the maker’s own profile. · launch · 25 Aug
- [Dan Kornas](https://x.com/DanKornas/status/2103858135240773950): A feature thread on the live session tree, quick prompts, revive and full-file review. · post · 26 Sep

![Agent Manager is live on Peerlist Launchpad](/assets/peerlist-launch-card.png)

---

## Discovery and syndication

Useful places people can find the project. These are indexes or automated pickups, not editorial endorsements.

- [daily.dev](https://daily.dev/posts/github---yoanwai-agent-manager-terminal-ui-to-manage-ai-coding-agent-sessions-claude-code-opencod-mikayess2): Hacker News pickup with its own summary and discussion. · syndication · 30 Jul
- [EveryDev](https://www.everydev.ai/tools/agent-manager): Featured developer-tool listing with a detailed product overview. · directory · 1 Aug
- [News roundup](https://note.com/lucid_lynx8370/n/n60f16237331c): Japanese Hacker News digest summarizing the project and workflow. · roundup · 日本語
- [狗蛋AI](https://daily.goudanai.com/2026-07-30.html): Daily briefing inclusion as both an AI news item and product recommendation. · briefing · 中文 · 30 Jul
- [AIgregator](https://aigregator.news/digests/2026-07-30.html): Included in the July 30 AI daily digest. · digest · 30 Jul
- [o16g](https://o16g.com/resources/): Curated resource entry for multi-agent teamwork and orchestration. · resource index · 30 Jul
- [LibHunt](https://www.libhunt.com/posts/1527533-agent-manager-a-tmux-tui-for-running-claude-code-codex-and-opencode): Project and Hacker News discussion index. · index · 30 Jul
- [MemeData](https://memedata.com/post/135895): Chinese summary and translation of the launch discussion. · syndication · 中文
- [uniget](https://tools.uniget.dev/tools/agent-manager/): Packaged in the uniget tool registry, with releases tracked automatically. · package index · 3 Aug
- [Awesome TUI](https://awesometui.com/agent-manager): Catalog listing that mirrors the README and latest release notes for the tmux TUI. · directory
- [imjee1](https://imjee1.github.io/ai-tool-blog/blog/2026-07-31-ai-tools/): Korean Hacker News digest with a short rec of the tmux TUI for Claude Code, Codex and OpenCode. · roundup · 한국어 · 31 Jul
- [cmdSpace](https://github.com/codeoneveryday-labs/cmdSpace/blob/main/docs/reports/awesome-agent-orchestrators-review-2026-08-31.md): Vietnamese survey of awesome-agent-orchestrators that includes agent-manager among terminal TUIs. · survey · Tiếng Việt · 31 Aug
- [All the Agents](https://alltheagents.org/agents/agent-manager/): Catalog entry listing agent-manager as a coding-agent multiplexer. · directory
- [awesome-cli-coding-agents](https://github.com/bradAGI/awesome-cli-coding-agents): A curated list entry describing per-pane status, prompts without attaching, worktrees and full-file review. · directory
- [pkg.so](https://github.com/mxcl/pkg.so/blob/HEAD/agents/agent-manager.yml): A package and agent index entry with config locations, docs links and a usage summary. · package index
- [Agent Skill Exchange](https://github.com/agentskillexchange/skills/blob/HEAD/skills/supervise-tmux-based-coding-agent-sessions-with-agent-manager/SKILL.md): A published agent skill for supervising tmux-based coding-agent sessions with agent-manager. · skill
- [OSINTech Timeline](https://github.com/bormaxi8080/osint-timeline/blob/HEAD/timelines/osintech-timeline_182_01.10.2026.md): A weekly tool digest entry for the tmux TUI. · digest · 1 Oct

This page intentionally distinguishes original writing from automated discovery. Inclusion here records where the project appeared; it does not imply endorsement.

---

Source: https://agent-manager.dev/about/

---
title: "About"
description: "What agent-manager is, who builds it, how it is licensed, and what an agent should know about it."
canonical: "https://agent-manager.dev/about/"
last-updated: "2026-10-23"
section: "Site"
source: "agent-manager"
---

# About

Run every AI coding agent side by side in one tmux TUI: live status, instant prompts, isolated git worktrees, agent-to-agent delegation, managed terminals, and in-place diff review.

## What it is

agent-manager is a single Go binary that sits on top of tmux. Each row in its list is a real tmux session running a real coding CLI: your own installation, your login, your config, your MCP servers. The manager never wraps or re-implements an agent. It reads the same pane you would read, colours the row by what that pane is actually doing, and gives you one keypress to answer it, spawn another beside it, or open its diff.

Twelve CLIs ship with working status detection out of the box: Claude Code, OpenCode, Codex, Grok Build, Gemini CLI, Antigravity CLI, Pi, Command Code, Hermes Agent, Muse Code, Oh My Pi and Cursor CLI. Each one's launch command, resume and fork commands, MCP registration and status rules live in the binary, so a release that fixes a CLI's new screen reaches everyone on the next update. Status is a handful of regexes over the text tmux hands back rather than a per-vendor integration, and a CLI outside the list is a feature request the manager opens prefilled from Settings.

## Who builds it

agent-manager is built by Yoan Wai and released under the Apache-2.0 licence. The source, the issue tracker, the release history and the contribution guide all live in one public repository on GitHub, and every release is built and published from it. Outside contributions arrive through pull requests there.

## How it got here

The project was launched publicly on Hacker News in July 2026 and featured on Product Hunt in August 2026. Releases have shipped continuously since, through Homebrew, an install script, the AUR, mise, `go install` and prebuilt binaries for macOS, Linux and Windows, amd64 and arm64. Windows also runs it inside WSL2.

## What agents should know

agent-manager is a local developer tool. It runs on a developer's own machine against local tmux sessions; it is not a hosted service, and there is no account to create. An agent that wants to drive it does so through the MCP server that ships inside the binary. This website publishes the project's reference data (the documentation, the CLIs it manages, the MCP tools it exposes and the current release) as machine-readable files and a small read-only API.

---

Source: https://agent-manager.dev/contact/

---
title: "Contact"
description: "Where to report a bug, ask a question, report a vulnerability or contribute to agent-manager."
canonical: "https://agent-manager.dev/contact/"
last-updated: "2026-10-21"
section: "Site"
source: "agent-manager"
---

# Contact

Everything about agent-manager happens in the open, in one public repository. Bugs, questions and security reports each have their own front door there.

## Report a bug or request a feature

Open an issue at [github.com/YoanWai/agent-manager/issues](https://github.com/YoanWai/agent-manager/issues). The issue templates ask for your platform, your tmux version, the CLI you were running and the output of `agent-manager --version`, because those four facts resolve most reports on the first reply. A pane that behaves unexpectedly is best reported with the output of `tmux capture-pane -p`, since that text is exactly what the status rules match against.

## Ask a question

Use [GitHub Discussions](https://github.com/YoanWai/agent-manager/discussions) for usage questions, configuration help, and profiles for coding CLIs that do not ship in the defaults. Profiles posted there are how new agents reach everyone else.

## Report a vulnerability

Report privately through [GitHub's private vulnerability reporting](https://github.com/YoanWai/agent-manager/security/advisories/new), which opens an advisory visible only to the maintainer. The repository's `SECURITY.md` carries the policy: what to include, and the acknowledgement and assessment it commits to.

## Contribute

Pull requests are welcome. `CONTRIBUTING.md` in the repository covers the build, the test suite and the commit conventions the project uses, and the code of conduct applies to every space listed on this page.

## Email

For anything that does not belong in a public thread, write to [contact@agent-manager.dev](mailto:contact@agent-manager.dev). Bugs and questions are answered faster in the tracker above, because the answer stays where the next person with the same problem will find it. Vulnerabilities go through the private advisory, not here.

## If you are an agent

There is no contact API and no support inbox to call. Start from [/llms.txt](/llms.txt), which lists every documentation page and machine-readable file this site publishes, or connect to the MCP server at [/mcp](/docs/mcp/) and call `tools/list`.

---

Source: https://agent-manager.dev/privacy/

---
title: "Privacy"
description: "What this site collects, what the agent-manager binary sends, and what never leaves your machine."
canonical: "https://agent-manager.dev/privacy/"
last-updated: "2026-09-29"
section: "Site"
source: "agent-manager"
---

# Privacy

This website sets no cookies and counts visits with Cloudflare Web Analytics, which identifies no one. The agent-manager binary sends no telemetry. What follows is the complete list of what is collected and what leaves your machine.

## This website

agent-manager.dev is a static site on Cloudflare Workers, and it sets no cookies. Every page carries the Cloudflare Web Analytics script, which counts page views and measures how fast pages load. It stores nothing in the browser, sets no cookies and does not fingerprint visitors. Cloudflare also records standard request logs for the pages and files it serves (IP address, user agent, requested path, timestamp) as part of operating and protecting the network. Those logs are Cloudflare's and are retained under Cloudflare's own policy. This project reads the traffic figures Cloudflare derives from them, such as views per page and requests per crawler, and never exports them or joins them with anything else.

Two third parties appear on some pages: a Product Hunt review badge in the footer and a Trendshift badge on the home page, both loaded as images from those services, which see the request the way any embedded image host does. The read-only API and the MCP endpoint on this domain require no account, no key and no identifier, and requests to them are not logged beyond Cloudflare's standard request logs.

## The agent-manager binary

agent-manager collects no telemetry, no analytics and no usage data. It has no account system, no licence check and nothing to sign in to. It makes exactly three kinds of outbound request, all unauthenticated public GETs:

- GitHub's public releases API, to notice that a newer version exists.
- A release asset on GitHub, only when you ask it to upgrade.
- A small notices file in the project's own repository, which is how a release note reaches the list.

Nothing about your sessions, prompts, code, repositories or machine is sent anywhere. The coding CLIs you run inside agent-manager are separate programs with their own network behaviour and their own privacy policies; agent-manager starts them and reads their panes, and does not proxy or inspect their traffic.

## What stays on your machine

Session state, group structure, the shared task list, file reservations and captured pane text are stored locally under your own config directory, in a SQLite database and a TOML config file. They are readable and deletable with ordinary file tools, and removing that directory removes everything agent-manager has kept.

## Questions and changes

Questions about this policy belong in [GitHub Discussions](https://github.com/YoanWai/agent-manager/discussions), and anything sensitive belongs in a private security report on the repository. This page is versioned with the rest of the site, so its history is public in the same repository as the code.

---

Source: https://agent-manager.dev/docs/

---
title: "Overview"
description: "What agent-manager is, what a session is, and where everything lives."
canonical: "https://agent-manager.dev/docs/"
last-updated: "2026-10-23"
section: "Documentation"
source: "agent-manager"
---

# Overview

agent-manager is a Go TUI that runs on top of tmux. Every agent you start is a real tmux session, so the manager is a window onto them rather than something they live inside. Quit it and they keep working.

![The session list: twenty-odd agent sessions grouped under backend, its api subgroup, frontend and infra, each with a colour-coded live status, a real Claude Code transcript in the preview on the right, and machine gauges at the bottom left.](/assets/screenshot-sessions.png)

_Twenty-three sessions, four projects, one nested subgroup. The preview is a real Claude Code pane._

## The first run

There is no config to write and no daemon to start. Install it, then:

```bash
agent-manager
```

Everything you can change lives in Settings (`s`): the keys, the editor, the theme and the rest. Claude Code, OpenCode, Codex, Grok Build, Gemini CLI, Antigravity CLI, Pi, Command Code, Hermes Agent, Muse Code, Oh My Pi and Cursor CLI are built in, rules included, so there is nothing to describe before the first spawn.

| Key | What it does |
| --- | --- |
| n | New session: name, tool, the model, effort and profile the tool offers, directory, optional saved prompts and a starting prompt, and a group. |
| g | New group. |
| space | Prompt the selected agent, or spawn one in the selected group. |
| C | Add an SSH connection, so another machine's sessions join the list. |
| ? | Every key, without leaving the app. |

The mouse works on every screen. A click focuses a session, a double click folds a group, the wheel moves the cursor, a row drags by its `⠿` handle, `[…]` opens a row's actions, and the divider between the list and the preview drags. The full-screen layout selects on a click and focuses on a double click.

## Where the sessions live

Agent sessions run on a private tmux server named `agentmgr`, in the `am_*` namespace, 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:

```bash
tmux -L agentmgr ls
tmux -L agentmgr attach -t am_<id>
```

> **Inside a session** `ctrl+q` detaches back to the manager when the terminal leaves it available; `ctrl+\` is an alternate. `ctrl+c` reaches the agent while the session is focused, and the manager stays up. `ctrl+r` opens review and `F3` opens the session's directory in your editor. These bindings are scoped to managed sessions, so they pass straight through everywhere else.

## Where to go next

| Page | What it covers |
| --- | --- |
| [Install](/docs/install/) | Homebrew, Scoop, the install script, the AUR, mise, Go, or a binary. |
| [Status](/docs/status/) | Why a row is orange, and where that verdict comes from. |
| [Quick prompt](/docs/prompt/) | Answering a blocked agent without attaching to it. |
| [Review mode](/docs/review/) | Whole-file diffs whose line comments go back to the agent. |
| [Sessions](/docs/sessions/) | Worktrees, shells, kill and revive, other machines, full screen. |
| [Keys](/docs/keys/) | The same keys the `?` overlay shows. |
| [MCP](/docs/mcp/) | What an agent can do, and spawning from a script. |
| [Configuration](/docs/config/) | Settings, the key tables, the twelve built-in CLIs, saved prompts, and the themes. |
| [Community](https://discord.gg/ecS88An7pF) | Questions and setup help in #help and shared setups in #workflows, on the Agent Manager Community Discord. Bugs and features go in [GitHub issues](https://github.com/YoanWai/agent-manager/issues/new/choose). |

---

Source: https://agent-manager.dev/docs/install/

---
title: "Installation"
description: "Homebrew, Scoop, the install scripts, the AUR, mise, go install, or a prebuilt binary. macOS and Linux, amd64 and arm64. Windows natively on psmux, and inside WSL2."
canonical: "https://agent-manager.dev/docs/install/"
last-updated: "2026-10-23"
section: "Documentation"
source: "agent-manager"
---

# Installation

macOS and Linux, amd64 and arm64. You need **tmux 3.1+**, since the whole thing is built on it. Windows runs natively on **psmux 3.4+** with PowerShell 7.3 or newer as the pane shell, and it still runs inside WSL2.

## Dependencies

| Tool | What it powers | Comes with |
| --- | --- | --- |
| tmux 3.1+ | Every agent session on macOS, Linux and WSL2. | Homebrew, AUR, install script. |
| psmux 3.4+ | Every agent session on native Windows. | Scoop, the Windows install script, or `winget install --id marlocarlo.psmux -e`. |
| git | Diff review and worktree sessions. | Homebrew, AUR, install script. |
| wl-clipboard or xclip | Pasting images into a prompt on Linux. | The install script on a Linux desktop, or your package manager. |
| notify-send | Desktop notifications on Linux. | The install script on a Linux desktop (`libnotify-bin` with apt, `libnotify-tools` with zypper, `libnotify` with the others), or your package manager. |

The mise, `go install`, and prebuilt-binary routes install the binary alone, so install tmux and git with your package manager. When the manager starts without tmux, or the diff view cannot find git, it names the install command it detected.

The agent CLIs are yours to install, and the manager helps. A spawn on a CLI that is not on `PATH` opens a dialog with that CLI's installer: `i` runs it in a shell tab and `c` copies it. For the CLIs that install through npm, it installs Node LTS first when there is no usable npm, and installs under your home directory when the global npm prefix is not writable.

## Homebrew

macOS and Linux. Installs tmux with it if you do not have it, and Homebrew runs on git.

```bash
brew install agent-manager
```

## Homebrew cask

macOS and Linux. Installs the released binary rather than building it, and pulls in tmux. The formula above is in homebrew-core and builds from source; this cask is the prebuilt route, and `agent-manager update` knows which one it is running under.

```bash
brew install --cask yoanwai/tap/agent-manager
```

## Install script

macOS and Linux. Download the release's `install.sh`, read it, then run it. The file is attached to the release and listed in `checksums.txt`. `sh install.sh` runs only after that download succeeds.

```bash
curl -fsSL -o install.sh https://github.com/YoanWai/agent-manager/releases/latest/download/install.sh && sh install.sh
```

`gh attestation verify install.sh --repo YoanWai/agent-manager` checks that file's build provenance when `gh` is logged in. The same command checks `install.ps1` or a release archive.

The script downloads the release for your platform, verifies it against the published checksums, and installs it to `~/.local/bin`. Then it checks for tmux 3.1+ and git. On macOS it offers Homebrew, and installs Homebrew first when that is missing. On Linux it offers apt, dnf, pacman, zypper, apk or Homebrew, and on a desktop it adds the clipboard and notification packages. Under WSL it skips those, since paste and toasts go through Windows. It prints the command when you decline, and a run with no terminal skips the prompt and still installs the binary. `AGENT_MANAGER_VERIFY_ATTESTATION=1` refuses the install unless `gh attestation verify` accepts the downloaded archive. `y`, `yes` and `true` do the same, in any case. With the variable unset, the script installs without calling `gh`. `gh` uses one exit status for a rejected attestation, a missing login, and a network error, so a set variable treats any of those as a failed install.

The short form runs the copy on `main` without saving a file to read.

```bash
curl -fsSL https://raw.githubusercontent.com/YoanWai/agent-manager/main/install.sh | sh
```

| Variable | Effect |
| --- | --- |
| AGENT_MANAGER_INSTALL_DIR | Install somewhere other than `~/.local/bin`. |
| AGENT_MANAGER_VERSION | Pin a version instead of taking the latest. |
| AGENT_MANAGER_INSTALL_DEPS | `1` answers the dependency prompt up front, `0` skips it. |
| AGENT_MANAGER_VERIFY_ATTESTATION | `1`, `y`, `yes` or `true` checks the downloaded archive with `gh attestation verify`. |

## Arch Linux

[agent-manager-bin](https://aur.archlinux.org/packages/agent-manager-bin) installs the released binary and pulls in tmux and git.

```bash
yay -S agent-manager-bin
```

## mise

Reads the GitHub release directly, so it needs no registry entry. Install tmux and git with your package manager.

```bash
mise use -g ubi:YoanWai/agent-manager
```

## Go

Needs Go 1.27.3+, tmux 3.1+ (psmux 3.4+ on Windows), and git. Lands in `$(go env GOPATH)/bin`.

```bash
go install github.com/YoanWai/agent-manager@latest
```

## Prebuilt binary

macOS, Linux and Windows, amd64 and arm64, straight from [Releases](https://github.com/YoanWai/agent-manager/releases). On Windows, install psmux and git with your package manager. Each release publishes `checksums.txt` for the archives, `install.sh` and `install.ps1`. `gh attestation verify <file> --repo YoanWai/agent-manager` checks a downloaded file's build provenance when `gh` is logged in.

## Scoop

Windows. Installs the version published in the [yoanwai bucket](https://github.com/YoanWai/scoop-bucket), and Scoop installs psmux with it. The bucket is updated on its own, so it can lag the latest release. The Windows install script installs the current release. Install git and PowerShell 7 with `scoop install git pwsh` when they are missing.

`PowerShell`

```
scoop bucket add yoanwai https://github.com/YoanWai/scoop-bucket
scoop install yoanwai/agent-manager
```

## Windows

On native Windows the multiplexer is [psmux](https://github.com/psmux/psmux) 3.4.0 or newer and the pane shell is PowerShell. Download `install.ps1` from the release, read it, then run it. The file is attached to the release and listed in `checksums.txt`. Windows PowerShell 5.1 has no `&&`, so the guard is `if ($?)`.

`PowerShell`

```
irm https://github.com/YoanWai/agent-manager/releases/latest/download/install.ps1 -OutFile install.ps1
if ($?) {
  Get-Content .\install.ps1
  Unblock-File .\install.ps1
  powershell -NoProfile -ExecutionPolicy Bypass -File .\install.ps1
}
```

`gh attestation verify .\install.ps1 --repo YoanWai/agent-manager` checks that file's build provenance when `gh` is logged in. On a machine whose execution policy is Restricted, `-ExecutionPolicy Bypass` applies to that process only, after you have read the file.

The script installs `agent-manager.exe` to `%LOCALAPPDATA%\Programs\agent-manager`, or to `$env:AGENT_MANAGER_INSTALL_DIR` when that is set, and adds that directory to your user Path. It offers to install whatever is missing, and a dependency that fails does not fail the binary install. `AGENT_MANAGER_INSTALL_DEPS=1` answers yes, `0` skips the prompt, and a run with no terminal skips it too. `AGENT_MANAGER_VERIFY_ATTESTATION` behaves as it does on the shell script.

The short form runs the copy on `main` without saving a file to read.

`PowerShell`

```
irm https://raw.githubusercontent.com/YoanWai/agent-manager/main/install.ps1 | iex
```

| Tool | Install |
| --- | --- |
| psmux 3.4.0+ | `winget install --id marlocarlo.psmux -e`, or `cargo install psmux`. The manager refuses an older build. |
| git | `winget install --id Git.Git -e` |
| PowerShell 7.3+ (`pwsh`) | `winget install --id Microsoft.PowerShell -e`. Windows PowerShell 5.1 and PowerShell before 7.3 are refused. They split a prompt that contains `"` into several arguments. |

Each session runs on its own psmux server, and previews poll the pane. Command Code shares its name with `cmd.exe`, so the tool pickers leave it out and a session asking for it is refused. A CLI installed only as a `.cmd` or `.bat` file is refused for the same quote-splitting reason. An npm install is fine, because npm adds a `.ps1` beside each `.cmd`. A CLI the setup dialog installs with npm lands in `%APPDATA%\npm`. When that directory was not on Path before the manager started, start the manager again before spawning that CLI.

[WSL2](https://learn.microsoft.com/windows/wsl/install) still works. In a WSL shell, use the install script, Homebrew, or the Linux binary, on tmux. The agent CLIs belong in the distro. A spawn that finds only the Windows copy of a CLI stops and names the command that installs it inside WSL.

## Run it

No config to write first and no daemon to start.

```bash
agent-manager
```

A start with no arguments, when standard input or standard output is not a terminal, prints usage on standard output and exits 0. Standard error reads `agent-manager: no terminal attached; run it in a terminal to open the manager`.

| Key | What it does |
| --- | --- |
| space | Prompt the selected session without attaching, or spawn an agent on a group row. |
| ctrl+r | Review that session's changes. `c` comments a line, `C` sends every comment back. |
| v | Relaunch a dead session on its original conversation. |
| enter | Focus the session in place, so your keys reach the agent and the list stays. `A` attaches it full screen. |
| ? | The keys overlay, any time. |

## Updating

The manager checks GitHub Releases every ten minutes and shows a `↑ vX.Y.Z available` badge in the header when a newer version is out. With the header hidden, the same badge sits on the top edge of the frame. Press `u` on the update message, or `enter` on the version row in Settings, and what happens next follows the install. A Homebrew, mise or AUR install hands the terminal to that package manager's own upgrade command, so its progress and any password prompt behave as they would in a shell. An install-script, `go install` or manual download is updated in place, by downloading the release and swapping the binary. Either way the manager restarts into the new build with every session still running.

Scoop refuses to update an app while its binary is running. The manager shows the command and leaves the binary in place. Close agent-manager, run `scoop update agent-manager`, then start it again. A global install uses `scoop update agent-manager --global` in an elevated PowerShell. Agent sessions keep running while the manager is closed.

> **Two cases update by hand** A pacman-owned install needs an AUR helper, `yay` or `paru`, on `PATH`. Without one the manager runs nothing and prints the command to use instead: `yay -S agent-manager-bin`. A Nix install lives in the read-only store, so the manager points you at Nix, such as `nix profile upgrade agent-manager` or your flake or NixOS config.

From a shell, `agent-manager update` does the same thing without opening the manager. It hands a Homebrew, mise or AUR install to that package manager, and it swaps the binary for an install-script, `go install` or manual download. A Scoop install prints the command and leaves the binary in place. Add `--json` to get the result as a machine-readable record. The per-installer commands still work directly.

```bash
agent-manager update                                # any install
brew upgrade agent-manager                          # Homebrew formula
brew upgrade --cask yoanwai/tap/agent-manager       # Homebrew cask
scoop update agent-manager                          # Scoop, after closing agent-manager
curl -fsSL -o install.sh https://github.com/YoanWai/agent-manager/releases/latest/download/install.sh && sh install.sh  # install script
mise upgrade --bump ubi:YoanWai/agent-manager       # mise
go install github.com/YoanWai/agent-manager@latest  # Go
```

---

Source: https://agent-manager.dev/docs/status/

---
title: "Status"
description: "Where each session's colour comes from: Claude Code hook events, per-tool pane rules, and how a turn is judged finished or waiting."
canonical: "https://agent-manager.dev/docs/status/"
last-updated: "2026-10-25"
section: "Documentation"
source: "agent-manager"
---

# Status

Every session's tmux pane is polled every two seconds, and each poll resolves to one state, which the row carries as a colored mark.

| Mark | Compatible | State | 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 `.`. |
| ✕ | x | errored | The tool reported an error. |
| ○ | . | idle | Nothing running. |
| ✕ | x | dead | The tmux session is gone. The row stays. |
| ◌ | ~ | starting | The pane is still launching. |

The first column is the default, Advanced. Settings → `symbols` switches to Compatible, plain ASCII marks for a font that cannot draw the others. There a starting row animates an ASCII spinner, a group folds with `>` and `v`, and `Y` marks a worktree branch or a fork.

## Pane rules

Detection matches per-tool regex rules against the visible pane, reads the newest turn to tell `finished` from `waiting`, and treats output that changes between polls as `working`. A turn that ends without any summary line still resolves: a `working` pane that goes quiet counts as `finished`, or `waiting` when it ended on a question.

Work that outlives the turn which started it and reports back to the agent (background agents, dynamic workflows, and an MCP call Claude Code moved to the background) is matched by `busy_line`, so a turn-end summary keeps reading as `working` while that work runs. A turn that leaves a background shell or monitor running ends like any other, and when that shell wakes the agent, the turn that follows reads `working` until it ends. A usage or rate-limit banner (`limit_line`) is `errored`. Polling continues while you are inside a session, so the statuses stay live. A dead row in the list or an open review leaves the poll on that two second interval. Before v0.43.2 each kept the poller spinning, because work the poll queued every pass asked for another poll as it finished.

## Claude Code hooks

For Claude Code the status is first-hand rather than inferred. Sessions launch with a generated `--settings` file whose [hooks](https://docs.anthropic.com/en/docs/claude-code/hooks) write the lifecycle state to a per-session status file, and the poller reads that first. The Claude Code profile sets `status_source` to `claude-hooks` for this.

A `StopFailure` of `rate_limit` writes `errored`. Pane rules still refine it, because hooks cannot see a plain-text question, an Esc interrupt or an error line, so a matching pane verdict upgrades the hook status. They take over entirely when the hook file is missing or stale.

## Reading the list

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 under it needs you. Press `w` and the list keeps only the waiting, finished and errored rows. Press it again for the whole tree. 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 messages from another agent waiting to be typed in wears `✉` (`@` in Compatible) and the count next to its name, and one that asked to be archived or killed once its turn ends wears `↓` or `■` (`A` or `K`) until the manager does it, or until `c` cancels it.

> **Killed and archived sessions** A session with no window left shows the snapshot taken while it still had one, so the row still says what the agent was doing when it stopped.

## Notifications

When a session's status flips to `waiting` or `errored`, the manager fires one notification, once per transition. The title names the session, the directory it runs in, the branch when the manager made a worktree for it, and the tool. A worktree names the repository it was cut from. The title reads `deploy · api · am/fix-login · codex`. The banner quotes the line the session row shows. A waiting one carries the question the agent is asking, as in `◆ Waiting: Do you want to create probe.txt?`. A finished one carries the prompt the row shows, as in `● Finished: write three sentences about rivers`. The quote is one line, cut at 120 characters. With nothing on the row to quote, a waiting banner stays `◆ Waiting for your input` and a finished banner stays `● Finished`. An errored banner stays `✕ Errored`. Settings has a `notifications` row that silences them (on by default) and a `notify on finish` row that adds `finished` transitions (off by default).

On macOS the desktop path gives waiting, finished and errored distinct sounds. Linux sends matching hints through `notify-send`. Inside Ghostty or cmux the state travels as an OSC 777 escape to the drawing terminal, which also reaches you when the manager runs on a remote host over SSH. Under WSL the banner is a native Windows toast posted through PowerShell. Other terminals use the OS desktop path, and a headless box without a desktop falls back to the terminal bell.

A shell command can run on every notification as well. Set it with `agent-manager settings set notify_command '<command>'` and clear it with an empty value. It has no Settings row. The manager runs it after the banner, through `sh -c` on macOS and Linux and through PowerShell on Windows, and the same two Settings rows gate it. The event arrives in the environment as `AM_NOTIFY_KIND`, `AM_SESSION_ID`, `AM_SESSION_NAME`, `AM_TOOL`, `AM_CWD`, `AM_BRANCH`, `AM_TITLE` and `AM_BODY`. On Windows those read as `$env:AM_BODY` and the rest. A command still running after 15 seconds is stopped. A failure is written to `notify-command.log` in the config directory, and the manager carries on. The first time a command is set, and whenever it changes, a `New notify command` banner shows the command before it runs.

---

Source: https://agent-manager.dev/docs/prompt/

---
title: "Quick prompt"
description: "space opens quick prompt mode: send into a session's pane, spawn into a group on the model you pick, paste an image, retarget mid-sentence."
canonical: "https://agent-manager.dev/docs/prompt/"
last-updated: "2026-10-25"
section: "Documentation"
source: "agent-manager"
---

# Quick prompt

`space` opens quick prompt mode. It takes over the footer, with the target and the prompt on a band across the whole width and the mode's keys on the row under it. The target follows the cursor while the bar is open, and `↑↓` still navigate from a one-row prompt.

![Animated demo: five prompts send five fresh agents to work from quick prompt mode without the cursor leaving the tree, the first on Opus at high effort picked with ctrl+l and ctrl+x, each row named by the prompt it was given, then a session's diff opens in review.](/assets/demo.gif)

_Fig. 2space · quick prompt_

## On a session row

An agent asks which database to use. Press `space` on its row, type the answer, and press `enter`. The text lands in its pane as a user message, and you stay on the list. The bar clears and stays open for the next answer. Settings can make it close instead.

## On a group row

`enter` spawns a new agent in that group with the prompt embedded, using the group's default path. The spawn tool starts on the tool of the last session you created in this run, or the Settings default before that. `tab` steps it forward and `shift+tab` steps it back one. The target row shows on its right what the new agent launches with: the tool, its model, effort and profile, and the worktree. A click on one does what its key does.

`ctrl+t` toggles whether the new agent gets its own git worktree (see [worktree sessions](/docs/sessions/#worktrees)). The target row reads `⎇ on` or `⎇ off`, or `⎇ no repo` when the target directory cannot hold one. Answering an existing session ignores the toggle. The agent starts on the prompt immediately.

## Several agents from one group

Put the cursor on the group and press `space`. Press `ctrl+t` so the row reads `⎇ on`. Type the first task and press `enter`. The bar clears and stays open. Type the next task and press `enter`. Each enter starts another agent, on its own worktree. `tab` changes the CLI for the next one, and `ctrl+l` picks its model.

You can also tell an agent that is already running "spawn an agent for this". It uses the same spawn. How far agents go on their own is the [coordination](/docs/mcp/#coordination) setting.

When the group's path is a folder of repos and not a repo itself, the row reads `⎇ no repo`. Those agents share that folder, and [review](/docs/review/#which-repo) still finds the repos under it.

## Model, effort and profile

`ctrl+l` swaps the bar for a sheet listing the models the tool reports. 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. Each CLI keeps your last picks, here and in the `n` form alike, and the next spawn on that CLI starts there. On a group row the footer names each of the three keys when the tool offers it:

`footer`

```
↵ send  ↑↓ target or caret  tab tool  shift+tab previous tool  ctrl+l model  ctrl+x effort  ctrl+y profile  ctrl+t worktree  esc close
```

The lists come from each CLI at runtime, so a new model shows up the day the CLI ships it. [Sessions](/docs/sessions/#model) has what each CLI reports.

## Saved prompts

Instructions you give every agent live as saved prompts, kept in Settings (see [Configuration](/docs/config/#saved-prompts)). In the bar, `ctrl+o` opens the list and drops the pick in as a chip that becomes its text when you send. On a session that text goes to the agent, and on a group it starts the new one. In the `n` form, `←→` on the saved prompts row picks one, and `ctrl+o` or `enter` opens the list. Its text goes in front of the task. In a focused session, `F4` pastes one into the agent's prompt without sending. `F4` moves with the other session keys.

```bash
agent-manager prompts add reviewer "Review the parser changes"
agent-manager spawn --saved reviewer --prompt "Start with the tests"
agent-manager send <session-id> --saved reviewer
```

`create_session` and `send_session` take the same name in `saved`. A name that does not exist is refused.

## Pasting text

Text goes into a pane through tmux's `paste-buffer` with `-r`, so every line break reaches the pane as a linefeed rather than a carriage return. That covers a paste into a focused session, a saved prompt dropped in with `F4`, and the text of a quick prompt or a send. An agent that reads a carriage return as Enter no longer submits part of a multi-line paste into a working session and queues the rest. This is tmux's own paste handling, not a per-CLI rule.

## Pasting an image

`ctrl+v` pastes an image from the system clipboard as an `[Image #1]` chip at the caret. The image is written 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 pastes as text.

> **Housekeeping** 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.

## Keys

| Key | Action |
| --- | --- |
| enter | Send into the pane, or spawn in the group. |
| ↑↓ | Retarget while the bar stays open, so three answers are three sentences. In a taller prompt they move the caret between rows. |
| tab | Step the tool a spawn will use forward (`alt+m` also works). |
| shift+tab | Step the tool a spawn will use back one. |
| ctrl+l | Pick the spawn's model from the ones the tool lists. |
| ctrl+x | Step the spawn's reasoning effort through the model's levels. |
| ctrl+y | Step the spawn's profile, for a tool that has them. |
| ctrl+t | Toggle worktree spawn for a new agent on a group row (`alt+w` also works). |
| ctrl+o | Pick a saved prompt, dropped in as a chip that becomes its text on send. |
| ctrl+v | Paste a clipboard image as a chip. |
| backspace | Next to a chip, removes the whole chip. `delete` removes the chip after the caret. |
| ←→ | Step over a chip as one token. |
| esc | Close the bar. |

A `/slash` command reaches the agent as a slash command; the self-naming directive follows as its own message rather than mangling it. The new-session form's optional prompt field launches an agent the same way, and it takes `ctrl+v` and its chips too, since a first task is often the screenshot that explains it. How the prompt reaches the agent depends on the CLI. Most take it as a launch argument, and a CLI that accepts none has it typed into its composer as soon as that composer is ready.

---

Source: https://agent-manager.dev/docs/review/

---
title: "Review mode"
description: "ctrl+r opens a full-screen whole-file diff whose line comments go back to the agent as one review prompt."
canonical: "https://agent-manager.dev/docs/review/"
last-updated: "2026-10-09"
section: "Documentation"
source: "agent-manager"
---

# Review mode

`ctrl+r` on a session hands the whole screen to its repo, with a keymap of its own: changed files and +/- counts on the left, the entire file on the right, syntax highlighted with changed lines tinted, so every edit reads in full context. It refreshes as the agent keeps editing, and `esc` returns you to the list where you left it.

![Animated demo of the review screen: a changed Go file scrolled side by side, the layout switched to unified, the next file opened, then a line comment sent back to the agent, which starts working on it.](/assets/demo-diff.gif)

_Fig. 3ctrl+r · review mode_

## Comments go back to the agent

`c` writes a note on any line and `d` drops one. Comments stay on the review screen until you send them. `C` flattens every one into a single prompt, asks you to confirm, and delivers it into the agent's pane, so the agent starts addressing your notes while you watch the diff update.

Comments and reviewed-file marks stay after you quit. `C` sends the current drafts as one numbered round, and each point keeps its id. An agent marks one handled with `review_comment`, or with `agent-manager review-comment` from a shell. On a sent comment, `d` marks it handled or open. A comment whose line is gone is marked outdated.

## Keys

| Key | Action |
| --- | --- |
| ↑↓ / j k | Scroll the file. `ctrl+d` and `ctrl+u` move half a page, `g` and `G` jump to the ends. |
| J / K | Next, previous file. `tab` and `shift+tab` do the same. |
| n / N | Jump between changes. |
| u | Toggle unified and side by side, holding the cursor on the same source line. |
| s | Cycle the scope: uncommitted, versus target, last commit, staged. |
| r | Pick the repo when the session's directory holds several. Type to filter. |
| b | Pick the branch from the repo's worktrees. |
| B | Pick the target the "versus target" scope compares against. |
| space | Mark a file reviewed. |
| f | Show code files only, hiding images, compiled assets and lock files; press again to show them. |
| c / d | Write or drop a draft. On a sent comment, mark it handled or open. |
| C | Send the current drafts as one numbered round. Enter or `y` confirms. |
| o / F3 | Open the current file in your editor. |
| 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.

![The review screen, side by side, with changed lines tinted inside the full file.](/assets/screenshot-review.png)

_Fig. 4side by side_

## Which repo it opens

A session often starts in a parent folder that is not a git repo. Take `~/company` with `api/` and `web/` inside it. Press `ctrl+r`. Review looks through the folders under it, three levels down, skips dependency and build directories, and opens the repo with uncommitted changes. When every repo is clean, it opens the one with the newest commit. Press `r` and type to pick a different one.

The list footer reminds you to tell the agent "set review mode". The agent calls the [`review`](/docs/mcp/#tools) tool, or runs the subcommand from a shell inside its session, and the next review opens there:

```bash
agent-manager review-repo /path/to/repo
agent-manager review-base main
```

The path is checked to be inside a git repo and resolved to the repo root, so a declaration is a fact rather than a guess. Review then resolves its target in a fixed order:

- a repo you picked by hand with `r` or `b`, for as long as the manager runs,
- then the agent's declared repo,
- then the ranking: dirty working trees first, then the most recent commit.

> **Worktrees** Linked worktrees are discovered wherever they live on disk, so one declaration names both the repo and the branch under review. A stored base ref that stops resolving surfaces as an error in review, and `B` opens a picker to set or clear it.

---

Source: https://agent-manager.dev/docs/sessions/

---
title: "Sessions and groups"
description: "Every agent is a real tmux session: its model, effort and profile, killing, reviving on the same conversation, the group tree, archiving, and self-naming."
canonical: "https://agent-manager.dev/docs/sessions/"
last-updated: "2026-10-23"
section: "Documentation"
source: "agent-manager"
---

# Sessions and groups

Every agent is a real tmux session with its own directory, command and environment. Quit the manager and they keep working; close the laptop lid and they are still there.

## 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. Quick prompt mode 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, asked 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, whatever you set up in that CLI. Each CLI remembers the model, effort and profile you last picked for it, and the next session on that CLI starts there. The model list filters as you type, your last picks come first, and only a listed model can be picked. The effort row offers the levels the chosen model takes. The choice is saved on the session, so restart (`R`), revive (`v`) and fork (`f`) all run on it.

| 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 | · |
| Oh My Pi | `omp --mode rpc` | yes | per model | · |
| Muse Code | `muse serve` | yes | per model | · |
| OpenCode | `opencode models` | 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 | · |
| Cursor CLI | ACP over `cursor-agent acp` | yes | carried in the model id | · |

A row the CLI cannot fill reads "not supported". OpenCode and Gemini CLI take no effort when they launch. Cursor CLI carries effort inside the model id, so the form offers no separate effort control. 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, and an older Pi opens the update dialog instead. When a CLI fails to answer for any other reason, 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. Answers are kept for six hours under `catalogs` in the config directory, and an upgraded CLI is asked again at once.

## Own git worktree per session

Parallel agents on the same repo can each get a private checkout. The `n` form has a `worktree` field between directory and prompt, and quick prompt mode's `ctrl+t` does the same when you spawn into a group. Both start on the tool and the worktree choice you picked last time, for as long as the manager runs, and Settings sets what they start from before that.

The worktree lives at `<repo>-worktrees/<name>` next to the repo, on a new branch `am/<name>`. It starts from the branch the repo's work merges into: the default branch of an `upstream` remote when the repo has one, then origin's default branch, then a local `main` or `master`, and finally `HEAD`. A group can name the starting point itself with the `base` row of the `g` form or its edit card, and a group left on `auto` takes its parent's. While the form or quick prompt mode is set to spawn a worktree, the manager fetches that one branch from its remote, at most once a minute for the same directory, and the `fetch on spawn` row in Settings turns that off. A worktree that fails to create blocks the spawn with an error. A path that is not a git repo cannot hold one, so the field reads unavailable and the session launches there as a plain session.

A branch that starts from `upstream` keeps tracking that remote, so `git pull` brings in the parent's changes and `git push` sends the branch to `origin`, your fork. A `remote.pushDefault` you already set is what `git push` uses.

Renaming a session renames its branch to `am/<new name>` and keeps the worktree directory at the path it was made at, so the running agent's working path stays valid. Deleting a session removes a clean worktree and its branch. Clean means no uncommitted changes and no commits that exist only in that worktree. A branch already in the base counts as merged, including after a squash, on git 2.38 or later. Commits that are already pushed leave the worktree free to go, and the local branch stays so those commits remain reachable. A dirty worktree stays on disk and the status line shows its path. Clean follows `git status`, so a gitignored file such as a `.env` is removed with the directory. Killing, archiving and reviving never touch the worktree.

## Terminal tabs

`T` opens a shell tab: a session like any other, 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. A nested shell is named after the session it hangs under, `terminal-review-done` rather than a generated id. 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 or onto a group.

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. Enter the session to type there.

## Killing without losing the row

`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.

A group of agents is done for the night. `x` on the group frees the RAM and leaves the rows. `v` on the group in the morning brings those same conversations back.

## Reviving the same conversation

`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`. The row follows a conversation the agent switches to with `/clear`, `/new`, `/resume` or `/fork`, so `v` reopens that one rather than the conversation the session started on. Only the agent the launch started moves the row. A CLI it runs as a tool, or one typed into the pane by hand, leaves the row where it is. Cursor CLI stays on the chat captured at launch. The commands are:

`resume_by_id_command`

```
claude --resume {id}
codex resume {id}
opencode --session {id}
grok --resume {id}
gemini --resume {id}
pi --session {id}
cmd --session {id}
hermes --cli --resume {id}
muse resume {id}
agy --conversation {id}
omp --resume {id}
cursor-agent --resume {id}
```

The id arrives one of two ways:

| Field | How the id is obtained |
| --- | --- |
| session_id_flag | The tool launches under an id the manager mints. |
| session_store | The tool mints its own, and a capturer reads it back (`codex`, `opencode`, `gemini`, `hermes`, `command-code`, `muse`, `antigravity`, `omp`, `cursor`). |

> **Without an id** Revive falls back to `revive_command` (`claude --continue`), which resumes the 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 everything dead under it, and `V` revives everything dead in view. Each asks to confirm first when that is more than one session. Both revive what they can, carry on past a failure, and name the first one.

## Forking a conversation

The session under the cursor tried one approach. `f` names a second session and continues from the same history, in the same group and directory, so the other approach runs beside it. It runs the tool's `fork_command`, which references the source through `{id}` or `{session_file}`, so a fork needs both that command and a captured conversation id. A session without one, or a shell, says so instead of forking. Claude Code, OpenCode, Codex, Grok Build, Gemini CLI, Pi, Command Code and Muse Code ship fork commands. Hermes Agent, Antigravity CLI, Oh My Pi and Cursor CLI have no fork. Muse forks only from inside the running source, so `f` types `/fork` into that session and opens the fork in a new pane. The source has to be at rest with nothing typed at its prompt.

## Restarting on an empty context

`R` keeps the row and drops the context: same name, group, tool and directory, launched on a conversation the agent has never seen. It is what a session that has piled up context you are done with wants, where reviving would spend the budget re-reading history. It asks to confirm first and works on a live session too, ending the running agent before the fresh one starts. The old conversation is retired rather than resumed, left on disk while the row stops pointing at it, and the row only changes hands once the new agent is up, so a launch that cannot start leaves the session on the conversation it had.

## Full screen

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 machine meters condense to one line above the key legend, and `→` on a row focuses that session across the whole body. While a message is unread, the View footer paints `M unread` as a filled chip, and a click on it opens them. The same keys return the layout to split, and the choice persists like the split ratio.

![Animated demo: Settings swaps the sessions layout to full screen, the list takes the whole terminal with the computer stats on its foot line, and a session is focused full screen then left.](/assets/demo-fullscreen.gif)

_Fig. 1sessions layout · full screen_

The **list density** row picks the rows' rhythm in either layout. Compact keeps a session to one line with the agent's last message riding beside its name. Comfortable unfolds each session to three lines: the name, your last message behind a `❯`, and the agent's last message under it.

![Animated demo: the same full-screen list at the comfortable density, every session on three lines with its task and the agent's last message under the name.](/assets/demo-fullscreen-comfortable.gif)

_Fig. 2full screen · comfortable density_

## Sessions on another machine

`C` adds an SSH connection: a name, a destination (`user@host` or an alias from `~/.ssh/config`), and optionally where agent-manager lives there. The other machine runs Linux or macOS with agent-manager on its login shell's `PATH`, and this machine's `ssh` needs a key it can use without a prompt. The manager never asks for a password and never touches your SSH config. Connect once by hand first (`ssh user@host`), so the host key is already known. A host it has never seen shows as `unreachable`.

The connection sits in the list after your own groups, in its own colour with an `ssh` label, and the other machine's groups, agents and terminals nest under it. It folds like a group. Each poll refreshes it in the background. A host that fails keeps its last rows with a note saying why (`unreachable`, `agent-manager missing`, `agent-manager too old`, `agent-manager newer` or `error`) and is tried again after a pause that grows to half a minute. The first refresh starts a headless manager there, `agent-manager serve --background`, so messages to its agents get delivered.

| Key | On a connection's rows |
| --- | --- |
| enter / A | Attach over SSH in this terminal. Detaching returns to the manager. A remote pane is never embedded. |
| space | Type into a remote agent's pane as you, or start a new agent there on the CLI, model, effort and profile the bar shows. |
| n | Start an agent there. The directory and worktree default are the other machine's unless you set them. |
| T / g | Open a terminal there, or create a group there. |
| x / v / a / u | Kill, revive, archive and restore. |
| r / d | On the connection row: edit it, or remove it. Removing asks first, and its sessions keep running on that host. |

Rename, move, delete, restart, fork, review, the editor, copying a reply, reordering, marking idle, cancelling an after-turn end, and kill all or revive all are refused on a remote row with a message naming the gap. Nothing from a connection is written to this machine's store. Agents reach the connection too, through [MCP](/docs/mcp/#ssh), so add one only for a host you would let all of them use.

## Groups are a tree

Groups are paths like `backend/api/auth`, forming a tree of unlimited depth, and sessions live at any node including the root.

| Key | Action |
| --- | --- |
| g | Create a subgroup inline. |
| enter | Fold a subtree. `F` folds or unfolds everything. |
| e | Hide or restore empty groups. |
| r | Edit a group's name, default path and worktree base. |
| K / J | Reorder sessions among their siblings, or whole groups among theirs. The order persists. |
| m | Move a session to a group, a terminal into a session, or a group under another group. |
| a / u | Archive / restore a session or group. Archive kills the process and keeps the last preview; restore resumes it. Restoring un-archives whichever ancestors it needs. |
| t | Toggle the archived view. |

## Sessions name themselves

A session spawned without a name gets a placeholder like `claude-a1b2`, and its first prompt opens by asking the agent to run `agent-manager rename` once, with a short name for the broad feature it is working on rather than a single subtask. The directive also tells the agent not to rename again unless you ask.

When the first prompt cannot carry the directive, because it is a `/slash` command or there is no prompt at all, the manager sends it as its own message once the tool's input box appears. The subcommand drops the name into a per-session file, and the manager picks it up on the next poll.

> **Names you set are kept** A session you named yourself is never renamed by its agent. The first prompt only notes that `agent-manager rename` is available later if you ask for it.

---

Source: https://agent-manager.dev/docs/stats/

---
title: "Stats and gauges"
description: "The fleet summary, per-agent CPU and RAM across the whole process tree, and the machine gauges."
canonical: "https://agent-manager.dev/docs/stats/"
last-updated: "2026-09-29"
section: "Documentation"
source: "agent-manager"
---

# Stats and gauges

The header carries a fleet summary: per-status session counts, plus the CPU and memory every live agent's full process tree is using, shell and agent and children together.

`header`

```
agents total usage: cpu 12% · ram 8% · 6.2 GB
```

CPU is that tree's CPU time over the last poll as a share of total machine capacity, the same 0-100% unit the computer gauge uses. RAM is resident set as a share of installed memory, with the absolute size beside it. The selected session's detail line uses the same scale for that one session.

## The machine gauges

| Gauge | What it measures |
| --- | --- |
| cpu | Whole-machine utilization, 0-100%. |
| mem | Used over 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 over the current swap allocation. On macOS the swap file grows under pressure, so the denominator is the live size from `vm.swapusage` rather than a fixed partition. |
| disk | Fill of the root filesystem, used over used plus 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 on a machine with no battery. |
| net | Up and down rates on real NICs only. Loopback, utun, bridges and similar virtual interfaces are excluded. |
| temp | `cpu`, `gpu` and `soc` in °C, each the hottest sensor in its category, sampled every five seconds. |

> **Apple Silicon** Its dies report as one `soc` figure rather than separate CPU and GPU lines. A reading appears when the machine exposes that sensor.

---

Source: https://agent-manager.dev/docs/mcp/

---
title: "MCP tools"
description: "Spawn, message and wait on other agents, drive terminals, declare the repo under review, and how the server registers itself into every session."
canonical: "https://agent-manager.dev/docs/mcp/"
last-updated: "2026-10-23"
section: "Documentation"
source: "agent-manager"
---

# MCP tools

Every session of an MCP-capable tool carries the agent-manager MCP server on spawn and revive, so the agent sees the whole workspace as native tools: its own session, the other agents running beside it, the groups they are filed under, and the managed terminals. No per-project setup. The server lives in the same binary (`agent-manager mcp`, stdio) and identifies the calling session through its environment.

## The tools

| Tool | What the agent uses it for |
| --- | --- |
| rename | Names its own session once it knows what the work is, replacing the placeholder. |
| review | Declares the repo under review, the base ref and the diff scope, in one call. |
| review_comment | Marks one sent review comment handled, using the stable comment id from the review prompt. |
| list_sessions | Lists every agent session with its id, CLI, group, directory, worktree branch and status. |
| create_session | Starts another agent CLI on a named task, optionally in its own git worktree. |
| read_session | Reads what another agent's screen currently shows. |
| send_session | Queues a message for another agent, delivered once it is at rest. |
| message_status | Checks whether a message is queued, held, delivered, dropped or answered. |
| wait_for_session | Parks until another session stops working, instead of polling it. |
| revive_session | Brings a dead session back, resuming the conversation it held. |
| kill_session | Stops a running agent, keeping its row and last screen. |
| archive_session | Files a finished session out of the active list, or restores it. |
| archive_self | Archives the calling session once its current turn ends, the way `a` does. |
| kill_self | Kills the calling session once its current turn ends, keeping its row for revive, the way `x` does. |
| task | The shared work list in one tool: `action` is `list`, `create`, `claim`, `finish`, `release` or `delete`. |
| reserve_files | Declares the files this session is editing, and sees who else claims them. |
| release_files | Gives those claims back. |
| list_reservations | Sees what every session is editing right now. |
| list_groups | Lists groups with their default directories, worktree defaults and session counts. |
| create_group | Adds a group, nested with a slash path, to file a fleet under. |
| delete_group | Removes a group whose work is done; sessions still filed there move to the root rather than stopping. |
| list_terminals | Lists active managed terminals and their current directories. |
| create_terminal | Opens a terminal under the calling session, or beside it when that session is itself a terminal, unless `nest` is false. |
| send_terminal | Submits a command or sends exact keys to a running terminal. |
| read_terminal | Reads the plain-text content currently visible in a terminal. |
| close_terminal | Closes a finished terminal nested under the caller: kills the pane and deletes the row. |
| report_issue | Files a bug report or a feature request on the public repo for the user. It composes a preview first, posts nothing until the user approves it, and files exactly what they saw. |

## Spawning and steering other agents

You tell a running agent "spawn an agent for this". It calls `create_session`, the same spawn the `n` form gives you: a name, a CLI, a group, a working directory, a first prompt and a worktree choice. The new session is a normal row in the list.

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, and an explicit directory wins over both. A name should describe the work; leaving it empty generates a placeholder and asks the new session to rename itself. Passing `worktree: true` adds a git worktree and branch off the directory's repo. `model`, `effort` and `profile` launch the CLI on values it lists, the way the form's rows do, and a value the CLI does not list is refused with the ones it does. `saved` names a [saved prompt](/docs/prompt/#saved) to put in front of the task, and `send_session` takes it too.

`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. `archive_session` files a finished row away or restores it.

### Ending its own session

An agent ends its own session with `archive_self` or `kill_self` (`agent-manager archive-self` and `kill-self` from a shell), so "archive yourself when it is merged" needs no trip back to the list. The call files the request and returns, the agent finishes its reply, and the manager acts on the first poll that reads the session at rest. Until then the row wears `↓` (archive) or `■` (kill) next to its name. `c`, or Cancel in the row's menu, keeps the session, and the agent can withdraw the request with `cancel`.

## 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, and nobody has a line part way written at its prompt.

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. A receiving agent treats it as an instruction from the same operator and does the ordinary work it asks. Permission prompts and that session's CLI settings stay with the person at the keyboard, and so do commit, push, merge, publish and delete. `message_status` reports whether a message is queued, held, delivered, dropped or answered.

> **Delivery needs the manager running** The 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.

## 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. A timeout returns the session's current state with `reached` false, because a timeout is an answer rather than a failure.

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, 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.

## File reservations

A worktree per session stops two agents overwriting one checkout when the work divides cleanly. `reserve_files` is for the other case: sessions that deliberately share a checkout declare the paths they are about to edit. Overlap with a lease another session holds comes back as conflicts, naming the holder, so the two can settle it through `send_session` before either commits. The lease is advisory: nothing is blocked.

## Terminals

`create_terminal` nests under the calling session unless `nest` is false, and a call from a terminal opens the new shell beside it. It defaults to the calling agent's group and live pane directory. `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. `send_terminal` accepts exactly one of a command, pasted and submitted with Enter, or a sequence of tmux key names such as `C-c`, `Up` and `Enter`.

The server's instructions teach agents when to use these tools. They treat the other sessions the way the [coordination](#coordination) mode says, and open a terminal for human-visible work such as SSH. They list and reuse a running terminal first, send the command and read its screen while the job runs, and call `close_terminal` when that job ends, unless the terminal is being left for the user.

> **These tools act on the user's machine** 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.

## Sessions on other machines

With [SSH connections](/docs/sessions/#ssh) added, `list_sessions`, `list_terminals` and `list_groups` refresh every connection and list its rows after this machine's, each with `host` set to the connection's name and its id written `host::id`. A host that cannot be refreshed never fails the list: its last rows come back with `stale: true`, and `connection_errors` says why.

`read_session`, `send_session`, `kill_session`, `revive_session`, `archive_session`, `read_terminal`, `send_terminal` and `close_terminal` take a `host::id` and run on that host. `create_session`, `create_terminal`, `create_group` and `delete_group` take a `host`. A message sent there cannot be followed with `message_status`, so read the agent's screen instead. Only the session that opened a terminal there can send to it or close it. `wait_for_session`, `task` and `review_comment` refuse a `host::id`, since that state stays on each host.

## 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" keeps 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. A session is briefed when it starts, so the ones already running keep the mode they started with.

## Registration

Registration is per tool and automatic. The built-in tools register on their own:

| Tool | How it is registered |
| --- | --- |
| claude | A generated `--mcp-config` file. |
| codex | `-c mcp_servers…` overrides on the command line. |
| opencode | An `OPENCODE_CONFIG` merge file. |
| grok, gemini, command-code | A one-time `mcp add --scope user` entry on first launch. |
| antigravity | A one-time `agy mcp add` on first launch, which writes `~/.gemini/config/mcp_config.json`. |
| hermes | 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. |
| cursor | The first launch writes the `agent-manager` server into `~/.cursor/mcp.json` and runs `cursor-agent mcp enable agent-manager`. |
| muse | Muse reads MCP servers only from its settings file, so the first launch adds the `agent-manager` server to `~/.config/muse/settings.json` and keeps every other setting and server. |

Pi and Oh My Pi get no MCP registration, and their sessions use the subcommands below. 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.

> **One server, every session** The stdio server serves whichever session called it, identified through its environment, so there is nothing per-session to launch or clean up.

## The same thing from a shell

Pi and Oh My Pi 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. Each one acts as the session or terminal it runs in.

```bash
agent-manager rename "auth token refresh"
agent-manager review-repo /path/to/repo
agent-manager review-base main
agent-manager spawn --name "cover the parser" --prompt "write the tests"
agent-manager archive-self
```

### From a script

A script walks a list of tickets and opens one agent for each. `sessions`, `groups`, `snapshot`, `spawn`, `read`, `wait`, `kill`, `revive`, `archive`, `create-group` and `delete-group` 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. `--model`, `--effort` and `--profile` pick what the CLI launches on.

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

Queued messages, status and after-turn archives and kills move only while a manager runs. `agent-manager serve` runs one with no screen, on a machine where nobody keeps the TUI open, and `serve --background` starts it detached, logging to `serve.log` in the profile directory. One runs per profile. It runs on macOS and Linux.

---

Source: https://agent-manager.dev/docs/config/

---
title: "Configuration"
description: "Settings, the key tables, upgrading from config.toml, how the built-in CLIs are kept current, themes, and where state is kept."
canonical: "https://agent-manager.dev/docs/config/"
last-updated: "2026-10-25"
section: "Documentation"
source: "agent-manager"
---

# Configuration

Everything you can change lives in Settings (`s`): the keys, the editor `o` opens, the theme and the rest of its rows. Changes are saved as you leave Settings and take effect at that moment. Only the rows you changed are written, so a change another manager or the CLI made in the meantime is kept. Every CLI's launch command, resume and fork commands, MCP registration and status rules live in the binary, so an upgrade brings the current version of all of them.

The manager stores each choice in `state.db`, a SQLite file in your OS user config directory:

| Platform | Directory |
| --- | --- |
| Linux | `~/.config/agent-manager/`, or under `$XDG_CONFIG_HOME` when that is set. |
| macOS | `~/Library/Application Support/agent-manager/` |
| Windows | `%AppData%\agent-manager\` |

The `editor` row picks the command `o` opens a directory in. `←→` steps through `auto`, each GUI editor found on your `PATH`, the editors `$VISUAL` and `$EDITOR` name, and `custom`, where `↵` opens a field for a command of your own. `auto` resolves to `$AGENT_MANAGER_EDITOR`, then a GUI editor on `PATH`, then `$VISUAL` / `$EDITOR`. On Linux, a link opens in `$BROWSER` when you set one, colon-separated with `%s` for the URL, falling back to `xdg-open`. macOS hands links to `open`. Panes are polled every two seconds for status, preview and stats.

## Settings from a shell

`agent-manager settings` reads and changes the same settings without opening the manager. `settings list` prints every key with its value and the values it takes. `settings get` prints one, the default when none is stored, and `settings set` writes one. A value outside that key's choices is refused, and the refusal names the choices.

```bash
agent-manager settings set theme "tokyo night"
agent-manager settings set notifications off
agent-manager settings get list_density
```

A running manager picks up `notifications`, `coordination` and `notify_command` at once. The rest show the next time Settings opens, and apply from then on. `notify_command` has no Settings row. [Status](/docs/status/#notifications) covers it.

## Saved prompts

The `saved prompts` row in Settings keeps named instructions you reuse. `n` creates one, `enter` edits it and `d` deletes it. `tab` moves between the name and the text, `ctrl+s` saves and `esc` backs out. Names are unique, up to 60 characters, with no brackets, and the text is up to 64 KiB. One list serves the `n` form, quick prompt mode, a focused session, `spawn --saved`, `send --saved`, and `saved` on `create_session` and `send_session`. [Quick prompt](/docs/prompt/#saved) shows where each one goes.

```bash
agent-manager prompts list
agent-manager prompts add reviewer "Review the parser changes"
agent-manager prompts rm reviewer
```

Changing or deleting one leaves a session that already started alone.

## Upgrading from config.toml

Releases before v0.40.0 read a `config.toml` in that directory. The first start after upgrading carries its `editor` line and its `[keybindings.session]` and `[keybindings.list]` tables into Settings, so every key you moved stays where you put it. That happens once. From then on Settings is the only place the manager reads, and the file is yours to delete. A file the manager cannot read is named in Messages (`M`) with the reason, and the keys start from the defaults.

The poll interval is fixed at two seconds, so a `poll_interval` line in that file has no effect. `[tools.<name>]` blocks have none either, since every CLI's commands and status rules are built into the binary.

## The CLIs

Claude Code, OpenCode, Codex, Grok Build, Gemini CLI, Antigravity CLI, Pi, Command Code, Hermes Agent, Muse Code, Oh My Pi and Cursor CLI are built in, plus the shell `T` opens. Each one's launch command, revive and fork commands, MCP registration, status rules, and the flags a session's model, effort and profile launch with ship in the binary. Hermes, Antigravity, Oh My Pi and Cursor CLI have no fork, and Pi and Oh My Pi get no MCP registration. Settings (`s`) has a `CLIs` row that picks which of them the `n` form and quick prompt mode offer, and its last entry, `request CLI support`, opens an issue prefilled for a CLI outside the list. [Supported agents](/docs/agents/) lists what each one gets.

Agent Manager supports the current release of each CLI. A CLI can also carry the oldest release it still works with. When yours is older, a spawn, a revive or its model list opens a dialog that names your version, the one it needs, and the vendor installer that updates it. `i` runs that installer and `c` copies it. A CLI under `/nix/store` gets no installer, and the dialog says to update it the way it was installed. Pi needs 0.84.3 or later. No other CLI has a floor yet.

## When a status reads wrong

The rules are ours to fix, for everyone. Read the pane the way the poller reads it, with the session id in place of `SESSION_ID`, and [open an issue](https://github.com/YoanWai/agent-manager/issues/new/choose) with that text and the CLI's version:

```bash
tmux -L agentmgr capture-pane -p -t am_SESSION_ID
```

## Key bindings

The `keybindings` row in Settings opens one picker with two tables: the keys the manager keeps inside a session, and the keys of its own list. Each action takes one key or several, and an action can be turned off. One key serves one action within a table.

`↵` binds the key you press next, `a` adds a second key to an action, `d` turns the action off, and `r` names what would move and asks, then puts the shipped keys back on every action. A key its table cannot take is refused there with the reason. Leaving the picker stores each table you changed and puts the keys to work at once. Only a key you moved is stored, so an action at its shipped default follows that default when a release changes it.

The session actions are `detach` (default `ctrl+q` and `ctrl+\`), `review` (default `ctrl+r`), `editor` (default `f3`), `saved_prompt` (default `f4`) and `tmux_prefix` (off by default). A session key is `ctrl+` a letter, `alt+` a letter or digit, or `f1` to `f12`, since a bare character would take it away from the agent. `detach` always keeps at least one key. `tmux_prefix` gives managed sessions a tmux prefix of their own, one key or two, so the key your own tmux.conf uses as its prefix is free to be a session key, `detach` for one.

The list actions are `up`, `down`, `open`, `attach`, `step_in`, `step_out`, `reorder_up`, `reorder_down`, `new_session`, `terminal`, `new_group`, `fork`, `prompt`, `copy_reply`, `review`, `mark_idle`, `rename`, `move`, `editor`, `restart`, `kill`, `kill_all`, `revive`, `revive_all`, `archive`, `cancel_end`, `restore`, `delete`, `search`, `filter`, `archived`, `empty_groups`, `fold_all`, `resize`, `settings`, `messages`, `help`, `quit` and `new_connection`. A list key is a plain character, a key name such as `space` or `pgup`, `shift+` an arrow or tab, or the modifier forms above. `esc` and `ctrl+c` stay as they are. `esc` cancels everywhere. `ctrl+c` quits the manager even with `quit` turned off, and a focused session sends it to the agent. `settings` keeps at least one key.

The same tables drive a full-screen attach and focus mode, and a session created from the `agent-manager` CLI or by an agent through the MCP server carries them too. The footer, the `?` key map and the session footers all read the tables, so a moved key is named where it moved to.

## Themes

`s` opens settings, where `↑↓` or the wheel move between fields and `←→` change the focused one. A click on a row moves to it, and a second click changes it or runs it. `save and close` is a footer key like the others: `esc` runs it, `enter` runs it on a row with no action of its own, and so does a click on it. Eighteen palettes ship. Eleven dark: classic, solarized dark, catppuccin mocha, tokyo night, gruvbox dark, nord, dracula, rosé pine, monochrome, kanagawa wave and onedark. 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 the picker, so it 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 restores them on exit. 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 lands on the same side the manager is drawing.

**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, SSH included. A theme already on the detected side stays; only a mismatch switches. A palette with a light and a dark variant switches to its other half, so catppuccin mocha turns into latte and back, and any other palette switches to classic or solarized light. Your manual pick is kept separately, so turning the toggle off returns to it.

**background** picks what fills the cells behind the UI. `theme`, the default, paints them in the palette's colors, so they hold on a terminal that ignores the color request. `terminal` leaves them on the terminal's own colors, for a translucent or blurred window.

### Symbols

**symbols** switches between **Advanced**, the default, and **Compatible**, plain ASCII marks for a font that cannot draw the geometric ones. Compatible covers the status marks, the loader, the inbox, drag handles, group folds, terminal rows, worktree and fork marks, the prompt and reply prefixes, search, dialog titles, notices, pending archives and kills, and diff outcomes. The row previews the marks as you step it, and the choice applies at once. Fonts are not detected. Borders, gauges, arrows, key hints and agent output stay as they are. [Status](/docs/status/) has both sets of marks.

![The settings screen with the theme picker and a strip of palette swatches beside the theme name.](/assets/screenshot-settings.png)

_Fig. 5s · settings_

## Right-to-left text

Hebrew and Arabic rows are painted as the cells they occupy, the same on every host. A terminal that runs its own bidirectional layout, iTerm2's right-to-left support or WezTerm's `bidi_enabled`, reorders those rows itself; turn that support off to read the frame in the columns Agent Manager paints.

## State

Settings, order, groups, archive, the sessions layout and the split ratio live in `state.db`, and are restored on the next run.

---

Source: https://agent-manager.dev/docs/agents/

---
title: "Supported agents"
description: "The twelve coding CLIs that ship with live status, how each one is read and resumed, which ones the pickers offer, and how a new CLI gets in."
canonical: "https://agent-manager.dev/docs/agents/"
last-updated: "2026-10-23"
section: "Documentation"
source: "agent-manager"
---

# Supported agents

Twelve agents ship with working status out of the box, and every one of them is read the same way: the manager looks at the pane tmux hands back, or at Claude Code's own hook events, and colours the row by what it finds. Each profile lives in the binary, so an upgrade brings the current rules for all of them.

## The twelve

| CLI | Command | Where its status comes from | Reviving and forking |
| --- | --- | --- | --- |
| Claude Code | `claude` | Claude Code hook events, with pane rules as the fallback. | Resumes an exact conversation by session id, and forks one into a new session. |
| Codex | `codex` | Pane rules over the status row and the bottom-pane dialogs. | Resumes by the session id Codex mints, and forks it. |
| OpenCode | `opencode` | Pane rules over the spinner row and the gutter composer. | Resumes by the session id OpenCode mints, and forks it. |
| Grok Build | `grok` | Pane rules over the braille spinner and the approval dialogs. | Resumes an exact conversation by session id, and forks one into a new session. |
| Gemini CLI | `gemini` | Pane rules over the loading line and the bordered approval box. | Resumes an exact conversation by session id; a fork imports the session file. |
| Antigravity CLI | `agy` | Pane rules over the braille spinner, the esc to cancel footer and the navigate hint its dialogs draw. | Resumes by the conversation id agy mints. |
| Pi | `pi` | Pane rules anchored on the composer rules and the spinner. | Resumes an exact conversation by session id, and forks one into a new session. |
| Command Code | `cmd` | Pane rules over the composer placeholder, recap blocks, and the busy footer. | Resumes by the session id Command Code mints, and forks it. |
| Hermes Agent | `hermes --cli` | Pane rules over the REPL prompt markers. | Resumes by the session id Hermes records on first input. |
| Muse Code | `muse` | Pane rules over the interrupt hint, the trust prompt and the resume picker. | Resumes by the session id Muse records, and forks with `/fork` inside the running session. |
| Oh My Pi | `omp` | Pane rules over the status band, its spinner and timer, and the approval dialogs. | Resumes by the session id omp mints. |
| Cursor CLI | `cursor-agent` | Pane rules over the braille spinner, the ctrl+c to stop hint and the approval dialogs. | Resumes by the chat id Cursor mints. |

OpenCode means OpenCode 2 (`@opencode/cli`), which the manager runs with `--standalone` so each session has a server of its own. OpenCode 1 exits on that flag, and `opencode upgrade --major` moves it to 2. Pi needs Pi 0.84.3 or later, because earlier releases save every model they are asked about as your default. An older Pi opens a dialog on spawn, revive or the model list that names your version, the one it needs, and the installer that updates it. No other CLI is checked yet. Hermes is tested with Hermes Agent 0.20.0 and launches its classic REPL with `--cli`, so the input, approval and activity markers stay stable whatever its own preference selects. Muse Code is tested with Muse 1.3.0 and 1.4.0, and its first launch adds the agent-manager MCP server to `~/.config/muse/settings.json`, keeping every other setting. Antigravity CLI is tested with agy 1.2.14, and its first launch registers the agent-manager server with `agy mcp add`. Oh My Pi is tested with omp 18.2.11 and 18.4.2 in its default band composer and glyphs, and it coordinates through the agent-manager subcommands. Cursor CLI is tested with cursor-agent 2026.10.19-6bbf2c4. Its first launch writes the agent-manager server into `~/.cursor/mcp.json` and runs `cursor-agent mcp enable agent-manager`, and its model list comes from `cursor-agent acp`. Hermes, Antigravity, Oh My Pi and Cursor CLI have no fork. On native Windows, Command Code is left out of the pickers and a session asking for it is refused, because `cmd` is `cmd.exe`. It still runs inside WSL2. Every spawn is the CLI you already installed, with its login, config and MCP servers untouched. [Status](/docs/status/) explains how a pane becomes a colour, and [Sessions](/docs/sessions/#reviving) how a dead one comes back on the same conversation.

## Which ones the pickers offer

Every built-in CLI 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. It only narrows the pickers, so a session already on an unchecked tool keeps running and revives on that same tool.

Each CLI also reports the models, reasoning efforts and profiles it takes, and the `n` form and quick prompt mode offer them per session. [Model, effort and profile](/docs/sessions/#model) has the table.

## A CLI outside the list

The last row of that checklist, `request CLI support`, opens an issue prefilled for it. A request that carries the CLI's version and a capture of its pane at the prompt, mid-turn, on a permission dialog and after an error holds everything a profile needs, since status is a handful of regexes over that text. Read a pane the way the poller reads it with the session id in place of `SESSION_ID`:

```bash
tmux -L agentmgr capture-pane -p -t am_SESSION_ID
```

A profile arrives as a pull request against the built-in defaults, reviewed and shipped in the next release, so everyone running that agent gets it on their next update. The [contribution guide](https://github.com/YoanWai/agent-manager/blob/main/.github/CONTRIBUTING.md) covers the checks CI runs.

## The shell beside them

`T` opens a terminal tab, a session like any other with your `$SHELL` in the pane. It rests at idle throughout, since turn tracking belongs to agents, and it nests under the selected agent or lands in the selected group. [Sessions](/docs/sessions/#terminals) has the rest.

---

Source: https://agent-manager.dev/docs/keys/

---
title: "Keyboard shortcuts"
description: "Every binding in the list, in a session, in review, and in quick prompt mode."
canonical: "https://agent-manager.dev/docs/keys/"
last-updated: "2026-10-25"
section: "Documentation"
source: "agent-manager"
---

# Keyboard shortcuts

Everything is one key from the list. `?` shows this inside the app. The mouse works too. A click on a session row or its preview focuses it, a double click on a group folds or unfolds it, a row drags by its `⠿` handle (`=` with Compatible symbols), `[…]` or a right click opens a row's actions, a click on `messages` in the key legend opens the messages panel while any message is left, and a press on the divider between the list and the preview drags it. The full-screen layout selects on a click and focuses on a double click. The wheel stays inside the app: a notch moves the cursor in the list, scrolls the diff, and in a focused session walks that pane's scrollback, where click-drag selects pane text and copies it. Settings has a `mouse` row, on by default, that hands the mouse back to the terminal.

Every other screen takes the mouse the same way. A click on a key in a footer, a card's foot, the bottom edge of Messages or a pill in Review's header runs that key. In a list inside a card (Settings, CLIs, Keybindings, saved prompts, Move, Review's pickers, Messages) the first click picks a row and the second runs it, and the wheel moves the cursor. In Review a click opens a file or moves to a code line. A click beside a card closes it the way `esc` does, except a form holding typed text. Keys, cards and menu entries act when the button comes back up, so a press that slides off does nothing.

![Animated demo: 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.](/assets/demo-mouse.gif)

_Fig. 1mouse · focus, drag, row actions_

## The list

| Key | Action |
| --- | --- |
| ↑↓ / j k | Move the cursor. |
| → | Step into the row: focus the session, or open the group. In beta; Settings (`s`) turns the pair off. |
| ← | Step out: close the group, or, inside a focused session with the caret at the start of the agent's prompt, come back to the list. Anywhere else in the prompt it moves the caret as usual. |
| K / J | Reorder the session or group among its visible siblings. `shift+↑↓` does the same. |
| enter | Focus the session in place, so keys go to the agent and the list stays. On a group, fold it. |
| A | Attach the session full screen. Settings can swap this with `enter`. |
| F | Fold or unfold every group. |
| e | Hide or show empty groups. |
| n | New session: name, tool, the model, effort and profile the tool offers, directory, worktree toggle and the base it starts from, optional saved prompts and starting prompt, group. |
| 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. |
| w | Keep only the waiting, finished and errored rows. Press again to show all. |
| g | New group: name, parent, default path, worktree default, worktree base. |
| C | New SSH connection: another machine's sessions in this list. See [Sessions](/docs/sessions/#ssh). |
| r | Rename a session or cycle its tool; edit a group's name and default path. |
| m | Move a session to a group, a terminal into a session, or a group under another group. |
| / | Search by name. The field takes a fill of its own while it is open, and the query lights up inside every row that matched on its name. |
| \| | Resize the split: `←→` nudge, `enter` commits, `esc` cancels. |
| s | Settings: default tool, theme, theme follows OS, background, list density, sessions layout, header, computer stats, review layout, after quick prompt, session keys, ←→ step in/out, mouse, spawn in worktree, fetch on spawn, coordination, notifications, notify on finish, editor, keybindings, CLIs, saved prompts, symbols, docs, report a bug, suggest a change, and the version row that updates in place. |
| M | Messages: updates, maintainer messages and tips. New notices open on their own. While one is unread, the View footer paints `M unread` as a filled chip, and a click on it opens the same panel. `↑↓` pick one, `pgup/pgdn`, `home/end` and the wheel scroll it, `enter` opens its link, `u` updates and restarts, `r` refreshes, `x` dismisses one for good. On an update message, `Press u to update to vX.Y.Z and restart.` and the footer's `u update` draw in the key color. No other message has `u update` in its footer. |
| ? | Keys overlay. It scrolls (`↑↓`, `pgup/pgdn`, `g/G`) and `/` searches it down to one line. `o` opens the docs and leaves the overlay up. The docs row in Settings opens the same page. |
| q | Quit, leaving every agent running. |
| ctrl+c | Quit, leaving every agent running. A focused session sends it to the agent, and the manager stays up. |

## Sessions

| Key | Action |
| --- | --- |
| space | Quick prompt on a session, or spawn an agent on a group. |
| . | Mark a finished session idle without entering it. |
| y | Copy the newest reply to the clipboard while you stay on the list. |
| ctrl+r | Review this session's changes. |
| x / X | Kill this session (or a group's), or every live session in view. |
| v / V | Revive this session (or a group's), or every dead session in view. |
| f | Fork the conversation into a named session in the same group and directory. |
| R | Restart the 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. |
| c | Keep a session that asked to be archived or killed once its turn ends. |
| t | Toggle the archived view. |
| d | Delete the record, or a group and its subtree. |
| ctrl+q / ctrl+\ | Inside a session: back to the manager when the terminal leaves the key available. |
| ctrl+c | Inside a focused session, send ctrl+c to the agent. The manager stays up. |
| pgup / pgdn | Inside a focused session on its normal screen, walk that pane's tmux history when it has some and has not claimed mouse input. Otherwise the agent gets them. `alt+pgup` and `alt+pgdn` always reach the agent. |
| F3 | Inside a session: open its directory in your editor. |
| F4 | Inside a session: pick a saved prompt and paste it without sending. |
| click / double click | Focus a session from its row or its preview, or fold a group. The full-screen layout selects on a click and focuses on a double click. Focused beside the list, a click on another row focuses that one, a click anywhere else in the list column returns to the list, and the mouse back button returns in either layout. |
| drag ⠿ / = | Reorder the row, drop it into another group, or nest a terminal under an agent. `esc` puts it back. |
| […] / right click | 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. |
| wheel / drag | Focused: the wheel walks that pane's scrollback and a drag selects its text and copies it. Double-click copies the word, triple-click the line, a click on a link opens it in your browser, and `alt+drag` passes the whole drag to an agent's own UI. A click on the preview beside the list leaves the link alone. Over SSH a clicked link prints on your terminal and reaches your clipboard through OSC 52, and so do copies. |

## Review

| Key | Action |
| --- | --- |
| ↑↓ / j k | Scroll the file. |
| ctrl+d / ctrl+u | Half a page. |
| g / G | Top, bottom. |
| J / K | Next, previous file. `tab` and `shift+tab` do the same. |
| n / N | Next, previous change. |
| u | Side by side or unified. |
| s | Cycle the diff scope. |
| r / b / B | Repo, branch, merge target. |
| space | Mark the file reviewed. |
| f | Show code files only, hiding images, compiled assets and lock files; press again to show them. |
| c / d | Write or drop a draft. On a sent comment, mark it handled or open. |
| C | Send the current drafts as one numbered round. |
| o / F3 | Open the current file in your editor. |
| esc / q | Close the review. |

## Quick prompt

| Key | Action |
| --- | --- |
| enter | Send, or spawn in the group. |
| ↑↓ | Retarget while the bar stays open, or move the caret in a taller prompt. |
| tab | Step the spawn tool forward (`alt+m` also works). |
| shift+tab | Step the spawn tool back one. |
| ctrl+l | Pick the spawn's model from the ones the tool lists. Typing filters the list. |
| ctrl+x | Step the spawn's reasoning effort through the model's levels. |
| ctrl+y | Step the spawn's profile, for a tool that has them. |
| ctrl+t | Toggle worktree spawn on a group row (`alt+w` also works). |
| ctrl+o | Pick a saved prompt. It sits in the bar as a chip and becomes its text when you send. |
| ctrl+v | Paste a clipboard image as a chip. |
| backspace | Next to a chip, remove the whole chip. `delete` removes the chip after the caret. |
| ←→ | Step over a chip as one token. |
| esc | Close the bar. |

## Dialogs

The new session, new group, rename, fork and move forms all take the same four keys.

| Key | Action |
| --- | --- |
| tab / ↑↓ | Next field. |
| ←→ | Change a picker's value. |
| enter | Confirm. |
| esc | Cancel. |

---

Source: https://agent-manager.dev/writing/

---
title: "Writing"
description: "Long-form writing about building agent-manager."
canonical: "https://agent-manager.dev/writing/"
last-updated: "2026-10-23"
section: "Writing"
source: "agent-manager"
---

# Writing

Writing

# Notes from building the manager.

Technical field notes from the project: the edges of agent status detection, the review loop, and other things that did not fit in the docs.

- [Mouse support](/writing/mouse-support/): The wheel moves the cursor, a click selects a row, a double click focuses a session, and the mouse back button brings you back. · field note · 22 Sep
- [Live code review](/writing/live-review-race/): Keeping line comments attached while a coding agent changes the diff underneath the reviewer. · field note · 12 Aug
- [Claude Code hooks](/writing/claude-code-hooks/): Six things the hook API never reports, found while wiring a live status list that confidently said the wrong thing. · field note · 3 Aug

---

Source: https://agent-manager.dev/writing/mouse-support/

---
title: "Mouse support"
description: "The wheel moves the cursor, a click selects a row, a double click focuses a session, and the mouse back button brings you back."
canonical: "https://agent-manager.dev/writing/mouse-support/"
last-updated: "2026-10-23"
section: "Writing"
source: "agent-manager"
---

# Mouse support

A terminal UI has a key for everything, so a pointer looks like decoration. The reason to build one was that the pointer was already doing something, and what it did was wrong.

Thank you to [fruch](https://github.com/fruch), who wrote the interaction model in [pull request 475](https://github.com/YoanWai/agent-manager/pull/475). What follows is what it does, and why it is built this way.

> **Since v0.39.0** In the split layout one click now focuses a session, every row drags by its `⠿` handle, and `[…]` opens a row's actions. The full-screen layout keeps the model below, where a click selects and a double click focuses. [Keyboard shortcuts](/docs/keys/) has the current bindings.

The manager runs every agent session as a tmux pane behind one list. Keys move the cursor, open a session, fold a group, resize the split. None of that needed a mouse. What needed attention was the wheel, because a terminal will act on a wheel notch whether or not the application asked for it.

Alternate scroll is the mechanism. While the alternate screen is up, a terminal with DECSET 1007 enabled turns each wheel notch into an arrow key and sends it as input. The manager read those arrows the only way it could, as cursor movement. Scrolling the list walked the selection, and the session your next keystroke would reach was no longer the one you thought you had picked.

![The wheel moves the cursor, a click selects a row, a double click focuses it, a click on the list comes back, the divider drags, and a click on the messages card opens the messages panel.](/assets/demo-mouse-v038.gif)

_Fig. 1mouse · select, focus, drag, open_

## The wheel was already typing

An arrow key synthesised by the terminal is indistinguishable from an arrow key a person pressed. There is no flag on it and no way to ask where it came from, so the fix was to stop the terminal from making them.

`internal/ui/altscroll.go`

```go
// DECSET 1007 (alternate scroll) makes the terminal translate wheel events
// into arrow keys while the alt screen is up. The manager turns it off: the
// arrows are indistinguishable from real ones, so a wheel notch walked the
// session cursor.
const altScrollOff = "\x1b[?1007l"
```

That left the wheel inert in the list, which was the honest state to sit in while the real handling was written. Mouse reporting carries the wheel itself now, as an event the application can read, refuse or act on. Every mouse event is consumed rather than passed along, so an outer terminal or an outer tmux never scrolls the manager off its own screen while you are looking at it.

## A click needs the map the frame drew

Reporting a click is the easy half. The hard half is answering what was under it. The list had no row geometry to test against, because nothing had ever needed one. Rows are one line tall in compact density and two in comfortable. A search field, filter badges and an archive count sit above them and push everything below down. The window scrolls independently of the cursor.

Recomputing that layout inside the mouse handler would work until the day the rail gains a row, and then it would be wrong in a way nobody notices for a week. So the rail records what it painted, once per frame, off the final line slice.

`internal/ui/listview.go`

```go
func (m *Model) recordRailHits(lines []contentLine) {
    m.railHits = m.railHits[:0]
    for _, line := range lines {
        m.railHits = append(m.railHits, line.row-1)
    }
}
```

`railHits` maps every painted line to the row a click there selects, and holds `-1` for chrome that cannot be selected. The handler reads the map instead of the layout, and it still checks the row against the current list, because a poll can rebuild the rows between the frame that painted and the press that arrives.

`internal/ui/split.go`

```go
idx := y - y0
if idx >= len(m.railHits) {
    return 0, false
}
row := m.railHits[idx]
// A rebuild between the paint and the press can shrink m.rows under
// the hits this frame recorded.
if row < 0 || row >= len(m.rows) {
    return 0, false
}
```

The focused pane already worked this way, recording its box at paint time. The rail borrowed the pattern rather than inventing a second answer to the same question.

## The second press has to find the same row

A double click focuses a session, or folds a group. Two presses inside 400 milliseconds make the pair. The obvious way to remember the first one is to keep its row index, and the obvious way is wrong here. The list refreshes on a poll, sessions finish, groups fold, rows are inserted and removed. Index 4 in the first press and index 4 in the second can be two different sessions.

`internal/ui/split.go`

```go
// Matched on the row's identity: the poll rebuilds m.rows between
// the presses, so one index can name two different rows.
key := rowKey(m.rows[row])
double := !m.listClickAt.IsZero() && m.listClickKey == key &&
    time.Since(m.listClickAt) < multiClickWindow
```

The cursor moves to the row under the pointer before either gesture runs, so a wheel notch or a key pressed between the two presses cannot leave the action pointing somewhere else. A matched pair is then consumed, so a third press opens a new run instead of firing again.

## A press inside a session is two gestures at once

Focus mode puts you inside the agent's own pane, and some agents track the mouse themselves. A press there can mean two incompatible things. It can be a click the application under the pane should receive, or it can be the start of a drag that selects text to copy. The press alone does not say which.

So the press is parked, and whatever comes next decides. Motion away from the cell turns it into a selection from where it started. A release on the same cell turns it into a click and forwards it. A release on a different cell with no motion in between is a drag from a terminal that reports no motion, so it selects and copies.

A link gets one more answer. The manager opens it, because the terminal's own link opener cannot reach through the mouse claim while the application holds it, and the application has no opener of its own. Holding alt forces an entire gesture through to the pane, drags included, for the cases where you want the application to have it.

## The divider arms on the press

Resizing the split from the keyboard arms a resize mode first, which takes the keyboard with it until you commit or cancel. A drag cannot borrow that. A press whose release lands outside the window, or never lands at all, would strand the list in a mode nobody can see. So a mouse drag arms the drag alone and leaves the keyboard where it is.

A press and release on the seam with no motion between them is a click, not a resize. Reading it as a resize would persist a ratio nobody dragged to and reflow every live pane for a frame that never changed.

## Giving the mouse back

Mouse reporting has a cost, and it is paid by people who never asked for it. While it is on, the terminal stops doing native click-drag selection across the rail and the detail column, so copying a session name out of the list with the mouse stops working the way it always had.

Settings carries a mouse row for that reason. It is on by default, and only an explicit off is stored. Turning it off releases mouse reporting and hands the columns back to the terminal. Focus mode keeps its own forwarding either way, because that pane's mouse handling predates the setting and people rely on it.

One more seam sits between the two worlds. A press on a rail row while a session is focused leaves focus so that the same click selects the row, rather than asking for a keystroke first and a click after. The pane's blank tail, the chrome and a full screen session all keep the keyboard.

## What shipped

All of it is in v0.38.0. [Pull request 475](https://github.com/YoanWai/agent-manager/pull/475) closed most of [issue 110](https://github.com/YoanWai/agent-manager/issues/110). [Chiyo-no-sake](https://github.com/Chiyo-no-sake) made clicks pass through to a focused agent in [pull request 272](https://github.com/YoanWai/agent-manager/pull/272). Double click to focus, and the mouse back button to leave, came after in [pull request 526](https://github.com/YoanWai/agent-manager/pull/526).

---

Source: https://agent-manager.dev/writing/live-review-race/

---
title: "Reviewing code while an AI agent is still rewriting it"
description: "Keeping review comments attached to the right code while the producer keeps editing."
canonical: "https://agent-manager.dev/writing/live-review-race/"
last-updated: "2026-10-23"
section: "Writing"
source: "agent-manager"
---

# Reviewing code while an AI agent is still rewriting it

A line number is a position, not an identity. Keeping review comments attached while the producer keeps editing requires more than refreshing `git diff`.

Code review normally begins after the author stops editing. A commit, patch or pull request gives the reviewer a stable object. Coding agents break that assumption. I often start reviewing while an agent is still working, leave a comment on line 84, and watch that line become 91 before I have finished the next file.

The first version of my terminal review UI refreshed the diff and kept each comment's file and line number. It looked fine until an agent inserted code above a commented line. The comment stayed at 84; the code it referred to moved. Sending that review back to the agent now attached a precise instruction to the wrong code, which is worse than sending no location at all.

This is the loop I wanted to preserve: read the agent's changes as whole files, comment on lines, then send all of the comments back as one prompt while the agent keeps working.

![A terminal review where line comments are sent back to a coding agent](/assets/demo-diff.gif)

_Fig. 1live review · comment back to the agent_

Making that loop live required treating a review as concurrent state, not a rendered `git diff`.

## A line number is a position, not an identity

Each saved comment started with the obvious fields:

`internal/ui/diffview.go`

```go
type annotation struct {
    file    string
    line    int
    deleted bool
    excerpt string
    text    string
}
```

`line` is the new-file line number, except for a pure deletion, where it is the old-file number. That is enough to render a comment immediately and produce a useful `path:line` instruction. It is not enough to find the same code after a refresh.

The extra identity is `excerpt`: the trimmed source text from the line when the comment was made, capped at 60 runes. After a same-review refresh, I scan the new file for a line with the same excerpt and the same added/deleted side. If there is exactly one match, the annotation moves to its new line number.

`re-anchoring, simplified`

```
matches, target := 0, 0
for _, line := range file.Lines {
    num, deleted := annotationLine(line)
    if deleted == note.deleted && excerptOf(line.Text) == note.excerpt {
        matches++
        target = num
    }
}
if matches == 1 && !annotationOccupies(target) {
    note.line = target
}
```

This deliberately fails closed. Blank lines are not anchors. If the excerpt occurs twice, the old line number stays. If another comment already occupies the target, the comments do not collapse onto one line. If the agent rewrites the line itself, the excerpt disappears and the original location remains as a best guess.

An AST or syntax-aware fingerprint could follow more edits, but the review works across every language Git can show. A conservative text anchor has one important property: when it cannot prove where a comment belongs, it does not silently invent an answer.

## Not every reload is the same review

Re-anchoring by matching text is only valid when the surrounding identity has stayed fixed. Changing from uncommitted changes to the last commit may show the same file and source line, but it is a different patch. Switching worktrees or sessions is more obviously different.

Annotations are therefore keyed by session and repository path, not by display name:

`review identity`

```
func (m *Model) reviewKey() string {
    return m.diff.sessID + "\x00" + m.diff.repoSel
}
```

The separator prevents ambiguous concatenations, and the resolved repository path prevents two same-named repositories under one agent directory from sharing review state.

Loads also carry the session, scope, repository and a monotonically increasing generation. The UI drops a result if any of those no longer match the open review. A slow Git command from the previous scope must not repaint the current one merely because it completed later.

Only a silent refresh of the same session, repository and scope enables re-anchoring. A scope cycle or session switch replaces the file set without moving saved comments against content they were never written for.

This distinction mattered more than the matching algorithm. “The diff changed” and “the user asked for a different diff” can produce identical data shapes but require different state transitions.

## Freeze the target while the user is typing

Generation checks reject obsolete work, but they do not solve one smaller race:

1. a refresh starts;
2. the reviewer opens the comment editor on line 84;
3. the refresh completes and shifts the file;
4. the reviewer presses Enter.

At step four, which line owns the text?

The UI pauses new probes while the comment editor or send-confirmation box is open. More importantly, a load that started before the box opened is discarded if it lands while either box is active. The in-flight flag is cleared, but the old fingerprint is retained, so the next probe loads the current diff after the box closes.

`drop an in-flight reload`

```
if m.diff.annotating || m.diff.sendConfirm {
    m.diff.loading = false
    return nil
}
```

This freezes only the semantic target during the short interaction. The agent is not paused, Git is not locked, and the rest of the manager keeps updating. Once the comment is saved, the next refresh can move it using the excerpt captured from the stable frame the reviewer actually saw.

## Refresh cheaply, hydrate selectively

Rebuilding and syntax-highlighting every changed file on every poll made large reviews unpleasant. The refresh path is now split into two phases.

First, a cheap repository fingerprint answers whether anything relevant changed. If it did, the UI reloads file metadata and immediately hydrates the selected file. Files with durable review state—a saved annotation or a reviewed mark—also need their contents so that state can be checked and re-anchored. Those less urgent loads run serially in one background command instead of spawning a Git process for every file at once.

`reload order, simplified`

```
current := loadCurrentDiffFile()
for each file with comments or reviewed state {
    stateful = append(stateful, loadDiffFile(file))
}
return tea.Batch(current, loadSerially(stateful))
```

Each file result repeats the same identity checks—session, scope, generation, repository, index and path—before it can mutate the model. Index alone is unsafe because a new diff can reorder the file list while a load is running.

## The final prompt carries coordinates and context

When the reviewer sends, the comments become one numbered, single-line prompt:

`prompt sent to the agent`

```
Code review of your uncommitted changes — address each numbered point,
then summarize what you changed per point:
(1) internal/store/store.go:91 (code: `return rows.Err()`) — wrap this error;
(2) internal/ui/list.go:44 (deleted line) — keep this behavior
```

Newlines in comment bodies become separators so text cannot submit halfway through the paste. Each non-deleted note includes both the latest line number and its code excerpt. The coordinates make the instruction quick to locate; the excerpt makes it intelligible if the agent edits again between the last refresh and reading the prompt.

This is still not collaborative editing. There is no operational transform, no CRDT and no claim that arbitrary rewrites preserve identity. The useful guarantee is narrower: background refreshes cannot move the editor underneath the user, stale asynchronous work cannot replace a newer review, and comments only move when the program has unambiguous evidence for their new location.

That was enough to make reviewing and producing code overlap safely in practice.

> **The code** The implementation is in [internal/ui/diffview.go](https://github.com/YoanWai/agent-manager/blob/main/internal/ui/diffview.go), with the race and ambiguity cases in [internal/ui/diffview_test.go](https://github.com/YoanWai/agent-manager/blob/main/internal/ui/diffview_test.go). Both are part of [agent-manager](https://github.com/YoanWai/agent-manager), an Apache-2.0 Go TUI for running coding agents on tmux. In the UI, `ctrl+r` opens the review, `c` comments a line, and `C` sends the review back to that agent.

---

Source: https://agent-manager.dev/writing/claude-code-hooks/

---
title: "Reading Claude Code's hooks for live session status"
description: "Turning Claude Code hook events into the colour on a session row."
canonical: "https://agent-manager.dev/writing/claude-code-hooks/"
last-updated: "2026-10-23"
section: "Writing"
source: "agent-manager"
---

# What Claude Code's hooks can't tell you

Six things the hook API never reports, each one found the same way: a row in my status list that confidently said the wrong thing.

I wanted one thing from my terminal: a list of running agents where I can see, without looking at any of them, which one is working, which one is blocked, and which one is done.

The obvious way is to read the screen. Capture the pane, run regexes over it, guess. That works with any CLI tool, which is why it is still the fallback for every tool I support. But it is guessing. A spinner disappears for one frame and the agent looks idle. A tool prints something spinner-shaped and it looks busy.

Claude Code has hooks, so for Claude sessions the guessing should be unnecessary. It mostly is. This is about the part that isn't.

## The setup

Each managed session launches with a generated settings file passed as `--settings`, and gets one environment variable: a path to write status into.

`environment`

```
AGENT_MANAGER_STATUS_FILE=/…/hooks/<session-id>.status
```

Every hook is a one-line shell command that writes a word to that file.

`hook command`

```
[ -z "$AGENT_MANAGER_STATUS_FILE" ] || printf working > "$AGENT_MANAGER_STATUS_FILE"
```

The guard matters more than it looks. If Claude Code ever loads that settings file outside a managed session, the variable is unset, the command exits 0, and nothing happens. A hook that can fail is a hook that breaks someone's agent.

| Event | Writes |
| --- | --- |
| `UserPromptSubmit` | working |
| `PreToolUse`, `PostToolUse` | working |
| `Notification` | waiting, conditionally |
| `Stop` | finished |
| `SessionStart` | idle |
| `SessionEnd` | deletes the file |

A poller reads that file every two seconds. That is the whole happy path, and for a straightforward turn it is exactly right, with none of the regex guessing. Then you meet the edges.

## Notification means two different things

It fires when the agent is blocked on a permission prompt. It also fires after 60 seconds of an idle input box, as a nudge that Claude is waiting for you. Same event, and only the first one means the agent is stuck on something.

Take it at face value and every session you walk away from eventually claims to be blocked. The fix is to read the payload on stdin and drop the reminder.

`Notification hook`

```
[ -z "$AGENT_MANAGER_STATUS_FILE" ] || grep -q "waiting for your input" \
  || printf waiting > "$AGENT_MANAGER_STATUS_FILE"
```

## A plain-text question fires nothing

The agent finishing its turn with "should I also update the tests?" is, to the hook API, a completed turn. `Stop` fires, the file says `finished`, and the list shows a green checkmark on a session that will sit there forever waiting for a one-word answer.

There is no event for "the model asked you something in prose". The only place that information exists is on screen.

## Esc fires nothing either

Interrupt a turn and no `Stop` arrives. The last thing written was `working`, so the file says `working`, and it will keep saying `working` until you type something. A status that is wrong until the user acts is worse than no status: it is the exact case where you walk away trusting the list.

## Stop is about the main agent, not the work

`Stop` means the main loop stopped responding. Background agents it spawned can still be grinding. So the file says `finished` while real work continues, and if that work writes files you get a "done" row whose repo is still changing under review.

The inverse shows up too. Background subagents write `working` through `PreToolUse` and `PostToolUse`, and fire no `Stop` of their own when they end. Nothing ever clears it. Without a correction, that file stays pinned at `working` forever.

## SessionStart fires mid-turn

It fires on startup, on resume, on clear, and also on compact. Compact happens in the middle of an active turn, so an unmatched `SessionStart` handler will write `idle` over a session that is very much working. The matcher has to be explicit.

`claude-settings.json`

```json
"SessionStart": [{ "matcher": "startup|resume|clear", "hooks": [ … ] }]
```

## A crash skips your cleanup

`SessionEnd` deletes the status file. A crash or a `SIGKILL` does not run `SessionEnd`. The file survives its process and keeps confidently reporting `working` for an agent that no longer exists. So the process has to be checked independently, and a status file whose agent is gone has to be deleted rather than trusted.

## Where that leaves the design

Hooks are the first-tier source, and the screen is still read on every poll to correct them. A hook saying `finished` is upgraded when the pane shows a question, an error, or ongoing work. A hook saying `working` is upgraded when the pane shows the turn already ended.

`internal/ui/poller.go`

```go
switch hookStatus {
case status.Finished:
	if matched && (paneStatus == status.Waiting || paneStatus == status.Errored || paneStatus == status.Working) {
		return paneStatus
	}
case status.Working:
	if matched && (paneStatus == status.Waiting || paneStatus == status.Finished || paneStatus == status.Errored) {
		return paneStatus
	}
}
return hookStatus
```

The pane only reports a resting state once the newest turn has gone quiet, so this never fires mid-stream.

I expected hooks to replace the screen scraping. What they actually did is make it accurate. Hooks know things the screen cannot show, like a tool call that prints nothing, and the screen knows things hooks never fire for, like a question, an interrupt, or an error. Neither one alone gets a status list you can trust while you are not looking at it, which was the entire point.

> **The code** [internal/hooks](https://github.com/YoanWai/agent-manager/tree/main/internal/hooks) generates the settings file and reads the status files; `internal/ui/poller.go` holds the reconciliation. Both are in [agent-manager](https://github.com/YoanWai/agent-manager), a tmux TUI for running Claude Code, Codex and OpenCode side by side. Go, Apache-2.0.
