# grix

[English](README.md) | 日本語

grix は trigram 索引を使う grep です。

初回にディレクトリツリーの索引を作り、次回からはその索引で候補ファイルを絞って検索します。
候補ファイルには実際に regex を走らせるので、対応している範囲では ripgrep と同じ行を返します。

[![ci](https://github.com/kyo5uke/grix/actions/workflows/ci.yml/badge.svg)](https://github.com/kyo5uke/grix/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

![Linuxカーネル92,823ファイルで ripgrep 1.57秒、grix 16.9ms。マッチ行は同一](docs/bench-kernel.png)

## なぜ作ったか

ripgrep は速いですが、検索するたびに対象ファイルを読み直します。
小さいリポジトリなら気になりませんが、大きいツリーでは1回の検索に数秒かかることがあります。
コールドキャッシュやネットワークドライブだと、さらに待つことになります。

実際の作業では、同じツリーを何度も検索することが多いです。
リファクタリング、コードレビュー、AI コーディングエージェントなどでは、
1つのセッションで grep が何十回、何百回も走ります。
grix はそのたびに全ファイルを読む代わりに、索引から候補ファイルを引きます。

近似検索ではありません。
embedding やセマンティック検索ではなく、最後は実際のファイル内容に regex をかけて確認します。
対応している検索で grix と ripgrep の出力行が違う場合は、grix 側のバグです。
テストにも「索引あり検索とフルスキャンの結果が一致する」ことを確認する性質テストを入れています。
ベンチマークスクリプトも、両方のマッチ行数が一致しない場合は計測を止めます。

## インストール

```bash
cargo install grix              # ソースからビルド
cargo binstall grix             # ビルド済みバイナリを取得
brew install kyo5uke/tap/grix   # macOS / Linux
```

ビルド済みバイナリは [Releases](https://github.com/kyo5uke/grix/releases) からも入手できます（Windows 含む）。

## 使い方

```bash
cd your-repo
grix 'fn main'            # 初回は索引を作成。以降は毎回、検索前に索引を増分更新
grix 'fn main' src/       # 検索範囲をディレクトリやファイルに絞る
grix -C2 'fn main'        # 前後2行を表示
grix -t rust 'fn main'    # 型で絞る（-g '*.rs' でも可）
grix --no-auto-index 'fn main'  # 更新せず既存索引のまま検索（最速・古い可能性あり）
```

既定では各検索の前に増分更新が走るので、結果は常に最新です。
未変更のファイルは読み直さない（サイズと mtime で判定）ので、更新はディレクトリ走査が主なコストです。
更新を省いて既存の索引をそのまま使う最速の経路がほしいときは `--no-auto-index` を付けます。

### watch モード — 瞬間、かつ常に最新

```bash
grix watch    # ターミナルで起動したまま（または裏で常駐）
```

`grix watch` はファイルシステムのイベントを使って索引を裏で最新に保ち、変わった
ファイルだけを再索引します。これが動いている間、検索は毎回のディレクトリ走査を
完全に省きつつ最新の結果を返します。つまり `--no-auto-index` の速さを、古くなる
心配なしに得られます。停止は Ctrl-C。もし落ちても、検索は自分で更新する動作に
戻るだけです。

設定ファイルは不要です。モデルのダウンロードなどもありません。

索引はキャッシュディレクトリに保存します。

* Windows: `%LOCALAPPDATA%\grix`
* Linux/macOS: `~/.cache/grix`

リポジトリ内には何も書き込みません。

## ベンチマーク

Linux カーネルソース v6.12 で計測しました。
対象は 92,823 ファイル、約 1.4GB です。

環境は Windows 11、NVMe、ripgrep 15.1.0。
[`bench/run.sh`](bench/run.sh) で再現できます。
全パターンで、ripgrep と grix のマッチ行数が一致することを確認してから計測しています。
grix は `--no-auto-index`（増分更新を挟まず索引をそのまま使う）での数値です。これはクエリ自体の速さで、ツリーが未変更なら通常検索でもほぼ同じです。

| パターン                              |   一致行数 | ripgrep |   grix |    倍率 |
| --------------------------------- | -----: | ------: | -----: | ----: |
| `PageTransHuge`（まれなリテラル）          |      5 |  2.31 s |  97 ms | 23.7× |
| `EXPORT_SYMBOL`（頻出リテラル）           | 38,267 |  2.29 s | 195 ms | 11.7× |
| `static\s+int\s+\w+_probe`（regex） | 10,081 |  2.10 s | 288 ms |  7.3× |
| `spinlock`（`-i`）                  | 17,086 |  2.23 s | 223 ms | 10.0× |
| `zzqqxx_does_not_exist`（ヒットなし）    |      0 |  2.09 s |  41 ms | 50.5× |

索引サイズは 162 MiB です。
初回構築は約 26 秒でした。
ファイルシステムキャッシュが冷えている場合は、約 90 秒かかりました。

無変更時の `grix index` は約 2.4 秒です。
その場合、ファイル内容は読み直しません。

Linux でも計測しています。
GitHub Actions の通常ランナー 4 コアでの結果です。
公開ログは[こちら](https://github.com/kyo5uke/grix/actions/runs/27286573555)です。

| パターン                              | ripgrep |   grix |    倍率 |
| --------------------------------- | ------: | -----: | ----: |
| `PageTransHuge`（まれなリテラル）          |  338 ms | 7.6 ms | 44.6× |
| `EXPORT_SYMBOL`（頻出リテラル）           |  355 ms |  63 ms |  5.6× |
| `static\s+int\s+\w+_probe`（regex） |  390 ms |  99 ms |  4.0× |
| `spinlock`（`-i`）                  |  409 ms |  71 ms |  5.8× |
| `zzqqxx_does_not_exist`（ヒットなし）    |  335 ms | 7.6 ms | 44.0× |

Windows はディレクトリ走査のコストが大きいので、環境によって差は変わります。
それでも、大きいツリーを繰り返し検索する用途ではかなり効きます。

## 仕組み

trigram は連続する3バイトです。
たとえば `hello` を含むファイルは、必ず `hel`、`ell`、`llo` を含みます。

つまり、trigram からファイル一覧を引ける索引があれば、
1GB のファイルを毎回読む代わりに、いくつかのソート済みリストを交差させるだけで候補を絞れます。

grix は regex パターンから trigram の制約を取り出します。

* `abc.*def` は `abc` AND `def`
* `abc|xyz` は `abc` OR `xyz`

この考え方は、Russ Cox による Google Code Search の trigram プランナを元にしています。

索引で絞ったファイルには、最後に本物の regex を現在の内容に対して実行します。
そのため、索引が古くても存在しない行を表示することはありません。
ただし、最近追加された行は、索引を更新するまで見落とす可能性があります。

`grix --explain` を使うと、任意のパターンがどのような trigram プランになるか確認できます。
詳しくは [ARCHITECTURE.md](ARCHITECTURE.md) に書いています。

## ripgrep 互換性

出力形式、終了コード、gitignore の扱い、バイナリ検出、行の意味論は ripgrep に合わせています。

対応している主な形式は次のとおりです。

* 通常出力: `path:line:text`
* tty でのヘッダ形式
* 終了コード: 0 / 1 / 2
* `--json`
* `--color`

フラグはまだ最小限です。

| 対応済み                                                                                        | 未対応               |
| ----------------------------------------------------------------------------------------- | ----------------- |
| `-i`, `-F`, `-l`, `-c`, `-m`, `-A`, `-B`, `-C`, `-g`, `-t`, `-T`, `--json`, `--no-heading`, `--color` | `-U`, `--replace` |

対応済みの範囲で grix と ripgrep のマッチ行が食い違ったら、issue をください。

## AI コーディングエージェントで使う

AI コーディングエージェントは、同じツリーに対して1セッションで何百回も grep を
実行します。grix はそのたびの全読み込みを索引参照に変え、`grix mcp` でそれを
**エージェントが直接呼べるツール**として提供します。

### MCP サーバー

`grix mcp` は stdio 上の [Model Context Protocol](https://modelcontextprotocol.io)
サーバーです。`code_search`(正規表現検索、`path:line:text` を返す)と
`list_matching_files`(安い下調べ)の2ツールを公開し、稼働中は索引を裏で最新に
保つので、検索は毎回**即時かつ最新**です。

Claude Code:

```bash
claude mcp add grix -- grix mcp
```

Cursor / Windsurf / 任意の MCP クライアント(`mcp.json`):

```json
{
  "mcpServers": {
    "grix": { "command": "grix", "args": ["mcp"] }
  }
}
```

検索したいリポジトリの中でサーバーを起動してください(作業ディレクトリが検索の
ルートになります)。結果は grep が出すものと同じなので、エージェントは「それっぽい
近いコード」ではなく**正確な呼び出し箇所**を得られます。

### CLI だけでも

MCP を使わない場合は、エージェントにコマンドを使わせます。

```text
コード検索には grep / rg ではなく `grix <pattern>` を使うこと。
```

`--json` で1行1オブジェクトの機械可読出力。基本的な使い方は ripgrep に近いので、
単純な検索ならそのまま置き換えられます。

## 先行プロジェクト

* [Google Code Search](https://github.com/google/codesearch): Russ Cox の trigram プランナが本プロジェクトの土台です。解説は [Regular Expression Matching with a Trigram Index](https://swtch.com/~rsc/regexp/regexp4.html)。
* [zoekt](https://github.com/sourcegraph/zoekt): サーバー型の trigram 検索エンジンです。grix はこれをローカルで、セットアップなしに使える形に寄せています。
* [qgrep](https://github.com/zeux/qgrep)、[ugrep-indexer](https://github.com/Genivia/ugrep-indexer): 索引つき grep CLI の先行例です。アーカイブ式やバッチ索引など、grix とは少し違う設計です。
* [ripgrep](https://github.com/BurntSushi/ripgrep): grix が出力や挙動を合わせる基準にしている検索ツールです。

## ライセンス

MIT