ちなみに

火曜日の空は僕を押しつぶした。

zellijからtmuxに戻した

去年の3月から zellij をターミナルマルチプレクサとして使っていたけど、約11ヶ月で結局 tmux に戻した。

zellijはRust製で設定がKDLで書けて、UIもモダンでめちゃくちゃ良かった。locked modeをデフォルトにして Ctrl+g でモード切替するスタイルが気に入っていた。floating paneの完成度はtmuxより進んでいたし、Wasmベースのプラグインシステムにも将来性を感じていた。

ただ、細かく改善しようとするとtmuxのほうが圧倒的に小回りが利く。戻した理由は大きく2つある。

1つ目はzellijの開発ペースへの不安。v0.42.2からv0.43.0まで4ヶ月リリースがなかったし、v0.43.1(2025年8月)以降もまた止まっている。メインのターミナル環境を更新の不透明なツールに預けるのはちょっと怖かった。

2つ目は Claude Code との相性。Claude Codeはターミナルで動くAIエージェントなんだけど、tmuxのpane操作やsession管理と組み合わせたい場面がめちゃくちゃ多い。tmuxなら tmux list-panestmux send-keys で外部からセッションを操作できる。zellijはCLIからの制御がまだ弱かった(し、スクリプタビリティ全般がtmuxに比べると足りなかった)。

そしてこのClaude Codeとの相性というのは、単にtmuxを操る側だけの話ではない。tmux configを書く側もClaude Codeに任せられる。実際、今回のリライトはほとんどClaude Codeにやってもらった。「こういう動きにしたい」と伝えるだけでconfigとシェルスクリプトが出てくる。ゼロコンフィグで使いやすいツールという考え方は少し古くなったのかもしれない。細かく設定できるツールをAIで好みに合わせて仕上げる、というのが今のやり方になりつつある。

おすすめの設定

折角なので気に入っている設定を紹介しておく。

Alt+矢印でpane移動して、端まで行ったら隣のwindowへ飛ぶようにしている。zellijの MoveFocusOrTab と同じ動きで、これがないと戻れなかった。

bind -n M-Left run-shell 'cur="#{pane_id}"; tmux select-pane -L; [ "$cur" = "$(tmux display -p "##{pane_id}")" ] && tmux select-window -t :-; true'
bind -n M-Right run-shell 'cur="#{pane_id}"; tmux select-pane -R; [ "$cur" = "$(tmux display -p "##{pane_id}")" ] && tmux select-window -t :+; true'

Alt+c でClaude Codeが動いているpaneにトグルする。どのsessionにいても飛べる。

bind -n M-c run-shell '~/.config/tmux/toggle-claude-pane.sh "#{pane_id}"'

toggle-claude-pane.sh の中身はこんな感じ。今いるpaneがClaude Codeなら前のpaneに戻り、そうでなければ全sessionからClaude Codeのpaneを探して飛ぶ。

#!/bin/bash
cur="$1"
cur_pid=$(tmux display -t "$cur" -p '#{pane_pid}')
if ps -eo ppid,comm | awk -v p="$cur_pid" '$1 == p && $2 == "claude"' | grep -q .; then
  tmux last-window 2>/dev/null || tmux last-pane 2>/dev/null
  exit 0
fi
target=""
while read -r id pid; do
  [ "$id" = "$cur" ] && continue
  if ps -eo ppid,comm | awk -v p="$pid" '$1 == p && $2 == "claude"' | grep -q .; then
    target="$id"; break
  fi
done < <(tmux list-panes -a -F '#{pane_id} #{pane_pid}')
if [ -n "$target" ]; then
  cur_session=$(tmux display -p '#{session_name}')
  target_session=$(tmux display -t "$target" -p '#{session_name}')
  [ "$cur_session" != "$target_session" ] && tmux switch-client -t "$target_session"
  tmux select-window -t "$target"
  tmux select-pane -t "$target"
else
  tmux display "No Claude pane found"
fi

yazi をpopupで開いて、閉じたときにcwdを呼び出し元のpaneに反映する。

bind y display-popup -E -w 90% -h 90% -d "#{pane_current_path}" \
  "tmux new-session '~/.config/tmux/yazi-popup.sh #{pane_id}' \\; set status off \\; set detach-on-destroy on"

yazi-popup.sh はこれだけ。yaziが終了時に書き出すcwdを send-keys で元のpaneに送る。

#!/bin/sh
pane_id="$1"
tmp=$(mktemp -t yazi-cwd.XXXXXX)
yazi --cwd-file="$tmp"
if cwd=$(cat -- "$tmp") && [ -n "$cwd" ] && [ "$cwd" != "$PWD" ]; then
  tmux send-keys -t "$pane_id" "cd \"$cwd\"" Enter
fi
rm -f -- "$tmp"

pane分割時に自動で均等配置にするhook。手動で select-layout -E しなくてよくなる。

set-hook -g after-split-window "select-layout -E"
set-hook -g pane-exited "select-layout -E"

tmuxを使い込んでいるみなさんもぜひ自分のおすすめ設定を教えてください。