jedipunkz です。
今回は自作した Fish シェルの Plugin である fuzz.fish を紹介します。
fuzz.fish は Fish Shell の コマンド履歴・ファイル・Git ブランチ・Git Worktree・Git コミット をインクリメンタルサーチできる Fish Plugin です。Go と Bubble Tea で TUI を実装していて、fzf や fd、ripgrep、bat といった外部ツールは一切不要です。プラグイン本体と単一の Go バイナリだけで動きます。
リンク
スクリーンショット
コマンド履歴・ファイル・Git ブランチ検索を切り替えている様子です。

開発動機
幾つか小さなツールはこれまでも作ってきましたが、利用する機会が減るとメンテナンスも怠りがちなことに気が付き、自分自身がよく使うツールを作ろうと思ったのがきっかけです。そして普段から Fish をシェルとして使っていますが、コマンド履歴検索や Git ブランチの切り替えをより効率的に出来れば何より自分にとって便利なツールになる予感がありました。
主な機能
- 外部ツールが不要。fzf / fd / ripgrep / bat をインストールしなくても、プラグインと Go バイナリだけで完結する
- キーバインドは
ctrl+rの 1 つ。起動後はctrl+sctrl+wctrl+gctrl+xで閉じずにモードを切り替えられる - 全モードにプレビューがある。履歴は実行時刻・ディレクトリ・前後のコマンド、ファイルはシンタックスハイライト付きの中身、Git 系はそれぞれの文脈を表示する
- 履歴は frecency で並ぶ。マッチ品質を最優先にした上で、
log1p(頻度)を最終実行時刻で減衰させるため、実際によく叩くコマンドが上に来る - Git はブランチだけでなく Worktree とコミットも扱える。コミット検索はハッシュと件名の両方にマッチし、
git show/git diff/git revert/git cherry-pickをプロンプトに置く(実行はしない)
インストール方法
必要なのは Fish Shell 3.0+ のみです。Fisher を使ってインストールします。
fisher install jedipunkz/fuzz.fish
インストール時に macOS / Linux (amd64 / arm64) 向けのビルド済みバイナリを GitHub Release からダウンロードします。それ以外のプラットフォームではソースからのビルドにフォールバックするため、その場合のみ Go 1.25+ と Git が必要です。
更新・アンインストールは Fisher の標準コマンドで行います。
fisher update jedipunkz/fuzz.fish
fisher remove jedipunkz/fuzz.fish
使い方
Fish のプロンプトで ctrl+r を押すと起動します。あとは文字を入力してインクリメンタル検索するだけです。モードはキー 1 つで切り替わります。
| キー | モード | enter の動作 |
|---|---|---|
ctrl+r |
コマンド履歴検索 (デフォルト) | コマンドをプロンプトに挿入する |
ctrl+s |
ファイル検索 | ファイルパスを挿入する / ディレクトリなら cd する |
ctrl+w |
Git Worktree 検索 | その Worktree へ cd する |
ctrl+g |
Git ブランチ検索 | そのブランチに切り替える |
ctrl+x |
Git コミット検索 | コミットに対して実行するコマンドを選ぶ |
全モード共通のキーです。
| キー | 動作 |
|---|---|
↑ / ↓ または ctrl+p / ctrl+n |
選択行を移動する |
tab |
選択中の項目でクエリを補完する |
ctrl+y |
選択中の項目をクリップボードにコピーする |
esc または ctrl+c |
キャンセルする |
細かい挙動です。
- コマンドラインに入力済みの文字列がそのまま検索ボックスに入ります。
vimと打ってからctrl+rを押すと、最初からvimで絞り込まれた履歴が出ます - クエリに
*を含めると全モードでファジー検索から glob マッチに切り替わります。nvim *.goはnvim internal/app/filter.goにマッチしますが、単に同じ文字を含むだけのコマンドにはマッチしません - Git ブランチ検索で、現在のブランチの上でもう一度
ctrl+gを押すとgit pull origin <branch>が走ります - リモートブランチを選択してローカルに同名のブランチがない場合は、ローカルブランチを作成して切り替えます
- Git コミット検索の
enterは小さなアクションリスト (git show,git diff,git revert,git cherry-pick,git rebase --onto, ハッシュのみ) を開き、選んだコマンドを実行せずにプロンプトに置きます - ファイル検索は隠しファイルと
node_modulesやvendorなどのビルド用ディレクトリをスキップします - Git リポジトリ外で
ctrl+xを押すとモードを切り替えず警告を出します
公式サイト
ドキュメント用に公式サイトも作りました。
https://jedipunkz.rocks/fuzz.fish/
Astro で構築し、GitHub Pages にデプロイしています。配色は TUI 側と揃えて Tokyo Night にしました。各モードの説明・キーバインド一覧・インストール手順をまとめてあるので、README を読むより手っ取り早く雰囲気が掴めると思います。
利用した Go パッケージ
- charm.land/bubbletea/v2 — TUI フレームワーク
- charm.land/bubbles/v2 / charm.land/lipgloss/v2 — 入力コンポーネントとスタイリング
- github.com/sahilm/fuzzy — ファジーマッチ
- github.com/go-git/go-git — Git 操作
- github.com/alecthomas/chroma — ファイルプレビューのシンタックスハイライト
- github.com/atotto/clipboard — クリップボード連携
Bubble Tea は v2 に移行しました。移行時にカーソルが二重に出る問題や Width/Height の計算がずれる問題があり、そこそこ手を入れています。
スコアリングについて
検索結果の並び順は自前で実装しています。マッチ品質 (前方一致・単語境界・連続マッチのボーナスと、離れたマッチへのギャップペナルティ) を 10 倍に重み付けして主たる指標とし、その上に履歴なら frecency、それ以外のモードなら recency を加算しています。
frecency は zoxide の考え方を借りて、log1p(頻度) に時間係数 (1 時間以内は ×4、1 日以内は ×2、1 週間以内は ×1、それ以上は ×0.5) を掛けています。log1p を挟んでいるのは、100 回使ったコマンドが 1 回のコマンドの 100 倍の重みを持たないようにするためです。
今後の改善点
- 設定ファイルによるキーバインド・配色のカスタマイズ
- 検索アルゴリズムの改善
- プレビュー機能の強化
まとめ
自分が毎日使うツールなので、使っていて気になった点をそのまま直す流れができていて、結果的に一番メンテナンスが続いているツールになりました。
フィードバックやプルリクエストは歓迎なので是非一度利用してみてくれると嬉しいです。