herdrからghqリポジトリを開くpopup pickerを作った話

概要

  • herdrから、ghqで並んでいるリポジトリを中央popupのfuzzy pickerで選び、そのリポジトリのworkspaceに一発で飛べるプラグインherdr-repo-pickerを書いた。
  • 同名のworkspaceが既に開いていればfocus、無ければ新規作成して初期ペインでclaudeを起動する。
  • 未allowの.envrcがあると起動をブロックし、direnv allowの手順を表示する安全側の挙動を入れた。
  • 実装はRust (ratatui / crossterm / nucleo)で、herdr 0.7.4以降のplacement = "popup"に乗せて画面中央の小窓として開くようにした。
  • prefix + pに割り当てておけば、複数リポジトリを行き来しながらClaude Codeで作業する日常が地味に軽くなる。

動機

herdrで日常の作業をやっていると、リポジトリごとに1つworkspaceを立てる運用に自然と落ち着く。
~/ghq/github.com/<org>/<repo>に入ってclaudeを起動し、そこからworktreeを切って作業していく形だ。

問題は、リポジトリを切り替えるたびに手順を踏み直すことだった。

  • 既存のworkspaceがあるかどうかを目で確認する。
  • 無ければherdr workspace create --cwd <path>を叩き、初期ペインでclaudeを起動し直す。
  • リポジトリ数が増えるほど、この儀式が体感の摩擦になる。

やりたいのは「prefix + pでpopupを開き、リポ名を数文字打って選び、あとは勝手にworkspaceを開いてほしい」の1点だった。
既存プラグインを軽く漁ったが、ghqと相性の良いものが見当たらなかったので自作した。

何ができるか

主な挙動は次の通り。

  • fuzzy検索: nucleo (HelixやZedと同じmatcher) でghq listの結果を絞り込む。
  • 既存workspaceのfocus: 同名workspaceが開いていれば、新規作成せず単にfocusする。
  • workspaceの新規作成 + claude起動: 無ければherdr workspace createして、初期ペインでconfigのlaunch_command (既定はclaude) を走らせる。
  • direnvガード: 選んだリポジトリに未allowの.envrcがあれば、起動をブロックしてdirenv allowの手順を表示する。
  • 中央popup UI: herdrのplacement = "popup"で、60%×60%の小窓として画面中央に浮かぶ (full-screenは潰さない)。

prefix + pでpopupを開いた直後の画面。中央60%×60%にRepo pickerが浮かび、ghq listのフル一覧が並んでいる。

インストール

herdrのプラグインとしてinstallする。

herdr plugin install mayaton/herdr-repo-picker

installの過程で、release済みのprebuildバイナリをGitHub Releasesから取ってくる。
DLに失敗した場合はcargoでの手元ビルドにフォールバックする1

対応プラットフォームはlinux/x86_64 / linux/aarch64 / macos/x86_64 / macos/aarch64の4種。

キーバインドを割り当てる

~/.config/herdr/config.tomlに、popupを開くキーを1つ足す。

[[keys.command]]
key = "prefix+p"
type = "plugin_action"
command = "herdr-repo-picker.open-picker"
description = "Open ghq repo picker"

保存したらherdr server reload-configで反映する。
prefix + pを叩くと、画面中央にpickerが浮かぶ。

検索欄に「do」と打った状態。nucleoが絞り込んだリポジトリだけがpopup内に並ぶ。

動作の流れ

popupを開いてから抜けるまでの流れは、次の3ステップで閉じる。

  1. ghq listのフル一覧がghq rootとの合成で絶対パス化されて表示される。
  2. 数文字打つとnucleoで絞り込まれ、上下キー / Enterで選択する。
  3. 選ばれたリポジトリに対し、同名のworkspaceがあればfocus、無ければ新規作成 + claude起動でジャンプする。

余計な確認は挟まない。
popup自体はEscで閉じるだけで、副作用は残らない。

configで挙動を差し替える

herdr plugin config-dir herdr-repo-pickerが指すディレクトリにconfig.tomlを置くと、コマンド類を差し替えられる。

# リポジトリ列挙 (ghq 互換ならなんでも良い)
list_command = ["ghq", "list"]
# 相対パスを絶対パスに合成する root コマンド
root_command = ["ghq", "root"]
# 新規 workspace 作成時に初期ペインで走らせるコマンド
# 空配列にすると素の shell のまま
launch_command = ["claude"]
# 未 allow の .envrc を検出したら起動をブロックする
direnv_guard = true

launch_commandだけを差し替えるユースケースが一番多いはず。
claudeの代わりにclaude --model opusnvimlazygitなどに書き換えれば、pickerは単に「ghqから選んでworkspaceに何かを走らせるランチャ」として使える。

list_commandroot_commandは、ghqと同じインターフェース (パス一覧 + rootディレクトリ) を返すコマンドなら差し替え可能なので、fdや独自スクリプトを載せても動く。

direnvガードの意図

未allowの.envrcを含むリポジトリに入ると、direnvは環境変数を読まずに警告だけ出す。
その状態でclaudeを起動すると、開発に必要な環境変数を欠いたまま作業に入ってしまう。

これに気付かず「なぜかテストが通らない」で時間を溶かした経験が何度かあったので、pickerの段階でブロックする挙動をデフォルトにした。

ブロック画面には、そのリポジトリでdirenv allowを打つための手順がそのまま表示される。
allowしてからpickerを開き直せば普通に飛べる。
「毎回allowを促されるのは煩わしい」場合はdirenv_guard = falseで無効化できる。

感想

  • 「prefix + pで既存workspaceにfocus、無ければ作る」だけの薄い機能で、リポ切り替えの体感摩擦が数秒→ほぼゼロまで落ちた。
  • placement = "popup"herdr plugin pane open --help--placement一覧に出てこない指定で、他プラグインのmanifestを眺めていて存在に気付いた。
  • 中央小窓のUIはfull-screen overlayとは体験が違うので、他プラグインでも試す価値がある。
  • Rust側の実装はratatui + crossterm + nucleoでコード全体1000行程度に収まった。
  • ghq互換のインターフェースを外部コマンドとして受け取る設計で、picker本体はghqそのものには依存していない。
  • MITライセンスで公開したので、ghqとherdrを併用している方は気軽に試してもらえれば。

  1. scripts/fetch-or-build.shv<version>のtagに紐付いたtarballを取得しSHA256でverifyしてから展開し、取得に失敗した場合はcargo build --releaseにフォールバックする。 ↩︎

Built with Hugo
Theme Stack designed by Jimmy