关于 skill-system-tmux
为使用 shell 的 Agent 提供命令路由与 tmux 会话管理规范,判断命令应直接运行还是放入 tmux。适用于需要持久运行、交互终端或超时风险的任务,并强调会话命名和清理。
主要功能
- 01根据耗时和交互性选择执行方式
- 02按约定命名 tmux 会话
- 03启动、复用和关闭持久会话
- 04捕获输出并检查任务就绪状态
- 05清理 Agent 会话与残留进程
适用场景
- 01在后台运行长时间构建或安装命令
- 02保持开发服务器在工具调用间持续运行
- 03通过 SSH 执行可承受连接中断的任务
- 04并行运行隔离的 Agent 或测试套件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ arthur0824hao/ skills --skill skill-system-tmux安装后在 Claude Code 中输入/skill-system-tmux确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
SKILL.behavior.yamlSKILL.spec.yamlscripts/test_tmux_tool.pytmux_tool.py详细介绍
Skill System Tmux
当 shell 命令可能超时、需要持久运行或需要交互式终端访问时,通过 tmux 执行。本技能提供决策启发式和会话管理约定,而非 tmux 教程。
决策矩阵
使用此矩阵决定使用直接 bash还是 tmux。
| 条件 | 执行方式 | 示例 |
|---|---|---|
| 预计耗时 > 60s | tmux | git clone <large>、npm install、docker build、训练脚本 |
| 命令不会自行终止 | tmux | npm run dev、python app.py、jupyter notebook |
| 需要 TUI 或交互式输入 | tmux | vim、htop、pudb、claude |
| 需要并行隔离执行 | tmux | 多个 Agent、并发测试套件 |
| 通过 SSH 远程执行 | tmux | 必须在连接断开后继续运行的命令 |
| 原子、非交互且快速(<30s) | 直接 bash | ls、grep、git status、cat、mkdir |
| 单次执行且预期会有输出 | 直接 bash | python script.py(快速)、git diff、npm test(短时) |
默认:直接 bash。 只有满足上述条件时才使用 tmux。
耗时估算启发式
可能超过超时时间的命令:
- Git 操作:
clone(大型仓库)、push(大型载荷)、checkout(工作树 >1GB) - 安装软件包:
npm install、pip install(依赖项很多)、cargo build - 构建工具:
docker build、make(大型项目)、webpack(生产构建) - 数据处理:训练脚本、预处理流水线、大型文件传输
- CI/测试套件:大型项目的完整测试运行
会话命名约定
使用包含项目上下文的名称,便于阅读:
Format: {project}-{purpose}
Agent-created sessions (MANDATORY prefix):
oc-{purpose} # e.g., oc-audit, oc-verify, oc-render
oc-{project}-{task} # e.g., oc-skills-build, oc-subproject-test
User/infra sessions (no prefix required):
postgres-dev
frontend-watch
ml-training
规则:
- 由 Agent 创建的会话必须使用
oc-前缀——便于批量识别和清理 - 使用小写字母,以连字符分隔(不要使用下划线)
- 在多项目环境中包含项目上下文
- 如果已有常用的
oc-会话名称,则重复使用(例如oc-exp-runner) - 如果继承了一个没有此前缀的旧 Agent 会话,在重新使用前将其重命名为
oc-* - 切勿创建仅由数字组成的会话名称(例如
531)
核心模式
模式 1:启动后不管(后台任务)
适用于不需要实时输出的长时间运行命令:
# Kill any existing session with same name, then start fresh
tmux kill-session -t {session} 2>/dev/null || true
tmux new-session -d -s {session} bash -c '{command}'
使用 interactive_bash 工具:
tmux_command: new-session -d -s {session}
tmux_command: send-keys -t {session} "{command}" Enter
模式 2:持久服务
适用于开发服务或必须持续运行的进程:
tmux_command: new-session -d -s {session}
tmux_command: send-keys -t {session} "{command}" Enter
稍后检查是否仍在运行:
tmux_command: has-session -t {session}
模式 3:捕获输出
读取 tmux 会话产生的内容:
tmux_command: capture-pane -p -t {session}
查看更长的历史记录(最近 1000 行):
tmux_command: capture-pane -p -t {session} -S -1000
模式 4:就绪状态检测
发送命令后,轮询以检测命令是否完成:
- 捕获窗格输出
- 查找输出末尾的 shell 提示符(正则表达式:
/(\\$|>|#)\s*$/m) - 如果出现提示符,则命令已完成
- 如果预期时长过后仍未出现提示符,则认为命令仍在运行或已挂起
模式 5:干净关闭
完成后务必清理会话:
tmux_command: kill-session -t {session}
先终止再新建模式可防止产生僵尸会话:
tmux_command: kill-session -t {session}
# (ignore error if doesn't exist)
tmux_command: new-session -d -s {session}
模式 6:批量回收资源
回收所有由 Agent 创建的会话,并检测过期资源。在以下情况下执行此操作:
- 机器运行迟缓或内存占用较高时
- Agent 长时间工作会话开始前或结束后
- 用户要求清理时
步骤 1:识别 Agent 会话
tmux list-sessions -F '#{session_name} #{session_activity}' | grep '^oc-'
步骤 2:终止所有 Agent 会话
tmux list-sessions -F '#{session_name}' | grep '^oc-' | xargs -I{} tmux kill-session -t {}
步骤 3:检测过期的非 Agent 会话
以下情况表明会话可能已过期:
- 上次活动时间超过 6 小时,且没有正在运行的前台进程
- 会话只有一个空闲的 shell 提示符(capture-pane 只显示
$或╰─)
# List sessions with last activity timestamp
tmux list-sessions -F '#{session_name} #{session_activity}'
# Compare #{session_activity} (epoch) against current time
# Capture pane of suspects to verify idle state
步骤 4:回收孤立进程
# Find opencode processes whose parent tmux pane was killed
ps aux | grep opencode | grep -v grep
# Check if port is held by a zombie
lsof -i :{port} 2>/dev/null
# Force kill if needed (SIGTERM first, SIGKILL if unresponsive after 5s)
kill {pid} && sleep 5 && kill -0 {pid} 2>/dev/null && kill -9 {pid}
重要:未经用户明确确认,切勿终止已知的非 Agent 基础设施会话。来自同级项目的旧会话名称应先规范为 oc-*,然后再将其视为由 Agent 管理的会话。
与 Agent 工具集成
interactive_bash 工具
主要接口。直接传递 tmux 子命令(不带 tmux 前缀):
# Create session
tmux_command: new-session -d -s oc-my-session
# Send command
tmux_command: send-keys -t oc-my-session "npm run dev" Enter
# Read output
tmux_command: capture-pane -p -t oc-my-session
# Kill session
tmux_command: kill-session -t oc-my-session
带超时的 bash 工具
对于接近超时边界的命令,优先增加 timeout 参数,而不是使用 tmux:
bash(command="npm install", timeout=300000) # 5 min timeout
仅在以下情况使用 tmux:
- 无法可靠估算超时时间
- 进程必须在当前工具调用结束后继续运行
- 需要交互式/TUI 访问
已知错误与解决方法
interactive_bash 中的 capture-pane 被阻止
错误:'capture-pane' is blocked in interactive_bash
这是最常见的 tmux 错误。interactive_bash 工具会阻止某些 tmux 子命令(包括 capture-pane、pipe-pane、save-buffer)。执行这些操作时,始终改用 Bash 工具。
# WRONG — will be blocked:
# interactive_bash: tmux_command: capture-pane -p -t oc-dev-server
# CORRECT — use Bash tool:
tmux capture-pane -p -t oc-dev-server
# Capture with history (last 1000 lines):
tmux capture-pane -p -t oc-dev-server -S -1000
# Capture and grep for specific output:
tmux capture-pane -p -t oc-dev-server | grep -i "error\|ready\|listening"
规则:对于任何 tmux 只读操作(capture-pane、list-sessions、display-message、show-options),优先使用 Bash 工具。将 interactive_bash 留给修改操作(send-keys、new-session、kill-session)。
杀掉并重新创建后出现 session not found
错误:can't find session: oc-xxx
在先杀掉再新建的操作发生竞争时常会出现此问题。添加一个简短的保护措施:
tmux kill-session -t oc-build 2>/dev/null || true
tmux new-session -d -s oc-build bash -c 'make all'
使用 new-session 时出现 duplicate session
错误:duplicate session: oc-xxx
该会话已存在。可以复用该会话,或先杀掉再新建:
# Reuse (send new command to existing session):
tmux send-keys -t oc-build "make clean && make all" Enter
# Or kill and recreate:
tmux kill-session -t oc-build 2>/dev/null || true
tmux new-session -d -s oc-build bash -c 'make all'
出现 no server running / tmux 未启动
错误:no server running on /tmp/tmux-*/default
tmux 服务未运行。任何 new-session 命令都会自动启动它:
tmux new-session -d -s oc-init echo "tmux ready"
工具选择速查
| tmux 子命令 | 使用 interactive_bash? | 使用 Bash 工具? |
|---|---|---|
new-session | 是 | 是 |
send-keys | 是(优先) | 是 |
kill-session | 是 | 是 |
capture-pane | 否 — 已阻止 | 是(必须) |
list-sessions | 避免使用 | 是(优先) |
has-session | 是 | 是 |
pipe-pane | 否 — 已阻止 | 是(必须) |
display-message | 避免使用 | 是(优先) |
反模式
| 不要这样做 | 应改为 |
|---|---|
| 通过 tmux 路由所有命令 | 对快速、原子操作直接使用 bash |
| 忘记在使用后杀掉会话 | 始终使用 kill-session 清理 |
使用 s1 这类通用会话名称 | 使用描述性名称:oc-project-purpose |
创建没有 oc- 前缀的 Agent 会话 | 始终添加前缀:oc-audit、oc-verify、oc-build |
| 以紧密循环轮询 capture-pane | 每次轮询间隔 1-2 秒 |
| 不杀掉旧会话就启动新会话 | 相同会话名称始终先杀掉再新建 |
| 未先清除当前行就发送命令 | 如果当前行可能残留输入,发送命令前先发送 C-u |
| 任务完成后仍让会话继续运行 | 在会话结束时运行批量回收(模式 6) |
使用 interactive_bash 执行 capture-pane | 执行 capture-pane 时始终使用 Bash 工具 |
在 interactive_bash 中重试被阻止的命令 | 立即切换到 Bash 工具 |
现有项目约定
此技能系统的同级项目可能包含较旧的 tmux 命名模式,但此仓库中由 Agent 管理的会话必须统一为 oc-*。
- 会话名称
oc-exp-runner:实验执行时优先使用的 Agent 自有名称 - 监视器会话:通过
tmux send-keys唤醒 Agent 的后台监视器 machines.json配置:tmux_session字段定义每台机器的会话名称- 先杀掉再新建:所有实验脚本中使用的标准清理模式
在具有现有 tmux 约定的项目中工作时,保留真正由用户/基础设施管理的会话,但在继续操作前,将继承的 Agent 会话重命名为 oc-* 格式。
{
"schema_version": "2.0",
"id": "skill-system-tmux",
"version": "1.0.0",
"capabilities": ["tmux-session-start", "tmux-capture", "tmux-wait", "tmux-session-list", "tmux-session-kill"],
"effects": ["proc.exec"],
"operations": {
"start-session": {
"description": "Start a long-running command in a managed tmux session.",
"input": {
"name": { "type": "string", "required": true, "description": "Logical session name" },
"command": { "type": "string", "required": true, "description": "Command to execute inside tmux" }
},
"output": {
"description": "Started session metadata",
"fields": { "status": "string", "session_name": "string" }
},
"entrypoints": {
"unix": ["python3", "{skill_dir}/scripts/tmux_tool.py", "start-session", "{name}", "{command}"]
}
},
"capture-pane": {
"description": "Capture output from a managed tmux session.",
"input": {
"session_name": { "type": "string", "required": true, "description": "Target session name" },
"lines": { "type": "integer", "required": false, "description": "Number of lines to capture" }
},
"output": {
"description": "Captured output",
"fields": { "status": "string", "output": "string" }
},
"entrypoints": {
"unix": ["python3", "{skill_dir}/scripts/tmux_tool.py", "capture-pane", "{session_name}"]
}
},
"wait-completion": {
"description": "Poll a managed tmux session until completion.",
"input": {
"session_name": { "type": "string", "required": true, "description": "Target session name" },
"timeout_seconds": { "type": "integer", "required": false, "description": "Maximum wait time" }
},
"output": {
"description": "Completion result and final output",
"fields": { "status": "string", "completed": "boolean", "output": "string" }
},
"entrypoints": {
"unix": ["python3", "{skill_dir}/scripts/tmux_tool.py", "wait-completion", "{session_name}"]
}
},
"list-sessions": {
"description": "List managed tmux sessions.",
"input": {},
"output": {
"description": "Managed session rows",
"fields": { "status": "string", "sessions": "array" }
},
"entrypoints": {
"unix": ["python3", "{skill_dir}/scripts/tmux_tool.py", "list-sessions"]
}
},
"kill-session": {
"description": "Kill a managed tmux session.",
"input": {
"session_name": { "type": "string", "required": true, "description": "Target session name" }
},
"output": {
"description": "Kill result",
"fields": { "status": "string", "session_name": "string" }
},
"entrypoints": {
"unix": ["python3", "{skill_dir}/scripts/tmux_tool.py", "kill-session", "{session_name}"]
}
}
},
"stdout_contract": {
"last_line_json": false,
"note": "Agent-executed; uses interactive_bash tool for tmux operations."
}
}