ターミナル 1 枚に AI 開発コックピットを建てる — Claude / File / GitHub を tmux に畳んだ常駐環境
SSH でログインしたら、いつも同じ作業場にそのまま着地する——Claude Code を何本も並行で回しながら、ファイルも PR も同じ場所で行き来できる「常駐の開発コックピット」を tmux で組んだ記録。1 枚の tmux セッションに Claude(Agent View)/ File(lf ファイラー)/ GitHub(PR ブラウザ)の 3 ウィンドウを畳み、Shift+矢印で回す。再アタッチで最新化する自己修復、gh-dash をやめて gh + fzf の自前ブラウザに置き換えた判断、そして下段ステータスバーに要対応セッションを型別で光らせるクロスセッション通知まで、実際のスクリプトと公式仕様の裏取りつきで、一次情報として残します。
何をしたかったか — 繋いだ先に欲しいのは「常駐の作業場」
このリポジトリは Codespaces 上の Dev コンテナで回している。母艦には SSH でログインして 入る。繋ぐところまでは整っていた。けれど実際に手を動かし始めると、繋いだ先に欲しいものが 「素のシェル 1 枚」では足りないことがすぐ分かった。
行動原則の一つは「AI を使い倒す」だ。使い倒すとは、1 本の対話を丁寧に回すことではなく、
別々の関心事に Claude Code を何本も並行で走らせ、その合間にファイルを覗き、PR を捌く
ことだった。そのたびに claude・lf・gh を手で立ち上げ直し、どこで何をしていたかを
思い出す——この摩擦が、並行で回す旨みを削っていた。
だから狙いを一つに定めた。SSH ログインしたら、いつも同じ「作業場」にそのまま着地する。
Claude を回すところ、ファイルを見るところ、PR を捌くところが、最初から 1 枚のターミナルに
畳まれていて、行き来するだけでいい。永続セッションなので、切れて戻っても同じ配置が待っている。
このコックピットを lab-view.sh として組んだ。以下、その一つひとつを、代替案や実際の
つまずきと一緒に残す。
設計 — 1 セッション・3 ウィンドウ・矢印キーで回す
コックピットの骨格はシンプルだ。1 つの tmux セッションに 3 つのウィンドウを並べ、 ウィンドウ名をそのまま下段メニューの表記にする。
- Claude —
claude agents(Agent View)を全画面で常駐。ログイン直後はここに着地する。 - File — lf ファイラー。
- GitHub —
gh+ fzf の PR ブラウザ(gh-view.sh)。
この骨格を成り立たせるために、いくつか地味な判断を重ねた。
- 専用ソケットで隔離する。 既定の tmux サーバを使うと、汎用セッション名や prefix 無しの
設定が他の用途とぶつかる。コックピットは専用ソケット(
-L suzlab)に隔離し、その上で セッション名をlabにする。キー設定やステータスバーの変更が他の tmux へ漏れない。 - ウィンドウ名 = メニュー表記。 下段メニューはウィンドウ名(
#W)を index 順に並べる。 だから表示したい表記(Claude / File / GitHub)をそのままウィンドウ名に採り、Shift+矢印の 切替対象や自己修復の grep も同じ名前で回す。主役の Claude を最左(index 0)に作り、 ログイン直後のフォーカスをそこへ置く。 - 切替は Shift + ←/→。 「左右に移動」という直感に合い、修飾キー付きなので素の矢印を 奪わない(矢印は lf / Claude / fzf が中で使う)。Claude → File → GitHub の循環にした。
- セッションは無いときだけ作り、キー設定と GitHub ウィンドウは毎回当て直す。 セッション 本体を毎回作り直すと再接続で状態が飛ぶ。一方、キーバインドや GitHub ウィンドウの用意を 作成分岐の中だけに置くと、後から入れた fzf や設定変更が既存セッションに反映されない。 そこで本体は冪等に温存・付帯設定は毎回適用に分け、再アタッチのたびに最新化される ようにした(自己修復)。
Claude ウィンドウ — Agent View を常駐させる
主役のウィンドウは claude agents(Agent View)を全画面で開いたまま常駐させる。複数の
Claude Code セッションの一覧・切替はここが担う。ログイン直後にここへ着地するので、「開いたら
すぐ AI に投げられる」——同じ作業場に着地するという狙いが、そのまま作業場の入口になった。
Claude Code には Agent View / Agent Teams のような複数エージェントを束ねる native な導線が 育ちつつある(docs)。コックピットは、それを 1 枚のターミナルの中心に据えつつ、周りにファイルと PR の窓を足した恰好だ。
File ウィンドウ — lf に bat プレビューを噛ませる
ファイルを覗く窓は lf にした。既定で「左=ファイルリスト/右=プレビュー」の 2 列を出す (追加設定なし)。プレビューは bat でシンタックスハイライトと行番号つきにし、Enter で ファイル全体をページャ表示、← でファイラーに戻る、という往復にした。
ターミナルファイラーは ranger / yazi / nnn と選択肢が多い (2026 年の比較では、画像プレビューが素で効く・非同期でUIが固まらない yazi を新規に薦める 声もある)。それでも lf を採ったのは、 単一バイナリで依存が軽く、既定のまま 2 列プレビューが出て、Codespaces に置くのが楽 だったからだ。速さや多機能でなく、「この環境に薄く乗る」を優先した。
GitHub ウィンドウ — gh-dash をやめて gh + fzf にした
PR を捌く窓は、最初 gh-dash を使うつもりだった。GitHub CLI
拡張の TUI ダッシュボードで、repo 別セクション・Vim キー・YAML 設定を備えた成熟したツールだ
(〜12k stars)。ところが Codespaces で詰まった。既定の GITHUB_TOKEN では
branchProtectionRules を読む権限が無く、PR 一覧の取得がそこで固まる。
環境側のトークン権限に依存する不安定さを持ち込みたくなかったので、gh-dash はやめて、
gh pr list と gh pr view / gh pr diff(branchProtectionRules を要求しない)を叩いて
fzf で選ぶだけの薄い自前ブラウザ(gh-view.sh)に置き換えた。成熟した汎用ツールより、この
環境で確実に動く薄い自前を採る——ガードレールの監査で身につけた判断を、ここでも使った。
自前にしたぶん、細かい挙動も自分で決められる。gh-view の fzf は自分のイベント(起動・Ctrl-R・ 詳細/diff を閉じた後)でしか一覧を取り直さないので、tmux のウィンドウ切替で GitHub に戻っただけ では表示が古いまま残る。そこで GitHub ウィンドウへ遷移したら、その場で fzf に reload(Ctrl-R)を 送って最新化する tmux バインドを足した。条件を「window_name==GitHub かつ pane_current_command==fzf」に絞り、pager(less)や shell が前面のときには送らない。
下段の一機能 — 要対応セッションを型別で光らせる
3 ウィンドウを行き来していると、地味だが致命的な問題が出る。別ウィンドウにいるあいだ、 裏で止まった Claude セッションに気づけない。 Claude は権限確認や質問で頻繁に止まり、 こちらが入力を返すまで待つ。File や GitHub を見ているあいだ、それが放置される。
そこでコックピットの下段ステータスバーに、要対応セッション数を持続表示する一機能を足した。 先行事例(Marc Nuri は「5〜10 並行で手に 負えなくなる」と書き、人間が要るイベントだけ通知して alert fatigue を避けることを勧める)に 倣い、「要対応だけを、緊急度で型分けして出す」を原則にした。
| 型 | 意味 | いつ消えるべきか | 表示 |
|---|---|---|---|
| 入力待ち | あなたの入力を待って Claude が止まっている | 対応する(プロンプト送信)まで持続 | 赤・太字・点滅 |
| 完了 | 回答・結果が出そろって準備 OK | 開いて読めばよい=時間で自動失効(既定 10 分) | 緑・定常 |
状態を捕まえる入口は Claude Code の hooks だ。
フックには stdin で JSON が渡り、その中の session_id がセッションを一意に識別する。
Notification→入力待ち / Stop→完了 / UserPromptSubmit→クリア / SessionEnd→残骸掃除、の 4 つを
~/.claude-notify/<session_id>.<type> というマーカーファイルの作成・削除に落とす。表示側は
tmux の status-right で #(claude-notify-status.sh) を呼び、.wait / .done を型別に数えて
色付き文字列を返す(0 件なら空文字=時計だけ)。status-interval 5 の再描画が、点滅を数秒周期の
ちらつきとして見せる。
この一機能を作るなかで、公式仕様の裏取りが 2 つ効いた。
- フックは何があっても
exit 0で抜ける。 公式ドキュメントによればexit 2は ブロッキングエラーで、しかもブロックできるイベントにStopとUserPromptSubmitが 含まれる(NotificationとSessionEndは exit code が無視される)。つまり通知の都合で フックが非ゼロで落ちると、セッション本体を妨害しかねない。通知が主役の邪魔をしては 本末転倒なので、全経路exit 0を徹底した。作法ではなく仕様上の必然だ。 - 緑背景バッジ上の
blinkは背景ごと点滅する。 当初は完了も点滅させていたが、端末に よっては緑背景のステータスバー上でblinkを使うと文字だけでなくセルの背景まで点滅する。 SGR のblinkはセル単位の属性で、「背景据え置き・文字だけ点滅」を SGR だけで表す術が無い。 外部に裏の取れないこちらの手元の観察だが、だからこそ残す——完了は点滅をやめて定常にした。 結果、「点滅=要対応・定常=準備 OK」という素直な区別に落ち着いた。
なお同じ「どのセッションが待っているか」を、デスクトップ通知+ペインへジャンプで
解く先行事例や、claude agents --json を source of truthにして
fzf で並べる例もある。コックピットが選んだのは割り込まない・依存を足さない・視界の端に
常駐する「下段バッジ」だ。前面を奪わず、作業中の集中を切らさずに「今 2 つ待っている」が分かる。
つまずき
- 待機中に閉じたセッションの
.waitが残り続ける。 入力待ちのまま閉じると、素朴な実装では マーカーが安全弁の掃除まで残り「入力待ちでないのに ● 入力待ち が出続ける」。SessionEndで 入力待ちの残骸だけを消して塞いだ(完了表示は残す)。 session_idが取れないとき固定名に集約してはいけない。unknownのような 1 名に寄せると 複数セッションが 1 件に衝突してカウントが狂う。誤通知を避けて no-op にした。- GitHub ウィンドウが古いまま。 他ウィンドウから戻ると fzf の一覧が更新されない → 遷移時に reload を送って最新化(前述)。
- 新規作成時に GitHub に着地してしまう。
new-windowは既定でフォーカスを奪うので、GitHub を-dで裏に用意し、ログイン直後のフォーカスを Claude に残した。
検証
- ログイン → 着地。 SSH ログインで tmux セッション(
lab)に自動アタッチし、Claude Agent View に着地。Shift+←/→ で Claude → File → GitHub を循環できること。 - 自己修復。 再アタッチ時に GitHub ウィンドウ・キーバインド・fzf 最新化が効くこと。gh / fzf / 認証が揃わない環境では GitHub ウィンドウを作らず素直に落ちること。
- 通知の往復。 Claude を止める → 下段に
● 入力待ち 1(赤点滅)→ プロンプト送信で消える。 応答完了 →✓ 完了 1(緑定常)→ 10 分放置で自動失効。2 本止めれば● 入力待ち 2。別ウィンドウに いても下段に出続けること。 - 触れたのは devcontainer 側のスクリプトと
.claude/settings.jsonなので、アプリのビルドには 影響しない。リポジトリ全体でpnpm typecheck/pnpm lint/pnpm format:checkが通ること。
学び
- 作業場は「配置」で決まる。 速いエディタや多機能なファイラーより、「開いたらいつも同じ 3 枚が 同じ場所にある」ことのほうが、並行で回す摩擦をずっと減らした。いつも同じ作業場に着地するという 狙いは、結局この地味な定位置化で達成された。
- 成熟した汎用より、この環境で確実に動く薄い自前。 gh-dash を落として gh + fzf にしたのも、 lf を選んだのも、通知を割り込まない常時バッジにしたのも、根は同じだ。SSH で入る Codespaces という制約から逆算すると、選択は自ずと絞れる。
- 端末の制約が、設計をより素直にすることがある。 完了も点滅させたかったが、緑背景の都合で 定常にした。結果、最初の型分けより分かりやすい表現に落ち着いた。制約は必ずしも妥協ではない。
- 裏取りは「なんとなくの安全策」を「必然」に変える。 フックの
exit 0は、公式仕様を当たって 初めて「Stop/UserPromptSubmitを守るための必然」だと分かった。一次情報として書くと決めると、 自分の実装の理由まで確かめ直すことになる。




