Ghostty + herdr で組む開発環境(2026年9月版)
半年前、Claude Code 開発を支える私のツールチェイン という記事で WezTerm + tmux + 自作ツール Baton の構成を紹介した。あれから中身がだいぶ入れ替わったので、現時点の開発環境をまとめ直す。
入れ替えたのはターミナルエミュレーター・マルチプレクサ・AI セッション管理の 3 つで、エディタやバージョン管理まわりはほぼそのままだ。この記事ではまず「今どういう構成で、どう開発しているか」を書き、そのあとに「なぜこの形になったか」を短く添える。想定している読者は、ターミナル中心で開発したい人と、Claude Code を複数並べて動かしている人だ。
結論から
- 常用の構成は Ghostty(ターミナルエミュレーター)+ herdr(ターミナルマルチプレクサ) にした。WezTerm + tmux + Baton は捨てずに、戻るための退路として残している
- herdr を選んだ決め手は、tmux 時代にできなかった「Neovim での画像表示」と「ターミナル内ブラウザで画面の要素を選んで AI に渡すこと」ができるようになったことだ。承認待ちの検知や使い心地は、Baton とほとんど変わらない
- Ghostty に移ったのは、そのターミナル内ブラウザが WezTerm では重くて使い物にならず、Ghostty では普通に動いたから
全体の構成を 1 枚にするとこうなる。

半年前から変わったところ
変わったのは下の 3 層だけで、そのほかは据え置きだ。

| 層 | 2026年3月 | 2026年9月 |
|---|---|---|
| ターミナルエミュレーター | WezTerm | Ghostty(WezTerm は退路として残す) |
| マルチプレクサ | tmux | herdr |
| AI セッション管理 | Baton(自作の Go 製 TUI) | herdr のサイドバー + 自作スクリプト(Baton は休止) |
| ブラウザ連携 | なし | terminal-browser(ターミナル内の Chromium) |
| Neovim の画像表示 | tmux の中では実用にならない | 使える |
| エディタ / Git / worktree | Neovim / lazygit / gtr | 変わらず(hunk を試用中) |
| 環境管理 | dotfiles | dotfiles + Nix(nix-darwin + home-manager) |
Nix 化は 8 月の話で、経緯は dotfiles 運用のすすめと、その先の Nix に書いた。この記事の範囲は 9 月の入れ替えである。
Ghostty + herdr でどう開発しているか — 1 タスクの流れ
道具の一覧より先に、1 つのタスクをどう進めているかを書く。私の場合、常時 10 本前後のエージェントを並べて動かしている。

- プロジェクトを開く。 Ghostty を起動すると自動で herdr にアタッチし、前回の作業がそのまま残っている。新しく始めるときは
prefix+fで ghq 管理下のリポジトリを fzf で選び、herdr のワークスペースとして開く。Alfred から開くこともある - worktree を切る。 並列で進めるタスクごとに
wt(gtr のラッパー)で worktree を作り、同じピッカーから開く - 分割してエージェントを起動する。
prefix+2〜prefix+8で N 分割し、prefix+0でペイン数に応じて Neovim・Claude Code・Codex を起動する。全ペインで Claude Code を一斉に立ち上げたいときは、Loupedeck(物理ボタンの左手デバイス)の 1 ボタンで済ませる - 状態を見て承認する。 各ペインが working / blocked / idle のどれかを、herdr がサイドバーに出す。承認待ちが出たら
prefix+bの承認コンソールでa(承認)かd(拒否)を押すか、通知をクリックしてそのペインへ飛ぶ - (Web アプリのとき)ブラウザで確認して、要素ごと直させる。
tbで localhost を隣のペインに開き、気になる要素をCtrl+Gで選ぶ。選んだ要素の情報がエージェントの入力欄に入るので、そのあとに「ここの余白を詰めて」と書いて送る - 差分を読む。 エージェントが書いた変更は hunk でまとめて読む。気になるところは Neovim で開いて直接確認する
- コミットする。 基本はエージェントに commit スキルを実行させ、変更内容からコミットを作らせる。手で直したファイルは
prefix+gで開く lazygit でステージし、Ctrl+Gで Antigravity にコミットメッセージを作らせてそのままコミットする

4〜6 を何周も回すのが日常だ。5 は Web アプリを作っているときだけの手順で、それ以外の開発ではターミナル内ブラウザは使わない。
Web アプリの開発でいちばん変わったのは、その 5 である。以前は、ブラウザ(Dia)用に自作した拡張で要素の情報を拾い、ターミナルに戻って AI に渡していた。今はブラウザを切り替えず、Ctrl+G で要素を指すだけで済む。


※ 開発中のプロダクトは見せられないので、画面はサンプルとして Ghostty の公式ドキュメントを開いている。普段は localhost で動かしている手元のアプリに対して同じ操作をする。
レイヤー別のツール紹介
ここからは図 1 の層ごとに、使っているものを紹介する。
ターミナルエミュレーター: Ghostty
Ghostty は Zig 製の、GPU で描画するターミナルエミュレーターだ。公式ドキュメントに「Zero Configuration Philosophy」という節があり、設定なしでもほとんどの人がそのまま使えることを目標にしている(Ghostty Configuration)。
私の設定は本体 55 行と herdr 用のキー設定 51 行で、合わせて 106 行だ。WezTerm のときは Lua で 7 ファイル 388 行あった。ただ、減った分の大半は Ghostty の手柄ではない。タブや分割の管理を herdr に移したので、ターミナルエミュレーター側の仕事がそもそも減った。
Ghostty 側でやっていることは、ほぼ 1 つしかない。Cmd 系のキーを「herdr の prefix + キー」のバイト列に変換して送ることだ。Ghostty には Lua のようなスクリプトがないので、text: アクションでバイト列を直接書く。
# config/ghostty/herdr.conf(抜粋)
# Cmd 系のキーを「herdr の prefix(Ctrl+Q = \x11)+ キー」のバイト列で送る
# Cmd+D でペインを縦に分割、Cmd+T で新規タブ、Cmd+W でペインを閉じる
keybind = cmd+d=text:\x11v
keybind = cmd+t=text:\x11c
keybind = cmd+w=text:\x11x
# Cmd+1..9 を herdr のタブ切替(Alt+1..9)に回す。
# Ghostty 既定の super+digit_N=goto_tab が先にマッチするので、先に外しておく
keybind = cmd+digit_1=unbind
# Alt+1 は ESC 前置ではなく CSI u 形式で送る(herdr が kitty keyboard protocol を使うため)
keybind = cmd+1=text:\x1b[49;3u
ハマったのは Cmd+数字だ。cmd+1 を上書きしても、Ghostty 既定の物理キー指定(super+digit_1)が先にマッチしてしまう。cmd+digit_N=unbind で既定を外してから割り当てる必要があった。Alt の送り方も、よく見る ESC 前置(\x1b1)ではなく CSI u 形式(\x1b[49;3u)にしている。herdr が外側のターミナルエミュレーターに kitty keyboard protocol を要求するためだ。
見た目では、WezTerm で使っていた背景画像を引き継いだ。Ghostty には背景画像の明るさを下げる設定がないので、画像そのものを事前に暗く加工して敷いている。
マルチプレクサ: herdr
herdr は AI エージェントを動かす前提で作られたターミナルマルチプレクサで、Rust 製の単一バイナリだ。tmux と同じ層にあり、tmux の代わりに使う。
tmux から移って違いを感じたのは次の点である。
- 各ペインのエージェントの状態が見える。 Claude Code や Codex を自動で検出し、working / blocked / idle をサイドバーに出す
- マウスも普通に使える。 README に「keyboard and mouse, both first-class」とあり、prefix キーとクリック・ドラッグのどちらでも操作できる
- エージェントから操作できる。 CLI とソケット API があり、ペインの分割やキー送信をスクリプトから叩ける
キーバインドは tmux 時代のものをそのまま移植した。prefix も tmux と同じ Ctrl+Q にしている(herdr 既定の Ctrl+B は、Karabiner で左矢印に変換しているので届かない)。
# config/herdr/config.toml(抜粋)
[keys]
# prefix は tmux 時代と同じ Ctrl+Q
prefix = "ctrl+q"
# lazygit をポップアップで開く
[[keys.command]]
key = "prefix+g"
type = "popup"
command = "zsh -ic 'lazygit'"
width = "80%"
height = "80%"
herdr 単体で足りない部分は、自作スクリプト 10 本で埋めた。主なものは、承認待ちのエージェントを一覧して a / d で承認・拒否できる承認コンソール(Baton の TUI の代わり)、ghq のリポジトリピッカー、N 分割、ペイン数に応じたプロセスの一括起動である。スクリプトはすべて dotfiles の config/herdr/bin/ に置いている。
プラグインはタブ名を自動で付ける herdr-automatic-rename と、OS の通知バナーをクリックすると該当ペインへ飛べる herdr-terminal-notifier の 2 つを入れている。プラグインは herdr plugin install で入れる仕組みなのでリポジトリで管理しにくい。そこで、使うプラグインと commit SHA を書いたリストを dotfiles に置き、Nix の switch のたびにそのリストへ揃えるようにした。導入前にはスクリプトと同梱アプリの中身を確認し、commit を固定している。
細かいところでは、サイドバーのエージェント名を Claude はオレンジ、Codex は青緑というように色分けした。10 本並ぶと、色がないとどれが何か分からない。
AI エージェント: Claude Code / Codex / takt / OpenCode / Antigravity
エージェントは Claude Code と Codex を主に使い、手順が決まった作業は nrslib さんの takt に流している。takt はステップごとにプロバイダを割り当てられるので、実装とテストは Codex、計画とレビューは Claude Code、AI 特有のアンチパターン検出だけは OpenCode、という分担にしている。
承認まわりは半年で様子が変わった。Baton を作った当初は、Claude Code にも Codex にも、承認を自動で判定する仕組みがなかった。すべてを許可するモード(--dangerously-skip-permissions)で起動すれば承認は出なくなるが、それは怖い。そこで Baton に「safe auto mode」を実装した。決まったルールで rm や本番操作のような危険な操作を弾き、判断に迷うものだけ別の LLM に審査させる仕組みだ。その後 Claude Code に auto mode が入り、Claude Code にはあるのに Codex にはない時期も Baton で補っていた。今は Codex にも承認リクエストを自動でレビューする設定があり、どちらも組み込みの機能で足りるようになった。
このほかに OpenCode と Antigravity(agy)も入れている。OpenCode は takt のレビュー工程の一部を担当し、Antigravity は調べものの委譲と、後述する lazygit のコミットメッセージ生成に使っている。herdr はどちらも検出してサイドバーに出すので、Claude Code や Codex と同じ一覧で状態を追える。
開発ツール
Neovim。 全部入りのディストリビューションは使わず、プラグインマネージャーの lazy.nvim で 1 つずつ足してきた。今は 29 ファイルで、選んだ理由は ADR としてリポジトリに残している。herdr に移って、snacks.nvim の画像表示が使えるようになった。Ghostty では Markdown の画像リンクのすぐ下に画像が埋め込まれるので、記事を書きながら図の見た目を確かめられる。WezTerm のときは埋め込みに対応しておらず、カーソルを乗せたときのフロート表示までだった。

実は 9/15 に一度、この画像表示を諦めている。tmux の中では画像の残像や位置ずれが出たので、画像表示を切り、パスをコピーして Alfred で開く運用にしていた。herdr に移ってから有効に戻したところ、残像も位置ずれも出なかった。一度諦めた機能だったので、これはかなり嬉しかった。
lazygit。 手で直したファイルをコミットするときに使う。herdr の prefix+g でポップアップとして開くので、作業中のペインを崩さない。ステージしてから Ctrl+G を押すと、Antigravity が差分から Conventional Commits 形式のメッセージを作り、そのままコミットする(lazygit の customCommands に登録している)。エージェントが書いた変更は、エージェント自身に commit スキルを実行させてコミットすることが多い。
hunk。 hunk は、エージェントが書いた変更をまとめて読むための diff ビューアで、「review-first」をうたっている。今回の移行と同じタイミングで試し始めた。1 行ずつステージするなら lazygit、エージェントが複数ファイルにまたがって書いた変更の全体を読むなら hunk、という分担を考えている。
gtr。 Git worktree の作成と削除をまとめるツールで、wt というラッパーでブランチ名も自動で付けている。worktree を作ったあとにポートの割り当てや依存のインストールを流す処理も、gtr の hook に書いてある。
terminal-browser。 terminal-browser は、Electron で本物の Chromium を動かし、その画面を Kitty graphics protocol でペインに描くブラウザである。localhost で動かしている手元のプロダクトも、普通の Web サイトも、実ブラウザと同じ見た目で表示される。起動は短いシェル関数にしている。
# tb → localhost:3000 を右に分割して開く
# tb 5173 → localhost:5173
# tb ... down → 2 つ目の引数で分割方向
tb() {
local target="${1:-localhost:3000}"
local dir="${2:-right}"
[[ "$target" == <-> ]] && target="localhost:$target"
terminal-browser open "$target" --split "$dir"
}
要素を選んで AI に渡す Ctrl+G は terminal-browser の組み込み機能だ。要素ピッカー(react-grab)が起動し、選んだ要素の HTML の抜粋が、同じタブにあるエージェントの入力欄に > ... の 1 行で入る。React の開発ビルドなら、コンポーネント名とソースの位置も付く。Enter は押されないので、続けて指示を書いてから送る。逆に AI 側からは terminal-browser action で、開いているタブのスクリーンショット・DOM の取得・クリックなどができる。
OS 操作: AeroSpace / Alfred / Karabiner / Loupedeck
ターミナルの外側は、キーボードから手を離さずに済むことを基準に選んでいる。
- AeroSpace: タイル型のウィンドウマネージャー。Ghostty は決まったワークスペースに固定で置いている
- Alfred: ランチャー。リポジトリを herdr のワークスペースとして Ghostty で開くワークフローや、Taskfile のタスクを実行するワークフローを自作している
- Karabiner-Elements: キーの変換
- Loupedeck: 物理ボタンの左手デバイス。全ペインで Claude Code を起動する、全ペインで
/clearを実行する、といった一括操作を割り当てている
基盤: Nix + dotfiles
ここまでのツールは、すべて dotfiles リポジトリで管理し、nix-darwin + home-manager で配っている。herdr・Ghostty・terminal-browser も Homebrew の宣言に入れてある。宣言にないパッケージは switch のたびに消える設定(cleanup = "zap")にしているので、試しに入れたツールを片付け忘れることがない。
設定を変えるときは、判断の理由を ADR としてリポジトリに残すようにしている。今回の移行でも、試用の開始から採用の確定、ターミナル内ブラウザの経路の選択まで、2 本の ADR を書いた。この記事の数字や経緯も、ほぼその ADR から拾っている。
なぜこの構成になったか
ここからは移行の経緯を短く書く。流れを図にするとこうなる。

きっかけは Orca の「要素を選んで AI に渡す」機能
9 月の頭、AI エージェント向けの統合開発環境として話題になっていた Orca を検討した。いちばん惹かれたのは、内蔵ブラウザで画面の要素を選び、コメントを付けてそのまま AI に渡せる機能だった。
ただ、Orca 自体は私の使い方に合わなかった。GUI 前提のツールで、私の環境は逆に GUI を減らしてターミナルに寄せる方向に進んでいる。Orca の売りである worktree 管理は gtr で、GitHub 連携は gh で足りていたし、複数エージェントの管理は当時 Baton で賄えていた。
それでも「要素を選んで AI に渡す」だけは欲しかった。同じことをターミナルの中でできないか。そう考えたのが、今回の入れ替えの出発点である。
herdr: 画像とブラウザが動いたのが決め手
herdr は、実は 9 月の頭に一度見送っている。当時は承認待ちの検知精度をいちばん重く見ていて、その観点では Baton と herdr に差がなかった。どちらも画面の文字を読んで判定する方式である。一方で移行の手間は重かった。あとで数えたところ、tmux のキーバインドが 89 個、自作スクリプトが 26 本、Baton の Go コードが 7,639 行あった。差がないなら移る理由はない、と判断した。
考えが変わったのは、開発環境全体を見直したときだ。Claude Code や Codex に自動承認の仕組みが組み込まれて、Baton の safe auto mode の出番が減っていた。自作ツールを直し続ける手間と、コミュニティが活発な herdr に乗る場合を比べると、後者のほうが先がある。そこで、机上で比べるのをやめて実際に使ってみることにした。
使ってみた結果、エージェント管理の使い心地は正直ほとんど変わらなかった。変わったのは別のところだ。tmux は Kitty graphics protocol に対応していない。そのため、ターミナルに画像を描く系統の機能が tmux の中では思うように動かなかった。herdr ではこれが動き、Neovim の画像表示とターミナル内ブラウザの両方が使えるようになった。takt を動かすと画面がちらつく問題も、tmux では設定で抑え込んでいたが、herdr では最初から出なかった。
決め手はここだった。やりたいことがあって、それを叶えてくれたのが herdr だった。tmux のままでも解決する道はあったのかもしれない。ただ、流行っているエージェント管理ツールを試したら、副産物として長く諦めていたことが解決した。それなら herdr でいい、となった。
移るときは、いきなり切り替えずに試用から始めた。最初は ~/.config/use-herdr というファイルがあるかないかで、WezTerm が tmux と herdr のどちらを起動するかを切り替える形にした。戻すときは rm 1 回で済む。この状態で tmux + Baton と使い比べてから、採用を決めた。今も WezTerm を起動すれば tmux + Baton の環境に戻れるようにしてある。
Baton でやっていたことは、次のように置き換えた。
| Baton / tmux でやっていたこと | herdr での置き換え |
|---|---|
| セッション一覧と状態表示 | サイドバーの working / blocked / idle 表示 |
| TUI から承認・拒否 | 自作の承認コンソール(prefix+b → a / d) |
| 承認待ちのペインへジャンプ | 通知のクリック、または承認待ち優先の並び + prefix+alt+1 |
| ghq のプロジェクトピッカー | 自作の herdr-sessionizer(prefix+f) |
| N 分割・ペインの初期化 | 自作の herdr-split-layout / herdr-init-panes |
| tmux のキーバインド | 同じ prefix(Ctrl+Q)で移植 |
| safe auto mode | 置き換えなし(各エージェントの自動承認に任せる) |
| takt のセッション検知 | 置き換えなし(Claude Code として一覧に出るので実害は小さい) |
Ghostty: ターミナル内ブラウザが WezTerm では重すぎた
herdr に移って最初に試したターミナル内ブラウザは Chawan だった。独自エンジンで軽く、Wikipedia のような文書中心のページは問題なく読める。ところが私のプロダクト(Next.js)を開くと、アイコンがすべて [img] という文字に置き換わった。アイコンが HTML に直接埋め込まれた <svg> で、Chawan はこれを描けない。モダンな Web アプリの確認には使えないので、入れて 10 分足らずで外した。
次に試したのが terminal-browser だ。こちらは中身が本物の Chromium なので、表示は実ブラウザと変わらない。ただ、WezTerm + herdr の組み合わせでは、スクロールするだけで WezTerm が CPU を 1 コア以上使い続け、操作がまともに追いつかなかった。同じことを Ghostty + herdr で試すと、CPU 使用率は十数%に収まった。普通のブラウザと同じ感覚でスクロールでき、一気に快適になった。
原因は、terminal-browser が画面を転送する経路にあった。terminal-browser は herdr を通してターミナルエミュレーターへ画像を直接流す専用の経路(ストリーム)を開くが、WezTerm ではこれが約 3 秒で閉じてしまい、流量制限のない予備の経路に落ちていた。なぜ閉じるのかまでは突き止めていない。
もう 1 つ、Ghostty にしてよかったことがある。iPhone(Moshi)から herdr のセッションにアタッチしたあと PC に戻ると、WezTerm ではペインが実色で塗りつぶされ、背景画像が隠れてしまう現象があった。Ghostty にしてからは、今のところこれが起きていない。こちらも原因は切り分けていない。
ついでに書くと、起動直後のメモリは WezTerm の約 316MB に対して Ghostty は約 213MB だった。ただ、普段のテキスト表示の速さはほぼ変わらない。Ghostty に移って得たのは、ターミナル内ブラウザが使えるようになったことだ。
失ったもの・割り切ったこと
使い心地はほぼ変わらないと書いたが、herdr に移って失ったものもある。いちばん大きいのは見た目の自由度だ。tmux では好きに組めたステータス表示が、herdr では決まった枠の中でしかいじれない。サイドバーの列を揃える設定も、モード別に色を変える設定もない。
ほかにも、次のような制約を抱えたまま使っている。
- ペインを ID で直接フォーカスできない。 tmux 時代のペイン番号ジャンプは再現できなかった
- ターミナル内のリンクをクリックで開けない。 herdr がマウスを捕まえているためで、マウス捕捉を切るとサイドバーのクリックやドラッグでのコピーも失う
- safe auto mode と takt の検知がない。 どちらも Baton にしかない機能だった
- terminal-browser は herdr のクライアントが 1 つのときしか軽く動かない。 iPhone から同じセッションに繋ぐと、Ghostty でも重い経路に落ちる。開き直すまで戻らない
- 通知が二重に出る。 Claude Code / Codex の hook の通知と herdr の通知が重なっている。まだ直していない
- Ghostty のメモリは観測中。 Claude Code 由来のメモリリークの報告があったため、移行後 1 週間は 10 分おきに記録している。今のところ 200MB 前後で推移していて、増え続ける様子はない
Baton は休止にした。WezTerm を起動すると従来どおり tmux + Baton で立ち上がるようにしてあり、いつでも戻れる。takt のセッション検知のように Baton にしかない機能もあるので、戻る可能性はゼロではない。ただ、takt のセッションも herdr のエージェント一覧には Claude Code として出てくるので、今のところ困っていない。たぶん戻らないと思う。
ターミナル中心の環境を作る人へ
前提として、開発の軸をターミナルに置くところから始めてほしい。そのうえで大事だと思うのは、一度に全部を入れないことだ。
私は Neovim を組み始めたとき、プラグインを次々に入れていた。Neovim には LazyVim のような全部入りのディストリビューションと、lazy.nvim のようなプラグインマネージャーで 1 つずつ足していくやり方がある(名前が似ていてややこしい)。全部入りで始めると、自分の環境に何の機能が入っているのかを把握しきれなくなる。私は 1 つずつ入れて使い心地を確かめ、目的を 1 つずつ潰していくやり方のほうが合っていた。
設定が面倒な人には、Ghostty と herdr から始めるのがいいと思う。Ghostty は設定なしでまともに動くことを設計思想にしている。herdr も、tmux の取っつきにくさを減らす方向で作られている。キーボードとマウスの両方を正式な操作方法として扱っているので、prefix キーを覚える前からクリックとドラッグで分割や移動ができる。
そこから先は、使いながら育てればいい。やりたいことが出てきたとき、他の人の環境紹介を読んで「この機能いいな」と思ったときに、その考え方を 1 つずつ取り込む。私の今の構成も、Orca の機能に惹かれたところから始まっている。
まとめ
半年前の記事と比べると、入れ替えたのはターミナルエミュレーター・マルチプレクサ・セッション管理の 3 つだけだ。エージェントを並べて承認を回す、という開発のやり方もほとんど変わっていない。
それでも、tmux 時代に諦めていたことが 2 つ戻ってきた。Neovim で画像を見ることと、ブラウザで要素を指して AI に直させることだ。環境を入れ替える理由として、私にはこれで十分だった。
使い込むうちに、また不満や入れ替えたいものが出てくると思う。そのときはまた ADR に書き足していく。