Claude Codeのエージェントとメモと次の指示を1画面で管理するTUIを作った

Claude Codeでエージェントを複数動かすようになると、すぐにある不便にぶつかります。やりかけのメモはエディタ、次に投げたい指示は頭の中かどこかのメモ帳、そして各エージェントの状態はターミナルのあちこち。情報が画面の方々に散らばり、視線とウィンドウの移動だけで消耗していきます。

弊社では以前、複数エージェントの状態を1枚で見るためのダッシュボードを作りました。ただ、見えるようにはなっても「メモを取る」「次の指示を用意する」といった操作は別の場所に残ったままでした。今回はそこを1画面に畳み込むために、ターミナル多重化ツールのTUIをフォークして手を入れた記録です。


なぜ1画面に集約したかったのか

前回作ったダッシュボードは「今、誰が何をしているか」を見るためのものでした。とくに許可待ちで止まったエージェントが見えるようになり、人間がボトルネックになる時間は減りました。可視化としては効いたと感じています。

ただ運用を続けると、見えたあとの行動が画面の外にあることに気づきます。状態を確認したあと実際にやりたいのは、メモを取る、次に投げる指示を用意する、別のエージェントに切り替える、といった操作です。これらが別ウィンドウや別アプリに散っていると、結局そこで視線が飛びます。

とくに困ったのが「次に投げる指示」の置き場所でした。あるエージェントが作業している最中に次の指示を思いついても、それを止めずに書き留めておく場所がありません。付箋やメモ帳に逃がすと、今度はそのメモ自体が散らばっていきます。

欲しかったのは、次の3つが常に同じ画面の同じ場所にあることでした。

  • 消えないメモ
  • エージェントごとの「次に投げる指示」の待ち行列
  • エージェントの一覧と状態

Claude Codeマルチエージェントのリアルタイム監視ダッシュボードを作った

今回の前段にあたる、状態の可視化に絞った記事です

/blog/claude-code-agent-dashboard/


herdrをフォークした理由

自前でゼロから作る選択肢もありました。ですがターミナルの多重化、つまりworkspaceやtab、pane、そしてそれぞれの疑似端末(PTY)を管理する部分は、まじめに作ると車輪の再発明になります。

幸い、workspaceとtabとpaneの構造を持ち、エージェントの状態検知まで備えたTUIにherdrがあります。これをフォークして「全workspace共通の常駐pane 1枚」を足すほうが、目的に対して圧倒的に近道だと判断しました。

方針はシンプルです。本体の改造は最小限にとどめ、画面の右側に全workspace共通の常駐カラムを1本だけ足す。そこにメモと次プロンプトのキュー、そしてエージェント一覧を集約する、という形です。

着手前に、フォーク元の実装で接続点を確認しました。状態変化が必ず通る単一の経路があること、PTYへ文字を送る口があること、レイアウトが矩形の集合で表現されていて列を1本差し込めること。この3点が揃っていたので、常駐カラムは無理なく差し込めると見込みました。

なお herdr は AGPL-3.0-or-later と商用のデュアルライセンスで公開されています。今回の改造はこのライセンスの範囲での利用です。

ogulcancelik/herdr

フォーク元のターミナル多重化TUI(AGPL-3.0-or-later / 商用のデュアルライセンス)

https://github.com/ogulcancelik/herdr


設計:メモと次プロンプトとエージェントを右側に集約

常駐カラムは上下に分けました。上が常駐メモ、下が次プロンプトのキューです。比率はメモ寄りにして、メモ対キューを3対2にしています。文章を書くメモのほうに高さが要ると感じたためです。

メモは1つのエディタをworkspace横断で常駐させています。どのプロジェクトを開いていても、同じメモが同じ場所に見えます。プロジェクトをまたいだ「あとでやる」「この前の判断」を1か所に貯められるようにしたかったからです。

キューは、エージェントごとに「次に投げる指示」を積んでおく待ち行列です。エージェントが手すきになったタイミングで手動で送る運用を基本にしつつ、手すきを検知して自動で送るトグルも用意しました。自動送信は誤爆が怖いので、画面検知で手すきが確定してから送る作りにしています。

ここで言う「次プロンプト」は、エージェントに次に投げる指示の下書きです。今の作業を止めずに書き溜めておき、頭の中ではなくキューに逃がすことを狙っています。

エージェントの一覧と状態も同じ列に置きました。これで「状態を見る」「メモを取る」「次の指示を積む」が、目線を大きく動かさずに完結します。


次の指示をその場で書く:キュー領域でエディタを開く

次の指示をその場で書こうとすると、入力方法が課題になりました。TUIに用意した簡易入力欄でも短い文は打てますが、日本語入力、とくにSKKのような入力方式は素直に使えません。せっかく次の指示を貯める仕組みを作っても、書く体験が悪いと結局使わなくなります。

そこで、使い慣れたエディタ(nvim)をそのまま開いて書く方式にしました。エディタで書いて保存して閉じると、その内容がキューに入る、という流れです。自分の設定もIMEもそのまま使えるので、書く負担がぐっと下がりました。

最初の実装では、エディタを開くときに作業領域を分割して、そこにエディタを出していました。ですがこれは主客転倒で、次の指示を1行書くために作業中のペインが潰れてしまいます。そこで、エディタを常駐メモと同じ仕組みの独立した端末として扱い、キューの領域の中に描画するよう作り替えました。エディタは作業領域に触れず、右カラムの下、キューが居た場所に開きます。


ハマった所:開いたエディタが1ミリ秒で死ぬ

キュー領域の中でエディタを開くようにした直後、おかしな挙動に出会いました。キューでEnterを押しても「何も起きない」のです。エディタも出ないし、エラーの通知も出ません。

手がかりはログにありました。ペインは確かに生成されていて、その約1ミリ秒後に終了コード1、シグナルはHangupで終了していたのです。

pane.spawn.start  pane_id=15
pane.spawned      pid=84777        ← エディタは起動している
pane.exit  code:1, signal:Hangup   ← 1ミリ秒後に死亡

起動して1ミリ秒で死ぬのは、起動に失敗したのではなく、起動直後に外から殺されている合図です。Hangup、つまりSIGHUPは、端末が切断されたときに子プロセスへ送られます。疑似端末の親側(PTYマスター)が即座に閉じられ、子のエディタにハングアップが届いていました。

原因は単純でした。生成したペインはPTYマスターを所有するオブジェクトなのですが、それを管理用のレジストリに登録し忘れていました。登録しないまま関数を抜けると、そのオブジェクトはその場で破棄されます。親が閉じれば、子は当然ハングアップして死にます。

修正は、生成した直後にランタイムをレジストリへ登録して、マスターを生かしておくだけでした。フォーク元が他のペインを生成するときと同じ手順に揃えるだけで、エディタはきちんと開いて入力を受け付けるようになりました。

弊社の経験では、PTYを使う機能で「起動直後にSIGHUPで即死する」場合は、まず疑似端末の親側の寿命、つまり所有権を疑うのが近道です。プロセスの起動引数や環境変数を疑う前に、親を誰が握っていて、いつ手放しているかを確認したほうが早いと感じています。


まとめ

今回やったことを振り返ると、次のとおりです。

  • 状態を見るだけのダッシュボードから一歩進め、メモと次の指示の操作まで1画面に集約した
  • 既存のTUIをフォークし、全workspace共通の常駐カラムを1本足す形で実装した
  • 次の指示はキューの領域内でエディタを開いて書けるようにし、日本語入力の体験を確保した
  • 起動直後にSIGHUPで死ぬ不具合は、PTYマスターの所有権の取り違えが原因だった

得られた一番の効果は、視線移動と「メモの散らばり」が減ったことです。今の作業を止めずに次の指示を貯められるようになり、頭の中に指示を抱えておく負担も軽くなりました。

このあとは、カラムの比率や自動送信の挙動を設定で調整できるようにして、運用しながら詰めていく予定です。マルチエージェント運用そのものの背景は、以下の記事にまとめています。

Claude Codeをtmuxで11人編成のマルチエージェント開発チームにした

今回のTUIが前提としている、複数エージェント運用の全体像です

/blog/claude-code-multi-agent-team/

関連記事