skill-system-tmux について
シェルを使うエージェント向けに、コマンドを直接実行するかtmuxに回すかを判断し、セッション管理の規約を示すスキルです。長時間・対話型・タイムアウトの恐れがある処理を対象に、命名や後片付けも扱います。
主な機能
- 01所要時間と対話性に応じた実行方法の選択
- 02規約に沿ったtmuxセッションの命名
- 03永続セッションの開始・再利用・終了
- 04出力取得と処理完了の確認
- 05エージェントセッションと残存プロセスの整理
活用シーン
- 01長時間のビルドやインストールのバックグラウンド実行
- 02ツール呼び出し間での開発サーバーの維持
- 03接続切断後も継続するSSHタスクの実行
- 04分離したエージェントやテストスイートの並列実行
インストール
コマンドをコピーしてターミナルで実行します。
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
タイムアウトのリスクがある場合、処理の継続が必要な場合、または対話型ターミナルへのアクセスが必要な場合は、シェルコマンドを tmux 経由で実行します。このスキルでは、判断のためのヒューリスティックとセッション管理の慣例を説明します。tmux のチュートリアルではありません。
判断マトリクス
このマトリクスを使って、直接 bash と tmux のどちらを使用するか判断します。
| 条件 | 実行方法 | 例 |
|---|---|---|
| 推定実行時間が 60 秒を超える | tmux | git clone <large>、npm install、docker build、トレーニングスクリプト |
| コマンドが自動終了しない | tmux | npm run dev、python app.py、jupyter notebook |
| TUI または対話型入力が必要 | tmux | vim、htop、pudb、claude |
| 分離した並列実行が必要 | tmux | 複数のエージェント、並行するテストスイート |
| SSH 経由のリモート実行 | tmux | 接続が切れても実行を継続する必要があるコマンド |
| 単一処理で、対話型ではなく、高速(30 秒未満) | 直接 bash | ls、grep、git status、cat、mkdir |
| 1 回限りの実行で、出力が予想できる | 直接 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
ルール:
- エージェントが作成するセッションには、必ず
oc-プレフィックスを付けます — 一括識別とクリーンアップが可能になります - 小文字を使い、区切りにはハイフンを使用します(アンダースコアは使用しません)
- 複数のプロジェクトを扱う環境では、プロジェクトのコンテキストを含めます
- 既知の
oc-セッション名がある場合は、それを再利用します(例:oc-exp-runner) - プレフィックスのないレガシーなエージェントセッションを引き継いだ場合は、再利用する前に
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:準備完了の検出
コマンドを送信した後、完了を検出するためにポーリングします:
- ペインの出力を取得します
- 出力の末尾にシェルプロンプトがあるか確認します(正規表現:
/(\\$|>|#)\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:リソースの一括解放
エージェントが作成したすべてのセッションを解放し、古いリソースを検出します。次の場合に実行します:
- マシンの動作が遅い、またはメモリ使用量が多い場合
- エージェントによる長時間の作業セッションの前後
- ユーザーからクリーンアップを依頼された場合
ステップ 1:エージェントのセッションを特定する
tmux list-sessions -F '#{session_name} #{session_activity}' | grep '^oc-'
ステップ 2:エージェントのセッションをすべて終了する
tmux list-sessions -F '#{session_name}' | grep '^oc-' | xargs -I{} tmux kill-session -t {}
ステップ 3:古い非エージェントセッションを検出する
次の条件に該当するセッションは、古くなっている可能性があります:
- 最後のアクティビティから 6 時間以上経過しており、フォアグラウンドプロセスが実行されていない
- セッションにアイドル状態のシェルプロンプトしかない(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}
重要:既知の非エージェントのインフラセッションは、ユーザーから明示的な確認を得ずに終了しないでください。関連プロジェクトのレガシーな名前は、エージェント管理対象のセッションとして扱う前に oc-* に統一する必要があります。
エージェントツールとの統合
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 ツール
タイムアウト境界付近のコマンドでは、tmux を使うより timeout パラメーターを増やしてください。
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)に限定してください。
kill/再作成後に session not found が発生する
エラー:can't find session: oc-xxx
kill してから新規作成するパターンで競合が発生した場合によく起こります。簡単なガードを追加してください。
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
セッションはすでに存在します。再利用するか、kill してから新規作成してください。
# 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- 接頭辞なしでエージェントのセッションを作成する | 必ず oc-audit、oc-verify、oc-build のように接頭辞を付けます |
capture-pane を短い間隔で繰り返し確認する | 確認の間隔を 1~2 秒にします |
| 古いセッションを終了せずに新しいセッションを開始する | 同じセッション名では、必ず終了してから新規作成します |
| 行を消去せずにコマンドを送信する | 行に入力が残っている可能性がある場合は、コマンドの前に C-u を送信します |
| タスク完了後もセッションを実行したままにする | セッション終了時に一括回収(パターン 6)を実行します |
capture-pane に interactive_bash を使う | capture-pane には必ず Bash ツールを使います |
ブロックされたコマンドを interactive_bash で再試行する | すぐに Bash ツールに切り替えます |
既存プロジェクトの規約
このスキルシステムの関連プロジェクトには、古い tmux の命名パターンが残っている場合がありますが、このリポジトリのエージェント所有セッションは oc-* に統一する必要があります。
- セッション名
oc-exp-runner:実験実行用のエージェント所有セッションに推奨される名前です - ウォッチャーセッション:
tmux send-keysを使ってエージェントを起動するバックグラウンドモニターです machines.json設定:tmux_sessionフィールドでマシンごとのセッション名を定義します- kill してから新規作成:すべての実験スクリプトで使われる標準的な後片付けパターンです
既存の tmux 規約があるプロジェクトで作業する場合は、実際のユーザー/インフラのセッションを維持しつつ、引き継いだエージェントセッションは作業を続ける前に 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."
}
}