feat: tmux セッション整理コマンド tmux-first / tmux-clean を base イメージへ追加 - #99
Conversation
VS Code のウィンドウが異常終了 (クラッシュ / ホスト再起動 / 接続断) すると、 VS Code サーバー側に pty が取り残され、tmux クライアントだけがセッションへ 繋がったまま残る。統合ターミナルの起動スクリプトはそのセッションを「使用中」と 判定するため、<repo>-1 に戻れず <repo>-2, <repo>-3 ... と新しいセッションが 増え続ける。 tmux-first は対象セッションに居座っている自分以外のクライアントを切断し、 一番若い番号のセッションへ現在の端末を切り替える。ベース名は現在のセッション名 から末尾の -数字 を除いて推定し、対象は数字の昇順で最小のものを選ぶ。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017BpznFY4zi8kvY7hhHV7EK
tmux-first で若い番号のセッションへ戻したあと、置き去りになった <repo>-<数字> のセッションを削除するコマンドを追加する。 既定では安全側に倒し、keeper (tmux 内なら現在のセッション、外なら最小 番号)、アタッチ中のセッション、シェル以外を実行中のセッションは残す。 -n で削除対象と除外理由を確認でき、-f ですべて削除する。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017BpznFY4zi8kvY7hhHV7EK
Dockerfile 内にシェルスクリプトをベタ書きすると差分が読みづらく、 シンタックスハイライトや shellcheck も効かないため、 containers/base/tmux-first, tmux-clean として切り出して COPY する (entrypoint.sh / dind と同じ方式)。スクリプト本文に変更はない。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3p9U34HsArSpxJ1E1SK94
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 1 | codex | REQUEST_CHANGES
既定動作で正常なクライアントまたはバックグラウンド処理を終了し得るため、安全な判定または明示的な破壊操作への分離が必要です。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 1 | gemini | APPROVE
シェルスクリプトの堅牢性(文字列比較の安全性、パイプラインのエラーハンドリング、エッジケースへの配慮)や仕様の安全性(keeper の選定、dry-run の実装)が十分に担保されており、懸念点はありません。
レビュー指摘 (codex round 1) への対応。 - tmux-first: 対象セッションに接続しているというだけで切断せず、最終操作から TMUX_FIRST_IDLE 秒 (既定 300) 以上経過したクライアントだけを居座りとみなす。 操作中のクライアントは残し、-f を付けたときのみ全件切断する。 dry-run では切断対象に加えて残すクライアントと経過秒数も表示する。 - tmux-clean: pane_current_command は foreground しか見えないため、 pane のシェル (pane_pid) に子プロセスが残っていれば実行中とみなして残す。 /proc/<pid>/task/*/children を優先し、無い環境では pgrep -P にフォールバック。 - CHANGELOG / docs へ上記の既定動作を反映。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3p9U34HsArSpxJ1E1SK94
🔧 cross-review fix | round 1対応件数: 2 件 (修正 2 / deferred 0 / rejected 0)
修正内容
あわせて CHANGELOG / deferred / rejectedなし。 検証
|
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 2 | codex | REQUEST_CHANGES
TMUX_FIRST_IDLE の不正値で -f 相当の切断が起きないよう、破壊操作前の入力検証が必要です。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 2 | gemini | COMMENT
セッションやプロセスの依存関係を丁寧に辿り、使用中の端末やバックグラウンドジョブを保護する堅牢な実装でした。シェルスクリプトのベストプラクティス(set -eu の活用、安全な区切り文字の選択、/proc と pgrep の併用など)が適切に適用されており、非常に高品質です。1 点だけ、実行中の並行終了によるスクリプトの中断を防ぐための軽微な修正を提案します。
- tmux-first: TMUX_FIRST_IDLE に負数や数値以外が入ると全クライアントが idle 条件を 満たし、-f なしで操作中の端末まで切断されるため、引数解釈後に 0 以上の整数か検証し 不正値では exit 2 で終了する - tmux-clean: kill-session の直前にセッションが終了していた場合の競合エラーで set -e により後続の削除処理が止まらないよう || true を付与 - CHANGELOG / docs/user/environment-variables.md に TMUX_FIRST_IDLE の制約を追記 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3p9U34HsArSpxJ1E1SK94
cross-review round 2 — 修正サマリ修正コミット: 83b1d73
1. TMUX_FIRST_IDLE の入力検証 (major)
オプション解釈の直後に 0 以上の整数かを検証し、不正値では 2. kill-session の競合エラー (minor)セッション一覧の取得から ドキュメント整合
検証
deferred / rejected: なし(未 Resolve の 2 件はいずれも修正のうえ Resolve 済み) |
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 3 | codex | REQUEST_CHANGES
設定値検証と削除結果の報告について、2件の修正が必要です。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 3 | gemini | APPROVE
シェル非依存でのコマンド提供と、セッション削除競合時のログの正確性向上について、いくつかマイナーな修正提案をインラインに残しました。確認をお願いします。
- tmux-first: `${TMUX_FIRST_IDLE:-300}` を `${TMUX_FIRST_IDLE-300}` に変更。
明示的な空文字が既定値 300 に化けて検証を素通りしていたため、
「0 以上の整数以外はエラー終了」というドキュメントの契約と食い違っていた。
- tmux-clean: `kill-session || true` が競合以外の失敗も成功として KILL 表示・
KILLED 加算していた。削除成功時のみ加算し、対象消失は skip、それ以外の失敗は
標準エラーへ出力して失敗件数と終了ステータス 1 で通知する。
- Dockerfile: tmux1 / tmuxc を ~/.bashrc の alias ではなく /usr/local/bin の
symlink で提供する。alias は bash の対話シェルにしか効かず、zsh や
docker exec などの非対話実行では使えないため。
- CHANGELOG / docs も上記に合わせて更新。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3p9U34HsArSpxJ1E1SK94
🔧 /ndf:fix サマリ (round 3)対応件数: critical=0 / major=0 / minor=4 / nit=0 (合計 4 件、うち 1 件は codex/gemini の同一箇所重複指摘) 詳細
補足
検証
|
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 4 | codex | REQUEST_CHANGES
呼び出し元クライアントの識別を安全に行えるよう修正が必要です。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 4 | gemini | COMMENT
シェルスクリプトのパース処理における空白文字・メタ文字・globbing由来のエッジケースによる誤動作を防ぐため、より堅牢なパース方法への修正を提案しています。
レビュー round 4 の指摘対応。
- tmux-first: 実行元クライアントを「最終操作が SELF_FRESH(10) 秒以内」であることまで
確認できた場合のみ信頼し、確認できない場合は -f 付きでも切断せず切り替えのみ行う。
hook や send-keys 経由でキー入力を伴わずに起動された場合、従来は最終操作が最も新しい
だけの無関係なクライアントを実行元とみなし、実際の利用者の端末を切断していた。
client_name と client_activity は 1 回の display-message でまとめて取得し、
switch-client にも -c で同じクライアントを渡して判定と操作の食い違いをなくす。
- tmux-first / tmux-clean: `for e in $(tmux ...)` を while read + ヒアドキュメントに
置き換え、空白による単語分割とパス名展開 (glob) を排除。
- tmux-clean: `for s in $SESSIONS` も同様に while read へ。セッション名に空白が
含まれると別名として扱われ、削除対象を取り違えていた。
- tmux-clean: `-f "#{==:#{session_name},$s}"` はセッション名に `}` や `,` が含まれると
format 構文が壊れ、全セッションのアタッチ数を返して掃除が一切効かなくなるため、
`#{session_attached} #{session_name}` を awk で完全一致させる方式へ変更。
検証: sh -n / bash -n / dash -n、shellcheck (-s sh) クリーン。
実 tmux 3.7b (隔離ソケット) で、同一セッションへ 2 クライアント接続時の実行元保護、
別セッションからの ghost 切断 + 切り替え、非対話起動時の切断抑止、`we}ird` / `my proj`
/ `st*ar` / `com,ma` 各セッション名での keeper・アタッチ中・実行中判定を確認。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M3p9U34HsArSpxJ1E1SK94
🔧 /ndf:fix サマリ (round 4)対応件数: critical=0 / major=1 / minor=3 (合計 4 件) — 未 Resolve の指摘 4 件すべて対応 詳細
指摘 1 についてご提案の また 実在したリスクは「キー入力を伴わない起動」(hook / そこで 「実行元と断定するには最終操作が ご指摘外で併せて修正
検証
|
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 5 | codex | REQUEST_CHANGES
実行元クライアントを特定できない場合の操作対象と、セッション状態を取得できない場合の削除判定を安全側に修正してください。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 5 | gemini | APPROVE
指摘事項はありません。
- tmux-clean: list-sessions / list-panes の失敗を `|| true` で空一覧に丸めていたため、 アタッチ数も実行中コマンドも不明なまま「アイドル」と判定して既定実行で削除していた。 取得できなかった場合は削除せず keep する (-f 指定時のみ従来どおり削除)。 走査中にセッションが消えていた場合は has-session で切り分けて skip する。 - tmux-first: ME_VERIFIED=0 のとき $ME は「直近に操作された別クライアント」を指し得るのに、 切断を止めたあとで switch-client -c "$ME" によりその別クライアントを切り替えていた。 未検証時は $ME を捨て、切断だけでなく切り替えも行わず手動コマンドを案内する。 - CHANGELOG / docs/user/environment-variables.md の記述を挙動に合わせる。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M3p9U34HsArSpxJ1E1SK94
/ndf:fix round 5 対応サマリ修正コミット: round 5 の未 Resolve 指摘 2 件 (codex: REQUEST_CHANGES) をコードを読んで再判定し、いずれも major で妥当 と判断して修正しました。2 件とも round 4 (
1. tmux-clean を fail-closed 化
2. tmux-first の未検証クライアント操作を停止
検証
tmux-clean (
tmux-first (別 tmux サーバーの pane から本物のクライアントを
その他
|
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 6 | codex | APPROVE
修正が必要な指摘はありません。
takemi-ohama
left a comment
There was a problem hiding this comment.
🤖 cross-review | round 6 | gemini | APPROVE
設計・実装・ドキュメントにおいて修正を要する指摘事項はありません。
概要
VS Code のウィンドウが異常終了 (クラッシュ / ホスト再起動 / 接続断) すると、VS Code サーバー側に pty が取り残され、tmux クライアントだけがセッションへ繋がったまま残ります。統合ターミナルの起動スクリプトはそのセッションを「使用中」と判定するため、
<repo>-1に戻れず<repo>-2,<repo>-3… と新しいセッションが増え続けます。これに対処する 2 つのコマンドを base イメージへ追加します。
tmux-firsttmux1tmux-cleantmuxc関連 Issue
変更点
containers/base/Dockerfile:tmux-first/tmux-cleanを/usr/local/binへ配置し、tmux1/tmuxcエイリアスを~/.bashrcへ追加docs/user/environment-variables.md: 「tmux / screen 経由で使う場合」に対処手順を追記CHANGELOG.md:[Unreleased] / Addedに追記tmux-first
-数字を除去 > カレントディレクトリ名<ベース名>-<数字>を数値昇順に並べた最小のものswitch-client、tmux 外ならattach-sessioncarmo-system-console-2のようにハイフンを含む名前でも正しく分解します-10より-1が優先されます-nで切り替えずに対象と切断予定クライアントを確認できますtmux-clean
既定では安全側に倒し、次のセッションは残します。
-nで削除対象と除外理由を確認できます-fで上記も含めてすべて削除します動作確認
./bin/devbase --helpが正常に動作するdocs/,README.md) を更新した1. スクリプト単体(tmux セッションと居座りクライアントを再現)
本番と同じ形(
adminer-1に居座りクライアント、adminer-2に自分のクライアント)を作り、tmux-first→tmux-cleanの順に実行:あわせて次を確認:
carmo-system-console-3->carmo-system-console)-10より-1を優先)すでに <session> ですを表示tmux-cleanの除外判定(keeper / アタッチ中 / 実行中の 3 種すべて)と-fでの上書きsh -n/bash -nの構文チェック2. イメージへの組み込み(
devbase-base:latestを親に、本 PR の Dockerfile ブロックをそのまま実ビルド)補足
COPY --chmodとヒアドキュメントは BuildKit 専用ですが、既存のCOPY --chmod=755 entrypoint.shが同じ前提のため追加要件はありませんCOPYはUSERに関係なく root 権限で書き込むためUSERの切り替えは不要で、実行ユーザーはubuntuのままですtmux-cleanの実装上の注意として、tmux のdisplay-message -tは target-session ではなく target-pane を取り、また書式内のタブは_に置換されるため、アタッチ数の取得にはlist-sessions -fのフィルタを使っています🤖 Generated with Claude Code
https://claude.ai/code/session_017BpznFY4zi8kvY7hhHV7EK