关于 tmux-skill
tmux-skill 指导编码 Agent 通过 Shell 以编程方式操作 tmux,涵盖持久会话、交互进程、输出捕获和可靠的完成检测。
主要功能
- 01创建分离且指定尺寸的会话
- 02向窗格发送按键并运行命令
- 03捕获屏幕输出与回滚内容
- 04用机器可读格式检查窗格
- 05通过标记或窗格状态检测完成
适用场景
- 01让长时间构建或服务器持续运行
- 02操作 REPL 和交互式终端应用
- 03检查运行中的进程或供人查看
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ agents365-ai/ 365-skills --skill tmux-skill安装后在 Claude Code 中输入/tmux-skill确认已加载。
附带文件.skillspector-baseline.yaml
详细介绍
面向编码 Agent 的 tmux
tmux 是终端复用器:它在窗格中运行程序,即使终端关闭,程序也会继续运行,并且可以通过编程方式检查、驱动和重新连接。 本技能介绍非交互式(编程式)tmux 用法:编码 Agent 应如何通过 shell 驱动 tmux。内容基于官方 tmux wiki。
何时使用 tmux(以及何时不使用)
遇到以下需求时,请使用 tmux:
- 需要让某个进程在 Agent 回合 / 终端结束后继续运行(长时间构建、服务、监视进程)
- 需要交互性:REPL、会提示输入的程序、TUI 应用(vim、htop、fzf)
- 需要真实的 TTY(拒绝在没有
isatty时运行的工具,或需要固定窗口大小的工具) - 需要在程序运行时查看实时输出,或让人类通过
attach连接并查看
如果简单的重定向就够用,则无需使用 tmux:cmd > out.log 2>&1 或
cmd & / nohup 更简单,而且你无需读取 TUI。
概念模型
server (one per socket, auto-started, exits when last session dies)
└── sessions ($0, $1, ... unique names)
└── windows (@0, @1, ... named, linked to 1+ sessions)
└── panes (%0, %1, ... one terminal + one running program each)
- 在脚本中,优先使用唯一 ID(
$3会话、@2窗口、%5窗格),而非名称 / 索引:ID 不会产生歧义,也不会因重命名 / 重新排序而改变。 - 默认套接字与用户的 tmux 共享。对于 Agent 专用会话,请使用隔离的套接字:
tmux -L pi-agent ...。所有命令都适用相同的标志。
核心循环(规范模式)
# 1. Create a detached session with a fixed size (else it defaults to 80x24,
# which mangles wide output you later capture)
tmux -L pi-agent new-session -d -s build -x 220 -y 50
# 2. Send a command. Append an echo sentinel to detect completion reliably.
# Grep the EXPANDED sentinel (__DONE__:<digits>): the echoed command line still
# contains the literal "$?", so a plain grep for __DONE__ matches the echo
# before the command even runs.
tmux -L pi-agent send-keys -t build 'make 2>&1; echo "__DONE__:$?"' Enter
# 3. Poll for the sentinel in the pane output
until tmux -L pi-agent capture-pane -p -t build | grep -q '__DONE__:[0-9]'; do sleep 1; done
# 4. Read the result (including scrollback)
tmux -L pi-agent capture-pane -p -S -2000 -t build
# -> last line tells you the exit code after "__DONE__:"
# 5. Clean up (or leave it running for the human to attach to)
tmux -L pi-agent kill-session -t build
使用哨兵值进行轮询,是最稳健的完成检测方式。tmux wait-for
(chan / wait-for -S chan)也可行,但存在竞态条件:如果信号在开始等待前触发,就会永远挂起;只有在你能控制两端时才使用它。
命令速查表
tmux ls # list sessions (exit 1 + "no server running" = none; not an error)
tmux new-session -d -s NAME # detached session; -x/-y set size; -c DIR sets start dir
tmux new-session -A -s NAME # attach-or-create (human-facing convenience)
tmux send-keys -t TGT 'cmd' Enter # type into a pane; Enter is a KEY NAME, not "\n"
tmux capture-pane -p -t TGT # pane text to stdout (-S -3000 scrollback, -J join wrapped lines)
tmux list-panes -a -F FMT # enumerate panes with a format string
tmux display-message -p -t TGT '#{...}' # expand a format for one target
tmux kill-session -t NAME # kill one session; kill-server kills everything
tmux split-window -h -t TGT 'cmd' # new pane right; -v below; -d don't focus it; -b before
tmux select-pane -t %5 # make a pane active
tmux set-option -t TGT remain-on-exit on # keep pane (and its output) after the process exits
tmux respawn-pane -t %5 # restart the program in a dead pane
tmux rename-window -t @2 name # windows auto-rename by default; pin with rename or disable automatic-rename
tmux run-shell 'cmd' # run once in tmux's shell, no pane
tmux set-hook -t s hook 'cmds' # run tmux commands on events (pane-died, after-send-keys, ...)
tmux wait-for CHAN / wait-for -S CHAN # synchronization primitive (see race caveat above)
-t 的目标格式:session:window.pane,例如 build:0.1,或直接使用 ID:-t %5 /
-t @2 / -t '$3'。对 $ 加引号,避免 shell 展开会话 ID。
send-keys 规则
tmux send-keys -t build 'echo "a b"' Enter # args are typed into the pane; the pane's
# shell does the parsing: your quoting must
# survive tmux AND the target shell
tmux send-keys -t build C-c # send Ctrl-C to interrupt
tmux send-keys -t build Up Enter # repeat last command (shell history)
tmux send-keys -t repl 'x = 1' Enter # drive a REPL one statement at a time
tmux send-keys -t build -l '100%' # -l: literal text, key names like "Enter" not interpreted
tmux send-keys -t build -H 41 42 # -H: hex-encoded keys (unicode-safe)
Enter、Tab、Space、BSpace、C-c、C-l、Up、Down、Escape、F1.. 等是 按键名称;未知参数会作为字面文本输入。- 绝不要发送
"\n"来表示换行:这会输入两个字符\和n。请发送Enter。 - 遇到复杂的引号处理(heredoc、嵌套引号)时,请编写脚本文件,然后发送
bash /tmp/script.shEnter,不要费力处理多层引号。 send-keys会立即发送并返回;它不会等待程序响应。- 绝不要通过脚本发送
C-b前缀:该前缀供连接到 tmux 的人类客户端使用。请直接使用 tmux 命令。
capture-pane 规则
- 默认只捕获可见屏幕。 使用
-S -3000捕获滚动历史中的最后 N 行,使用-S -捕获全部历史(受history-limit限制,默认值为 2000)。 -S 0 -从屏幕顶部捕获到历史末尾:-S 0 -E -。-J将软换行合并为逻辑行:解析时通常需要这样做。-e会包含 ANSI 转义序列(颜色);若要获取纯文本,请省略。- 行尾可能带有空格;比较时可使用
sed -e 's/[[:space:]]*$//'清除。 -a捕获备用屏幕(绘制整个屏幕的 TUI 应用)。
格式(检查 API)
列表命令中的 -F 和 display-message -p 会提供机器可读的信息:
tmux -L pi-agent list-panes -a -F '#{session_name}:#{window_index}.#{pane_index} #{pane_id} cmd=#{pane_current_command} pid=#{pane_pid} dead=#{pane_dead} path=#{pane_current_path}'
tmux -L pi-agent display-message -p -t build '#{pane_pid} #{history_size} #{cursor_y}'
实用变量:#{session_id}/#{session_name}/#{session_attached}、
#{window_id}/#{window_index}/#{window_name}/#{window_active}、
#{pane_id}/#{pane_index}/#{pane_active}/#{pane_dead}/#{pane_pid}/
#{pane_current_command}/#{pane_current_path}/#{pane_title}/#{pane_in_mode}、
#{history_size}、#{socket_name}、#{pid}(服务端 PID)。使用 #{q:...} 转义。
完整列表:man 1 tmux 中的 FORMATS 章节。
完成检测的四种方式
- 哨兵回显(默认,无竞态):
send ... 'cmd; echo __DONE__:$?' Enter,轮询capture-pane | grep。 remain-on-exit+#{pane_dead}:设置该选项,直接运行命令, 然后等待display-message -p -t %5 '#{pane_dead}'输出1。窗格会保留其输出,直到你执行respawn-pane或kill-pane。- 进程检查:
#{pane_pid}是窗格的 shell;如果你将cmd直接作为窗格命令启动,其退出会导致窗格关闭(可结合方式 2)。 wait-for:仅在你也能控制执行顺序时使用(参见上文的竞态注意事项)。
示例
长时间运行的任务,也由人类监控
tmux -L pi-agent new-session -d -s train -x 220 -y 50
tmux -L pi-agent send-keys -t train './train.sh' Enter
echo "watch with: tmux -L pi-agent attach -t train" # hand this to the user
多窗格监控
tmux -L pi-agent new-session -d -s mon -x 220 -y 50 -c "$PWD"
tmux -L pi-agent send-keys -t mon 'tail -f app.log' Enter
tmux -L pi-agent split-window -v -t mon
tmux -L pi-agent send-keys -t mon.1 'watch -n5 df -h' Enter
# capture one specific pane: -t mon.0 / -t mon.1, or the pane ID
REPL / 交互式程序
tmux -L pi-agent new-session -d -s repl -x 220 -y 50 'python3 -i'
sleep 1 # let it start before typing
tmux -L pi-agent send-keys -t repl 'print(6*7)' Enter
sleep 0.5
tmux -L pi-agent capture-pane -p -t repl # parse "42" between prompts
tmux -L pi-agent send-keys -t repl C-d # exit
运行并获取输出,自包含辅助工具
tmuxrun() { # tmuxrun SESSION 'COMMAND' -> prints output, exit code in $?
local s=$1; local t=$2
tmux -L pi-agent new-session -d -s "$s" -x 220 -y 50 || return 1
tmux -L pi-agent send-keys -t "$s" "$t; echo \"__DONE__:\$?\"" Enter
until tmux -L pi-agent capture-pane -p -t "$s" | grep -q '__DONE__:[0-9]'; do sleep 1; done
tmux -L pi-agent capture-pane -p -S -2000 -t "$s" | sed -e 's/[[:space:]]*$//'
tmux -L pi-agent kill-session -t "$s"
}
常见问题检查清单
- 新会话是否以分离模式创建并且指定了大小(
-d -x ... -y ...)?未指定大小的分离窗格默认为 80x24。 - 发送的是按键名称(
Enter),而不是字符串\n? - 引号在 tmux 和目标 shell 中都能正确保留?如果不能:使用临时脚本文件。
- 需要滚屏历史时,是否使用了
-S捕获?默认只捕获可见屏幕。 - 是否通过哨兵 /
pane_dead检测完成,而不是固定的sleep? - 哨兵 grep 是否匹配展开后的形式(
__DONE__:[0-9])?只 grep 哨兵名称会在命令运行前匹配到 shell 回显的命令行。 - 定位时是否使用 ID(
%0、@1、$2),而不是窗口名称(名称会自动更改)? - 使用默认套接字时,是否先检查了
tmux ls:用户可能有正在运行的会话,不能干扰它们(在默认套接字上执行kill-session/kill-server会终止用户的会话)? -
tmux ls的退出代码为 1,且显示 “no server running”,意味着没有会话,而不是出现故障。 - 是否清理了遗留会话(
kill-session/kill-server),并确认套接字文件已删除? - 程序退出后窗格意外消失?运行前设置
remain-on-exit on。
控制模式(高级,极少需要)
tmux -C attach(或用于原始应用的 -CC)使用文本协议:输入命令,每条命令的响应都由 %begin ... %end(成功)或 %begin ... %error(失败)保护行包围;实时窗格输出则以 %output %paneID text 通知的形式到达。适合构建持续运行、流式传输窗格输出的监控程序;对于一次性驱动,以上 shell 命令更简单。重度流式传输可使用流控(refresh-client -f pause-after=...、%pause、%extended-output)和格式订阅(refresh-client -B):参见
Control-Mode wiki 页面。
参考资料
- Wiki:https://github.com/tmux/tmux/wiki(Getting-Started、Formats、Control-Mode、 Events、Clipboard、FAQ、Recipes)
- 手册页:
man 1 tmux(所有命令、标志、格式变量)