Skip to content

agent-manager

Ten coding agents, one terminal list: live status read from each tmux pane, a quick prompt that answers them without attaching, a dead session revived on its own conversation, and a diff review whose comments go back to the agent as one numbered round.

Screenshot of agent-manager
Editor screenshot, 1 Oct 2026agent-manager ↗

What it is

A terminal workspace for the coding agents a developer already has installed, and it does not reimplement any of them: each session launches the user’s own CLI, so login, subscription, config files, MCP servers and every feature that tool ships carry over unchanged. Ten are supported — Claude Code, OpenCode, Codex, Grok Build, Gemini CLI, Antigravity CLI, Pi, Command Code, Hermes Agent and Muse Code — each in a persistent tmux session of its own on a private server, all in one list with live status. The list is not a menu you have to leave: space sends a prompt into the selected session’s pane, enter focuses it with the list still on screen, and v brings a dead session back on its own conversation. ctrl+r opens what an agent changed as full-file, syntax-highlighted diffs, where comments left on lines go back to that agent’s pane as one numbered review round. A session can also spawn, message and wait on another, since every MCP-capable session launches with those tools registered. It is Apache-2.0, written in Go on Bubble Tea, and runs on macOS, Linux and Windows under WSL2.

Who built itThe repository’s 647 commits come from 27 accounts, but 563 of them are his; next come 18, a dependency bot at 11 and a coverage contributor at 9. Of the 41 co-author trailers, 13 name a Claude model, and the ones carrying a version are Opus 5 and Opus 5.5 in a million-token context.

How it is put together

The parts · 6

One long-lived terminal program holding a set of agent processes that outlive it. The program owns a tmux server of its own, launches the user’s installed CLI into one session per agent, and then works by observation and injection: it reads panes to decide what each agent is doing, writes into panes to answer them, and never speaks to an agent through a protocol the agent does not already expose. Two consequences shape the code. Everything that varies between CLIs is data rather than code — launch and resume flags, status patterns, the quirks of each interface — so a new model or a changed screen is a configuration change, and the instructions call a maintained list of values that an upstream release can change a defect. And because the interface is one Bubble Tea model that must never block, the poll, the diff, the clipboard check and the update check are commands rather than inline work, which is why the poller and the state machine are the two largest source files in the tree.

internal/status and internal/ui/poller.go
The state machine and the thing that feeds it: a 36,736-byte classifier with 123,149 bytes of tests, and a 52,629-byte poller that walks every session’s pane on an interval the user can configure. The rules and launch flags they apply live next door in a 38,344-byte configuration file whose built-in tool table is the only source of tool definitions in the program.
internal/tmux
A dedicated tmux server reached through one driver: a 40,294-byte tmux layer with 63,488 bytes of tests and a 9,529-byte control-mode client. Every call names the agentmgr socket, the pane base index is pinned to zero because a CLI can split its own window, and paste goes through a buffer whose name carries the process id.
internal/ui
107 files and 1,785 KB: one Bubble Tea model with a 73,774-byte shell, a 63,274-byte list view and a 39,324-byte lifecycle file around it, then files per feature — quick prompt, composer, focus and selection, settings, key map, row menu, rename, reorder, drag and drop, split layout, themes, notices and toasts.
internal/agentsession, internal/mcpserver and internal/mcpreg
How the manager learns and asserts identity. Nine files read the session identifier out of each CLI’s own store — one file per CLI plus a 31,389-byte capture layer — while a 44,127-byte MCP server with 48,382 bytes of tests gives agents tools to spawn, message and wait on each other, and a 13,440-byte registrar writes the server entry into each CLI’s configuration.
internal/git, internal/diff and internal/store
The state the program owns itself: git plumbing for worktrees at 25,422 bytes with 48,647 bytes of tests, a 13,478-byte diff engine, and a 56,283-byte SQLite store with 59,598 bytes of tests whose migration list is append-only because the loop swallows duplicate-column errors.
The documents and the assets
Ten documents govern the work: 11,313 characters of agent notes carrying the invariants and pitfalls, a 4,426-byte design system written as tokens under the name of a field desk, a 4,372-byte product document holding the thin-wrapper principle, a 1,932-byte review guide, and four documents under docs/ led by a 66,562-byte usage reference.

Choices, and what they beat

  • Read every varying value out of the CLI at runtime over a maintained list of models and options in the binary

    The pull request that added model, effort and profile selection per session states the reason: the model pickers in other tools went stale the week a model shipped, so the values are read from the CLI through the interface it offers programs and the binary holds only that CLI’s launch flags. The choice is saved on the session, so a restart, a revive, a relaunch and a fork all run on it.

  • Stay a thin wrapper around the CLIs over mirroring each tool’s features in the manager

    The product boundary is written down: keep the program a thin wrapper around the supported interfaces, preserve upstream defaults and user configuration, avoid hardcoded models and version-specific behaviour. Supporting a CLI does not mean copying its feature set — where discovery has no stable, documented interface, the feature stays in the underlying tool.

  • Sessions on a private tmux server, reached through one driver over lending the user’s own tmux server

    Sessions live on a server named agentmgr, so they never mix with the tmux a user runs. The invariant that follows is that every call names its socket through the driver, because a bare tmux command resolves through the environment to the developer’s own server, and a global option or a session kill on a socket this process does not own is data loss.

  • A worktree per session as an opt-in property over a worktree as the unit the tool is built around

    For CCManager the worktree is the unit of work and the program owns its lifecycle, restoring a session by re-running its command. Here the session is the unit: the worktree toggle sits on the new-session form, in the quick prompt and in Settings, and revive restores the conversation rather than replaying a launch command.

  • Ask before a batch action, and keep each key in its own view over letting one key do the same thing everywhere

    Reviving more than one dead session asks first, on the argument written in the pull request that each revived agent is a running CLI on the user’s account, so a batch revive gets the same question as a batch kill. The restore key then became the mirror of the archive key and works only in the archived view: in the active view it had opened its card on rows with nothing archived.

  • Treat the release note as structured input to the interface over a changelog written for a web page only

    The messages panel reads the bullets of two named sections and nothing else, so a release note is effectively a schema: short bullets, each under 120 characters or cut mid-word in the panel, one person per thank-you bullet, and prose that stays on the web page. Every message also carries the version it announces, because an unbounded entry shows to every user forever.

Read fromAGENTS.md (11,313 characters), DESIGN.md, PRODUCT.md, REVIEW.md, .github/CONTRIBUTING.md, the README in full, docs/usage.md (66,562 bytes) and the other three documents under docs/, and the complete 311-file tree with sizes.

Build log

6 stages
  1. 01

    Status is read off the pane, not reported by the agent

    Nothing here asks an agent to report what it is doing. The manager polls each session’s tmux pane, captures the text, normalises it and classifies it into one of the states the interface draws — working, waiting, finished, idle or error — with a per-CLI set of rules. That is why internal/status is a 36,736-byte file with 123,149 bytes of tests beside it, and why those rules live in configuration rather than a branch of code: builtinTools is the only source of tool definitions, and it loads on every start, so an edited pattern reaches every install. Two invariants keep it honest. Panes are read through the engine’s plain-text path rather than tmux’s escape-preserving capture, because a running step’s marker blinks: its off frame is a styled blank cell only the escape-preserving capture shows, so a rule matched on it flaps. And the capture is normalised once — status, activity hashes, quoted prompts and input guards read the same result while the preview keeps the original. The rest is per CLI: OpenCode rows need a sidebar cut out of the capture using the composer’s rendered border and grapheme-aware widths; Codex needs a rule that skips a message typed with Enter during a turn, which was being read as the agent’s last reply; Gemini needs that mechanism plus one optional rule, so an approval dialog quotes the question it is asking instead of the prompt echo.

  2. 02

    Diff review happens inside the manager, and the comments travel back

    ctrl+r opens a session’s changes as full-file diffs with syntax highlighting, not as a patch: changed lines are shown in the context of the whole file. A reviewer comments on a line with c, and C sends the round back into the agent’s pane as one numbered review prompt rather than a stream of separate messages, and comments already sent stay visible as open or handled. The same review is reachable without the interface, through subcommands that take a repository, a base ref and a scope, and the state of a round lives in a mailbox file under the manager’s directory. The review also follows the agent rather than the launch directory: the list row, the JSON session listing and the review follow tmux’s own record of where the pane is, so a session that used its CLI’s change-directory command is reviewed where it now stands. The verification rules are unusually specific. The suite drives a real tmux server; the variable pointing at the developer’s own server must be unset before running it; the socket directory has to stay short enough that tmux does not silently fall back to the default socket; and killing that server is forbidden. A green suite is explicitly not enough for a change to the interface, the poller or a tool’s status rules: that change is verified by running the binary on its own socket under a throwaway home directory and reading frames back from a real CLI.

  3. 03

    A session is the unit, and a worktree is one of its properties

    This is the clearest line between this project and the archive’s other terminal session manager. CCManager is built around the git worktree: the worktree is what it lists, creates, merges and deletes, and its restore deliberately re-runs a session’s launch command without bringing the conversation back. agent-manager starts from a tmux session instead. Each agent runs in a persistent session of its own on a private tmux server named agentmgr, so they never mix with the tmux a user runs, and quitting the manager does not end them. A worktree is a property a session can have: spawning into one is off by default and is toggled on the new-session form, in the quick prompt or in Settings, landing at <repo>-worktrees/<name> on a branch named am/<name>. What is restored is the conversation. v revives a dead session on its own conversation, R restarts it on an empty context with the same name, group, directory and tool, and f continues a conversation in a separate named fork. Each CLI is asked what it supports rather than assumed to be like the others: Hermes and Antigravity have no fork. The worktree plumbing is real, and the instructions record the trap that comes with it — git stash is repo-wide across worktrees, so with several sessions on one checkout a pop lands on somebody else’s work, and the rule is to commit to the branch instead.

  4. 04

    Eleven weeks, 65 releases, and a refactor the author filed against himself

    The repository was created on 2026-07-15 and held 1,545 lines of Go, its largest file 266 lines, and one state model with 20 fields. Eleven weeks later its author opened an issue against his own code: 65 releases and 429 merged pull requests on, main holds 43,491 lines of source and 58,355 lines of tests, the state model has 119 fields and 560 methods, and the file holding it has grown from 1,270 to 2,330 lines. The one restructure along the way is dated: on 2026-08-01 a change broke a 1,866-line key file into feature files and gathered the model’s loose fields into sub-structs, taking it down to 54 — after which new features kept landing in the same central files, which is the issue’s actual complaint. Its title asks for the code to be restructured by feature, and the author’s own comment writes out the end state he has in mind: a model of about 25 fields where each feature owns its state, with one services struct holding the configuration, store, tmux driver, hooks and git. His 647 commits run 277, then 216, then 154 by month. The first outside reply links a pull request on a fork, which is where the refactor stands: specified, not done. The archive’s release evidence covers the last twenty, v0.23.0 in August to v0.39.0 on 2026-09-26.

  5. 05

    The release note is a data source with a schema

    Pushing a version tag is the only way to ship: goreleaser runs on Actions with two secrets and attests build provenance over the checksums file, and only the repository’s admin role can push the tag. The instructions single out a silent failure mode — without the Arch packaging secret the Arch step skips without failing, so the run still reports success while the package goes stale — and whoever cuts the release is told to confirm from the log that the release, the Homebrew cask push and the Arch push all appeared. What makes this unusual is that the note itself is read by the program. The manager’s messages panel reads exactly the bullets under a Highlights heading and under a Thank you heading; prose in those sections stays on the web page, and only bullets travel. That is why the house style asks for a summary in the author’s own words above them: the generated list of pull requests says what landed, not what is different now. Thank-you bullets name one person each, and the instructions say to cover issue reporters as well as authors, because the generated changelog names authors only and a fix exists because somebody wrote the bug up. Two limits come with the format: a bullet past 120 characters is cut mid-word in the panel, and every entry carries the version it announces, because an unbounded entry shows to every user forever.

  6. 06

    A coverage campaign, a review bot, and the author’s own bug reports

    The busiest outside contributor spent late September raising test coverage. One issue is the ticket; eleven pull requests here answer it, each naming the package it lifts and why the package’s coverage did not count: code reached only through end-to-end tests contributes no lines to its own profile. The numbers moved in public — a terminal-escape package from 56 to 75 percent, the hook and clipboard packages past the 80 percent bar, the command layer from 73 to about 84. Asked whether being assigned those pull requests meant work for him, the maintainer answered that assignment is bookkeeping — every pull request is assigned to its author, his included — and that a label, not the assignee, shows whose move it is. Review is partly delegated to a code-review bot whose configuration is 12,142 bytes. He also files his own issues: four of the open ones here are his, two written as bugs with a reproduction — an agent killed with Ctrl-C that takes the launch script down with it on a Linux system whose shell is dash, closing the pane with no relaunch hint and no shell, and a CLI’s change-directory command that moves the agent inside its interface while the process keeps its original directory. Outside work lands: the update route learned to recognise a binary inside a read-only store and print advice rather than write beside it — a fix from packaging this program for a Nix repository.

Adjacent records

All records →