一个 tmux 窗口里跑着多个 Agent 时,怎样先找到需要你回答的那一个?
当 Claude Code、Codex 和 OpenCode 同时在 tmux 的不同 pane 里运行时,真正消耗注意力的通常不是再开一个 Agent,而是“巡视”。你得在 session 之间切换,判断哪个任务仍在生成、哪个已经等你确认、哪个其实已经结束;如果窗口多,轮询本身就会打断正在做的工作。
tmux-agent-switcher 是一个 MIT 许可证的 tmux 插件,试图把这件事压缩成一次弹出式查看。它不是新的 Agent 调度器:不会替你启动、包装或接管 Claude Code、Codex、OpenCode。插件读取 tmux pane 元数据、可见屏幕文本和进程树,为 pane 标出 Working、Blocked、Idle 三种状态;你仍按原来的方式启动各个 CLI。
这一区分很重要。调度器需要知道任务、权限和队列;这个插件解决的是更窄的问题:已经有很多终端任务时,怎样快速定位“现在需要人”的 pane。对习惯用 tmux 保留多个仓库、多个分支和多段 Agent 会话的开发者,它更像一个可视化的中控快捷键。
为什么“等你输入”比“正在运行”更值得被看见
编码 Agent 的等待状态并不总是显眼。它可能在询问是否执行迁移、是否采用某个计划、是否允许调用工具;也可能因为命令失败而停在交互提示符。若你只凭 CPU 使用率或终端标签判断,很容易把“已阻塞、等待选择”的任务当成仍在工作。
插件把状态显示在两个位置:弹窗中的 pane 徽标,以及 tmux 的窗口标签。按默认键 Ctrl-n 会打开全屏弹窗:左边列出跨 session 的窗口,右边预览当前高亮窗口。这样无需真正切入某个 pane,就可以先看它最后显示了什么,再跳转过去处理。
它还尝试保留 tmux 用户常见的导航习惯。Ctrl-h/j/k/l 可跨 pane、窗口、session 移动;若焦点在 Vim 或 Neovim 中,按键会让编辑器接管。弹窗内部默认使用 Vim 风格选择:j/k 移动高亮,数字加 j 或 k 可按相对位置直接跳转,搜索模式会模糊匹配 session 名、窗口名、进程名和工作目录。
这意味着它适合“任务多但不想改工作流”的场景:Agent 仍在原生终端中运行,tmux 仍是会话管理层,插件只增加观察与跳转能力。
最小安装:先让 tmux 接管加载
项目要求 tmux 3.3 或更高版本,因为它使用了 display-popup;运行时还依赖系统已有的 bash 与 ps。如果使用 TPM(Tmux Plugin Manager),在 ~/.tmux.conf 中加入插件声明,然后执行 TPM 的安装快捷键:
set -g @plugin 'Ymirke/tmux-agent-switcher'
安装完成后,在 tmux 中按 prefix 加 I。项目说明称,首次使用会优先下载当前平台的预编译二进制;若没有匹配的二进制,且本机有 Rust 工具链,则回退到源码构建。对不使用 TPM 的环境,也可以直接克隆仓库,并在配置中加载入口脚本:
git clone https://github.com/Ymirke/tmux-agent-switcher \ ~/.tmux/plugins/tmux-agent-switcher
run-shell "~/.tmux/plugins/tmux-agent-switcher/tmux-agent-switcher.tmux"
如果你的机器只允许从源码安装,仓库提供了 Cargo 方式:
cargo install --git https://github.com/Ymirke/tmux-agent-switcher
安装后,不必改动 Agent 的启动命令。比如原本在 pane 中执行 claude、codex 或 opencode,仍照常执行;打开侧栏后,后台轮询器会在首次使用时启动,并持续刷新状态。
把快捷键和视图调到不干扰既有配置
建议把配置写在插件加载之前,避免 tmux 在载入时读到默认值。下面是一组偏保守的配置:保留默认弹窗键,开启状态标签,但关闭插件提供的 Ctrl-h/j/k/l 导航,以免覆盖自己已有的 pane 导航绑定。
set -g @agent_switcher_key 'C-n' set -g @agent_switcher_nav 'off' set -g @agent_switcher_view 'sidebar' set -g @agent_switcher_input 'keys' set -g @agent_switcher_tab_status 'on'
其中 @agent_switcher_view 可以取 sidebar(左侧)、sidebar-right(右侧)或 palette(浮动面板)。如果你的 tmux 状态栏高度定制,先使用 @agent_switcher_tab_status 'off' 观察几天:项目会尝试把汇总后的 Agent 状态追加到窗口标签,而不是替换原有格式;但任何会改状态栏的插件都值得先在个人配置中验证兼容性。
输入模式也有取舍。keys 适合已经熟悉 Vim 键位的人;numbers 给 session 和窗口编号;search 则适合窗口名较规范、能通过仓库名或进程名直接定位的团队。弹窗内按 Tab 可在 Vim、数字和搜索模式之间切换,因此不必为所有人强制选一种模式。
状态是启发式结果,不是任务真相
最需要谨慎的一点在于:Working、Blocked、Idle 不是 Agent CLI 直接暴露的标准协议。该项目通过 pane_current_command、OSC pane 标题、tmux capture-pane 的可见文本,以及 ps 得到的进程树快照来推断状态。Working 依赖屏幕上的活动指示,Blocked 依赖等待提示或选择界面,Idle 则在活动稳定后判定;项目还使用去抖处理,避免一次偶然采样就把任务误判成完成。
这套实现避免了 wrapper、PID 文件、FIFO、LD_PRELOAD 或日志抓取,部署侵入性很低;代价是它天然会受 CLI 界面变化影响。Claude Code、Codex 或 OpenCode 更新终端 UI、自定义主题改变提示符、非英文 locale 改变文字,都可能影响分类准确性。因此,不应把 Blocked 徽标当作自动审批信号,更不应据此触发部署、删除或提交等副作用操作。
较稳妥的用法是把它当作优先级提示:看到 Blocked 后跳进 pane,读取真实提示,再由人完成选择;看到 Idle 后检查最后几行输出和 Git diff;看到 Working 时不急于打断。对于需要审计的自动化流程,真正的状态来源仍应是任务系统、CI、Agent 自身日志或结构化事件,而不是终端屏幕。
一套可复用的并行会话操作法
为了让侧栏真正降低打断,而不是制造新的观察负担,可以给每个 Agent pane 一个明确的职责和可读的窗口名。例如,把一个 session 用于实现、一个用于测试、一个用于审查;在启动前用 tmux 的 rename-window 写入仓库与任务短名。这样搜索模式匹配到的是稳定的上下文,而不是一串难以辨认的 shell 名称。
实际操作时,可以采用“三段检查”:先打开侧栏,只优先处理 Blocked 的 pane;进入后先阅读最近输出、当前分支和未提交 diff,再决定回答、继续还是停止;对于被标为 Idle 的 pane,不把它立即视为成功,而是检查测试退出码、变更范围和 Agent 最后的总结。Working 的 pane 则默认不打断,除非它明显超出预期时长或同一任务需要释放资源。
这种流程把终端状态用于排序,而把正确性判断留给代码和可验证信号。比如一个 Agent 显示 Idle,可能只是停在失败后的 shell;一个 Agent 显示 Working,也可能正在等待网络请求。因此,侧栏最适合回答“下一眼该看哪里”,不适合回答“任务是否已经完成”。在多人共享远程开发机时,还应避免把可见终端内容当成跨用户的审计记录:pane 预览可能含有命令输出、路径或临时敏感信息,应沿用原有 tmux 访问控制和终端脱敏习惯。
适合谁,以及何时不值得加入
它最适合三类工作方式:一是在同一台开发机上并行跑多个编码任务;二是通过 tmux 保留跨 session 的长时间 Agent 会话;三是经常在“等确认的任务”和“仍在推理的任务”之间切换。此时一个只读侧栏能减少无目的切窗口。
反过来,如果团队任务都在远程队列、CI 或 Web 控制台中统一管理,终端 pane 并不是权威状态来源;如果只有一个 Agent,普通 tmux 状态栏已经足够;如果环境强制使用非英语 UI 或高度定制提示符,也应先验证状态识别再依赖它。
把 tmux-agent-switcher 放在合适的位置,关键不在于增加一层“智能调度”,而是承认并行 Agent 的一个日常摩擦:人的注意力同样是稀缺资源。它用被动观察换来更快的定位;只要把结果视为提示而非判决,就能在不改变现有 CLI 工作流的前提下,少花一点时间寻找那个真正等待你的窗口。