商业产业 Hacker News Agent 2026-07-30T09:34 正文 ok 2520 words

🔶 Agent-Manager:用于运行 Claude Code、Codex 和 OpenCode 的 Tmux TUI

英文标题:🔶 Agent-Manager: A Tmux TUI for Running Claude Code, Codex and OpenCode

Agent-Manager 是一个基于 tmux 的终端用户界面,可在同一终端中并行管理 Claude Code、Codex、OpenCode 和 Grok 等 AI 编程 Agent。它通过项目树、实时状态监测、快速提示、会话恢复和全屏 Git Diff 审查,减少开发者在多个终端标签页之间切换的成本。项目原生支持 Claude Code、OpenCode、Codex 和 Grok Build 的状态识别,也允许通过配置为其他 CLI 工具添加状态规则。该工具支持 macOS、Linux 和 WSL2,Agent 会话运行在独立 tmux 会话中,因此即使退出管理器也会持续执行。
打开原文相关 6 · 机会 1
media2.2 KB media1.3 KB media1.3 KB media1.2 KB media1.2 KB media390.0 KB media352.1 KB media689.8 KB media226.8 KB media31.9 KB media175.8 KB

中文

CI Release Platforms Go version License

agent-manager 演示

从一个终端运行所有 AI 编程 Agent。Claude Code、Codex、OpenCode 和 Grok 可并排运行,各自位于独立的 tmux 会话中,因此即使你退出管理器,它们仍会继续工作。

无需在终端标签页中逐一查找哪个 Agent 已完成、哪个卡住,所有会话都会显示在同一个列表中,提供实时状态,并按可折叠、可重新排序的项目树分组。无需附着到会话即可回复任一 Agent:space 会将提示词直接发送到会话的窗格,或在所选分组中启动新的 Agent。已终止的会话可通过 v 从中断处恢复。ctrl+r 则会打开 Agent 改动内容的全文件 Diff,并提供语法高亮;你在某一行留下的评论会直接发送回 Agent 的窗格。

目前,系统开箱即支持 Claude Code、OpenCode、Codex 和 Grok Build 的状态检测。任何其他 CLI 工具都可作为会话运行;如需获取其实时状态,可添加包含状态规则的 [tools.<name>] 配置块(参见“配置”)。

brew install yoanwai/tap/agent-manager

如果系统中尚未安装,安装过程会一并安装 tmux。

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

需要 Go 1.26+ 和 tmux;程序将安装到 $(go env GOPATH)/bin。

可从 Releases 下载(支持 macOS 和 Linux、amd64/arm64)。

可在 WSL2 中运行:agent-manager 基于 tmux,而 tmux 是 Linux/macOS 工具。在 WSL shell 中,可通过 Homebrew 安装,或从 Releases 下载 Linux 二进制文件。

管理器每天检查一次 GitHub Releases;如有新版发布,标题栏会显示 ↑ vX.Y.Z available 徽标。请按你的安装方式进行升级:

brew upgrade yoanwai/tap/agent-manager   # Homebrew
go install github.com/YoanWai/agent-manager@latest   # Go

会话运行在 tmux(am_* 命名空间)中,因此在退出管理器后仍会存活。在会话中,按 Ctrl+Q 可脱离并返回管理器。agent-manager --version 会输出版本号。

键位 操作
n 新建会话(名称、工具、目录、可选初始提示词、分组选择器)
g 新建分组(名称、父分组、默认路径)
enter 附着到会话 / 折叠分组
ctrl+q 在会话内:返回管理器
K / J(或 shift+↑ / shift+↓) 在可见同级项目中重新排序会话或分组
m 将会话移动至其他分组
r 重命名会话 / 编辑工具;编辑分组名称和默认路径
v 恢复已终止会话(revive_command,例如 claude --continue,会恢复对话)
a / u 归档 / 恢复会话,或一个分组及其完整子树
d 删除会话,或一个分组及其完整子树
space 快速提示:回复所选会话,或在所选分组中启动 Agent
ctrl+r 审查所选会话的改动:全屏全文件 Diff,将行评论发送至 Agent
f 折叠 / 展开分组
s 设置(快速启动工具、主题、审查布局)
t 切换归档视图
e 隐藏 / 显示空分组
/ 搜索
? 帮助
q 退出(会话继续运行)

按下 space,可在侧边栏底部停靠一个提示词输入栏。输入栏打开时,其目标会随光标移动(↑↓ 仍可用于导航):

  • 在 会话 行上,按 enter 会将输入文本直接发送到该会话的窗格,因此 Agent 可在你无需附着的情况下将其作为用户消息接收。输入栏会保持打开并清空,便于继续回复。
  • 在 分组 行上,按 enter 会在该分组中启动一个新的 Agent,并将提示词嵌入其中,使用该分组的默认路径。启动工具默认使用“设置”(s)中设定的工具;按 tab 可在工具之间循环切换(claude ↔ opencode ↔ 任意已配置工具),页脚会显示当前选择。Agent 会立即开始处理该提示词。

esc 会关闭输入栏。新建会话表单中的可选 prompt 字段也会以相同方式启动 Agent;对于 CLI 需要通过标志位接收提示词的工具,可使用 prompt_flag 声明(参见“配置”)。

未指定自定义名称的会话(所有快速启动的会话,以及名称留空的表单会话)会得到一个类似 claude-a1b2 的占位名称。其首条提示词会要求 Agent 针对该会话所处理的广泛功能(而非某项单独子任务)拟定一个简短名称,并执行一次 agent-manager rename "<name>"。该指令还会告诉 Agent,除非用户要求,否则不要再次重命名。当首条提示词无法携带该指令时(例如 /slash 命令或根本没有提示词),管理器会在工具的输入框出现在窗格中后,将该指令作为独立消息发送。该子命令会将名称写入一个按会话存储的文件;管理器会在下次轮询时读取它,并更新侧边栏行和 tmux 状态栏。该机制适用于任何工具,因为它只要求 Agent 读取提示词并执行一条 shell 命令。

由你自行命名的会话会保留该名称:首条提示词只会说明,如有需要,之后可以使用 agent-manager rename,而不会指示 Agent 立即重命名。之后你仍可要求 Agent 重命名会话,或在会话内的 shell 中自行运行 agent-manager rename。

声明待审查的仓库

一个会话的工作目录通常是容纳多个仓库的上层文件夹,因此审查功能只能猜测 Agent 实际指的是哪个仓库。知道自己正在处理哪个仓库的 Agent,可以在会话内的 shell 中运行 agent-manager review-repo <path> 进行声明。该子命令会检查路径是否为 Git 仓库,或是否位于 Git 仓库内,将其解析为仓库根目录,并将其写入按会话存储的文件;管理器会在下次轮询时读取它,之后再次打开审查时,便会针对该仓库启动。位于 Git 仓库之外的路径会被拒绝,因此该声明始终是事实,而非猜测。

Agent 也可在其 worktree 内运行 agent-manager review-base <ref>,声明其分支要与哪个目标进行 Diff:该引用会在对应仓库中经过验证,并按会话和仓库保存;此后“与目标比较”范围会使用它。agent-manager review-base --clear 可恢复自动检测。若已保存的引用无法再解析,审查中会显示错误,而 B 会打开目标选择器(仓库分支列表及一个 auto 选项),供你手动设置或清除该引用。

Agent 通常会在 Git worktree 中工作,每个 worktree 对应一个分支,而这些 worktree 可位于磁盘上的任意位置。只要声明的路径是 worktree 根目录,无论其位置如何都会被接受,因此一次 review-repo 调用即可同时指定待审查的仓库及分支。审查会按固定顺序解析其目标:在管理器持续运行期间,通过 r 或 b 手动选定的仓库优先;其次是 Agent 声明的仓库;最后才是自动排序结果(优先有未提交改动的工作树,然后是最近提交的仓库)。当选定或声明的路径不再是 Git 仓库时,审查会在状态行中提示,且可通过 r 选择正确的仓库。

MCP:Agent 如何发现这些命令

管理器启动或恢复的每个会话都携带 agent-manager MCP 服务器,因此支持 MCP 的 Agent 会将 rename、review_repo 和 review_base 视为原生工具,并看到说明何时应调用每个工具的描述:无需提示词注入,也不需要逐项目配置。服务器位于同一个二进制文件中(agent-manager mcp,stdio),并通过环境变量识别调用它的会话。

注册按工具分别进行。内置的 claude、codex、opencode 和 grok 工具会自动注册:claude 会获得生成的 --mcp-config 文件,codex 会获得 -c mcp_servers... 覆盖配置,opencode 会获得 OPENCODE_CONFIG 合并文件,grok 则会在首次启动时执行一次 grok mcp add --scope user。自定义工具可在其配置段中使用 mcp = "<style>" 选择启用,或使用 mcp = "none" 选择退出。无论是否使用 MCP,CLI 子命令始终可以正常工作。

在会话上按 ctrl+r,即可打开其仓库的全屏审查界面:左侧显示变更文件及 +/− 计数,右侧以语法高亮显示完整文件,并对改动行着色,因此每一处编辑都能在完整上下文中阅读。方向键和 ctrl+d/ctrl+u 用于滚动文件,g/G 跳转至顶部和底部,J/K(或 tab/shift+tab)切换文件,n/N 在改动之间跳转,u 切换统一视图和并排视图,s 循环切换范围(未提交改动、与目标比较、最后一次提交、已暂存改动),space 将文件标记为已审查。当工作目录中包含多个仓库时,r 会打开可输入筛选条件的选择器;b 则按分支名称列出当前仓库的 worktree,让你按一次键即可审查其他分支。B 用于选择“与目标比较”范围要比较的目标(即合并进入的分支)。标题栏中每个可变值都带有自己的按键提示,因此范围、布局、仓库和目标标签会一目了然地显示为 s、u、r、B 图例。随着 Agent 持续编辑,Diff 会自动刷新。

并排审查:在完整文件上下文中高亮显示改动行

在某一行按 c 可撰写评论;C 会将所有评论合并为一条审查提示词,并直接发送到 Agent 的窗格,因此你可以在观察 Diff 更新的同时,让 Agent 开始处理你的意见。esc 会关闭审查界面。

Diff 审查演示

分组是路径(backend/api/auth),构成深度不限的树。会话可位于任何节点,包括根节点。可通过 g 内联创建子分组,使用 K / J(或 shift+↑↓,排序会持久保存)重新排序分组和会话,通过 f 折叠子树,通过 e 在视觉上隐藏或恢复空分组,并使用 r 编辑分组名称和默认路径。在会话上,r 可重命名会话,tab 可循环切换工具(状态规则和恢复操作会跟随新工具;当你在窗格中退出一个 Agent 并启动另一个时,这很有用)。

系统会轮询每个会话的 tmux 窗格(默认每 2 秒一次),以推导状态:

状态 含义
working Agent 正在处理当前轮次
waiting 正在等待你处理:对话框、权限请求或纯文本问题
finished 当前轮次已结束——在你进入会话后,该提醒会清除为 idle
errored 工具报告了错误
idle 没有任务在运行
dead tmux 会话已不存在

会话树:预览中显示等待中 Agent 的权限请求

每一行都会内联显示其状态和工具;折叠的分组会保留每种状态的数量,因此即便子树被折叠,你仍能知道是否有内容需要处理。选中会话后,右侧会显示其窗格末尾内容,因此无需附着,也能看到 waiting Agent 实际提出的问题。

检测机制会依据每个工具的正则规则匹配可见窗格内容,分析最新轮次以区分 finished 和 waiting,并将两次轮询间持续变化的流式输出视为 working。即使某一轮在结束时没有轮次摘要行,系统也能判断其状态:当 working 窗格安静下来时,该轮会被判定为 finished;若结束于一个问题,则判定为 waiting。即使你位于会话内部,轮询仍会继续运行,因此状态保持实时更新。所选会话的窗格末尾会显示在预览面板中,移动光标时会立即获取预览。

对于 Claude Code,状态首先来自 hook events,而不是猜测窗格内容:会话启动时会携带一个生成的 --settings 文件,其 hooks 会将生命周期状态(working、waiting、finished、idle)写入按会话存储的状态文件,轮询器会优先读取该文件。窗格规则仍会进一步细化状态——hooks 无法看到纯文本问题、Esc 中断或错误行,因此匹配到的窗格判断会升级 hook 状态;当 hook 文件缺失或过期时,窗格规则会完全接管作为后备机制。可通过每个工具的 status_source = "claude-hooks" 启用。

标题栏会显示 Agent 集群汇总:按状态统计的会话数量,以及每个在线 Agent 完整进程树(shell、Agent 及其子进程)的 agents total usage: cpu N% · ram M% · X GB。CPU 是该进程树在上次轮询后消耗的 CPU 时间占机器总计算能力的比例(与计算机仪表一致,单位为 0–100%)。RAM 是常驻内存集占已安装内存的比例,旁边会显示绝对容量。所选会话的详细信息

English / 原文

CI Release Platforms Go version License

agent-manager demo

Run every AI coding agent from one terminal. Claude Code, Codex, OpenCode, and Grok run side by side, each in its own tmux session, so they keep working after you quit the manager.

Instead of hunting through terminal tabs to see which agent is done and which is stuck, every session shows up in one list with live status, grouped into a project tree you can fold and reorder. You answer any of them without attaching: space sends a prompt straight into a session's pane, or spawns a new agent in the selected group. A dead session revives where it left off with v. And ctrl+r opens a full-file diff of what an agent changed, syntax-highlighted, where a comment you leave on a line goes straight back to the agent's pane.

Status detection currently supports Claude Code , OpenCode , Codex , and Grok Build out of the box. Any other CLI tool can run as a session; add a [tools.<name>] block with status rules to get live status for it (see Configuration).

brew install yoanwai/tap/agent-manager

Installs tmux with it if missing.

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

Requires Go 1.26+ and tmux; installs to $(go env GOPATH)/bin.

Download from Releases (macOS and Linux, amd64/arm64).

Run inside WSL2: agent-manager lives on tmux, which is a Linux/macOS tool. In a WSL shell, install with Homebrew or grab the Linux binary from Releases.

The manager checks GitHub Releases once a day and shows a ↑ vX.Y.Z available badge in the header when a newer version is out. Pull it in the way you installed:

brew upgrade yoanwai/tap/agent-manager   # Homebrew
go install github.com/YoanWai/agent-manager@latest   # Go

Sessions run inside tmux (am_* namespace), so they survive the manager quitting. Inside a session, Ctrl+Q detaches back to the manager. agent-manager --version prints the version.

Key Action
n New session (name, tool, directory, optional starting prompt, group picker)
g New group (name, parent, default path)
enter Attach session / fold group
ctrl+q Inside a session: back to the manager
K / J (or shift+↑ / shift+↓) Reorder session or group among its visible siblings
m Move session to another group
r Rename session / edit tool; edit group name and default path
v Revive a dead session (revive_command, e.g. claude --continue, resumes the conversation)
a / u Archive / restore a session, or a group and its entire subtree
d Delete session, or a group + its entire subtree
space Quick prompt: answer the selected session, or spawn an agent in the selected group
ctrl+r Review the selected session's changes: full-screen whole-file diffs, line comments sent to the agent
f Fold / unfold group
s Settings (quick-spawn tool, theme, review layout)
t Toggle archived view
e Hide / show empty groups
/ Search
? Help
q Quit (sessions keep running)

Press space to dock a prompt bar at the bottom of the sidebar. The target follows the cursor while the bar is open (↑↓ still navigate):

  • On a session row, enter sends the typed text straight into the session's pane, so the agent gets it as a user message without you attaching. The bar stays open and clears, ready for the next answer.
  • 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 at the Settings (s) default and tab cycles it (claude ↔ opencode ↔ any configured tool); the footer shows the current pick. The agent starts working on the prompt immediately.

esc closes the bar. The new-session form's optional prompt field launches an agent the same way; tools whose CLI takes the prompt behind a flag declare it with prompt_flag (see Configuration).

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

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

Declaring the repo under review

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

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

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

MCP: how agents discover these commands

Every session the manager spawns or revives carries the agent-manager MCP server, so MCP-capable agents see rename, review_repo and review_base as native tools with descriptions telling them when to call each: no prompt injection, no per-project setup. The server lives in the same binary (agent-manager mcp, stdio) and identifies the calling session through its environment.

Registration is per tool. The built-in claude, codex, opencode and grok tools register automatically: claude gets a generated --mcp-config file, codex gets -c mcp_servers... overrides, opencode gets an OPENCODE_CONFIG merge file, and grok gets a one-time grok mcp add --scope user entry on its first launch. A custom tool opts in with mcp = "<style>" in its config section, or out with mcp = "none". The CLI subcommands keep working everywhere, MCP or not.

Press ctrl+r on a session to open a full-screen review of its repo: changed files with +/− counts on the left, the whole file on the right with syntax highlighting and changed lines tinted, so every edit reads in full context. Arrow keys and ctrl+d/ctrl+u scroll the file, g/G jump to top and bottom, J/K (or tab/shift+tab) switch files, n/N jump between changes, u toggles unified and side-by-side, s cycles the scope (uncommitted, vs target, last commit, staged), and space marks a file reviewed. When the working directory holds several repos, r opens a picker you type to filter, and b lists the current repo's worktrees by branch name so you can review another branch with one keypress. B picks the target (the merge-into branch) the "vs target" scope compares against. 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 diff refreshes as the agent keeps editing.

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

Press c on a line to write a comment; C flattens every comment into one review prompt and sends it straight into the agent's pane, so the agent starts addressing your notes while you watch the diff update. esc closes the review.

diff review demo

Groups are paths (backend/api/auth) forming a tree of unlimited depth. Sessions can live at any node, including the root. Create subgroups inline with g, reorder both groups and sessions with K / J (or shift+↑↓; the order persists), fold a subtree with f, hide or restore empty groups visually with e, and edit a group's name and default path with r. On a session, r renames it and tab cycles the tool (status rules and revive follow the new tool; useful when you quit one agent in the pane and start another).

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

Status Meaning
working The agent is busy on a turn
waiting Blocked on you: a dialog, a permission ask, or a plain-text question
finished Turn ended — an alert that clears to idle once you enter the session
errored The tool reported an error
idle Nothing running
dead The tmux session is gone

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

Each row carries its status and tool inline, and a folded group keeps a count per status so a collapsed subtree still tells you whether anything needs you. Selecting a session shows the tail of its pane on the right, which is how a waiting agent's actual question reaches you without attaching.

Detection matches per-tool regex rules against the visible pane, analyzes the newest turn to tell finished from waiting, and treats streaming output (content changing between polls) as working. A turn that ends without any turn-summary line still resolves: when a working pane goes quiet, the turn counts as finished, or waiting when it ends on a question. Polling keeps running while you are inside a session, so statuses stay live. The selected session's pane tail renders in the preview panel, and moving the cursor fetches the preview immediately.

For Claude Code, status comes first-hand from hook events instead of pane guessing: sessions launch with a generated --settings file whose hooks write the lifecycle state (working, waiting, finished, idle) to a per-session status file that the poller reads first. Pane rules still refine it — hooks cannot see a plain-text question, an Esc interrupt, or an error line, so a matching pane verdict upgrades the hook status — and they take over fully as fallback when the hook file is missing or stale. Enabled per tool with status_source = "claude-hooks".

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

The Computer block in the sessions panel shows machine gauges:

  • CPU : whole-machine utilization (0-100%)
  • Memory : used/total. On macOS this matches Activity Monitor's Memory Used (resident RAM minus free, speculative, and reclaimable file cache). On Linux it is Total - MemAvailable, so file cache is not counted as used.
  • Swap : used/total of the current swap allocation (used/total * 100). On macOS the swap file grows under pressure, so the denominator is the live size from vm.swapusage, not a fixed partition.
  • Disk : fill of the root filesystem (used / (used + available)), with free space from the kernel's available figure
  • Network : up/down rates on real NICs only (loopback, utun, bridges, and similar virtual interfaces are excluded)

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

settings, with the theme picker and its palette swatches

Nine palettes ship: classic, solarized dark, catppuccin mocha, tokyo night, gruvbox dark, nord, dracula, rosé pine, and monochrome. The swatch strip beside the name previews the palette, and the theme applies as you step through it, so the picker is a live preview of the whole UI. The manager also matches the terminal's own background to the palette, so the window has no seam against it, and restores the terminal's background on exit. Your pick is saved with the rest of the state and restored on the next run.

Config lives in your OS user config dir (~/Library/Application Support/agent-manager/config.toml on macOS, ~/.config/agent-manager/config.toml on Linux) and is created on first run with working defaults for Claude Code, OpenCode, Codex, and Grok Build.

Top-level: poll_interval (default "2s") sets how often panes are polled for status, preview, and stats.

Add any CLI tool as a [tools.<name>] block:

[tools.mytool]
command = "mytool"
default_status = "idle"
rules = [
  { state = "working", pattern = "esc to interrupt" },
  { state = "errored", pattern = "(?im)^\\s*error:" },
]

Rules match top-down against the visible pane text; first match wins, and default_status applies when nothing matches. Optional per-tool fields refine detection: activity_cutoff (regex locating the tool's input box, everything above it is turn content), turn_end (a turn-summary line marking the turn as over), chrome_line, blocked_line, and trailing_note. The generated config's claude and opencode blocks show all of them in use.

Other per-tool fields: revive_command is what v runs to revive a dead session (e.g. claude --continue resumes the conversation in place); status_source = "claude-hooks" switches status to Claude Code hook events (see Status).

prompt_flag controls how the new-session form's optional prompt is embedded into the launch command. Tools that take the prompt as a positional argument (Claude Code: claude 'the prompt') leave it empty; tools whose positional argument means something else declare the flag (OpenCode: prompt_flag = "--prompt", since its positional argument is the project path). The prompt only shapes the launch command; revive (v) uses revive_command untouched.

State is stored next to the config in state.db (SQLite).

go test ./...   # includes end-to-end tests against a real tmux server
go run .

MIT