GitHubのトレンドを眺めていたら、tirth8205/code-review-graph というのが上のほうに来ていました。「AIコーディングツールがレビューのたびにコードベースを読み直して大量のトークンを焼くのを、tree-sitterで構造マップを作ってMCP経由で必要な分だけ渡すことで直す」——という触れ込みです。
私はふだんレビュー支援まわりのツールを何度か試しては、結局「結果が読めない・セットアップが重い」で外してきた経験があるので、この手の「トークンを削る」系は正直あまり期待していませんでした。ただ、pip install 一発でローカル完結、外部APIキーも要らないと書いてある。それなら使い捨てのコンテナに放り込んで、どこまで本当に動くか見てみようと思ったわけです。
結論から言うと、ツール自体は驚くほどあっさり動きました。むしろハマったのは自分の準備のほうで、しかもそのハマり方が「このツールの設計思想を分かってなかった」という類のもので、個人的にはそこが一番の収穫でした。順番に残しておきます。
試した環境
- ホストはWindows + Docker Desktop、検証は使い捨ての
python:3.12-bookwormコンテナの中だけで完結 - コンテナ内: Python 3.12.13 / pip 25.0.1 / git 2.39.5(Debian 12 bookworm)
- 対象:
code-review-graph(PyPI版 2.3.6、リポジトリの clone は commit50a8ad3)
対象リポジトリのcloneも依存のインストールも、全部コンテナの中でやっています。ホストには何も入れていません。
pip install はほぼ一瞬
まず素直にQuick Startどおり入れてみます。
$ pip install code-review-graph
...
real 0m10.327s
$ pip show code-review-graph | grep -E "Version|Requires"
Version: 2.3.6
Requires: fastmcp, mcp, networkx, tree-sitter, tree-sitter-language-pack, watchdog
10秒ちょっと。依存も networkx(グラフ構造)・tree-sitter(パーサ)・fastmcp/mcp(MCPサーバ)・watchdog(ファイル監視)と、名前を見ただけで「あぁ、そういう作りね」と想像がつく最小構成でした。重い機械学習系の依存が一切ぶら下がってこないのは好印象です。埋め込み(embedding)による意味検索は別サブコマンドで、必要な人だけが入れる建て付けになっているようでした。
CLIのサブコマンドは想像より多くて、build / update / status / visualize / wiki / detect-changes / serve(MCPサーバ起動)あたりが並びます。まずはツール自身のソースをそのまま対象にしてグラフを作ってみます。
$ code-review-graph build
INFO: Schema version 1 -> 9: running migrations
INFO: Running migration v2
...
INFO: Migrations complete, now at schema version 9
INFO: Progress: 185/185 files parsed
INFO: FTS index rebuilt: 3515 rows indexed
INFO: Loaded 3330 unique nodes, 25858 edges
Full build: 185 files, 3539 nodes, 26112 edges (postprocess=full)
real 0m5.523s
185ファイルを 5.5秒 でパースして、3539ノード・26112エッジのグラフができました。初回起動でSQLiteのスキーマをv1→v9まで自動マイグレーションしていくログが流れるのは、地味ですが「ちゃんとバージョン管理された永続DBなんだな」と伝わってきて、こういうところは経験上あとで効いてきます。
ちなみに build の最後は 3539 nodes / 26112 edges と言っているのに、直後の status は少し数字が減ります。
$ code-review-graph status
Nodes: 3515
Edges: 25858
Files: 185
Languages: bash, javascript, typescript, python, java, tsx, csharp, r, c, cpp, dart, ...
Built at commit: 50a8ad3aeb91
build の生パース数と、FTS索引に載る status の数がずれる、というだけの話だと思いますが(ここは推測です)、レビュー支援の文脈で「数が合わない」と一瞬ドキッとするので、最初に知っておくと落ち着けます。あとサポート言語が32種類(bash, python, go, rust, kotlin, swift, solidity, zig …)もあって、tree-sitterの言語パックをそのまま食わせている強みが出ていました。
ハマったのは「浅いclone」
ここからが本題です。私はコンテナ内で対象を git clone --depth 1 で持ってきていました。ディスクも時間も節約できるので、検証ではほぼ反射的にこうしてしまいます。
この状態で目玉機能の detect-changes(変更の影響範囲とリスクを、既存グラフに照らして読み取る)を叩くと、こうなりました。
$ code-review-graph detect-changes
WARNING: git diff failed (rc=128): fatal: bad revision 'HEAD~1'
{
"summary": "Analyzed 1 changed file(s):\n - 0 changed function(s)/class(es)\n - 0 affected flow(s)\n - 0 test gap(s)\n - Overall risk score: 0.00",
"risk_score": 0.0,
"changed_functions": [],
...
}
WARNING は出るものの、コマンド自体は成功(exit 0)で返ってきて、JSONは「変更0件・リスク0.00」。ここが今回一番の落とし穴でした。警告は1行だけ、そのあとは正常っぽい空の結果が返るので、気づかないとそのまま「変更なし」と受け取ってしまうんですよね。
原因は単純で、detect-changes は既定で HEAD~1 と HEAD の差分(=直前のコミットで何が変わったか)を見にいく設計なのに、--depth 1 の浅いcloneには HEAD~1 が存在しないからでした。ツールが悪いのではなく、私が「レビュー対象の履歴を1コミットしか持ってこなかった」のが敗因です。
試しに git fetch --unshallow で履歴を全部(564コミット)持ってきてから、まったく同じコマンドを叩き直すと、別物のように喋りはじめました。
$ git fetch --unshallow # 564 commits
$ code-review-graph detect-changes
{
"summary": "Analyzed 6 changed file(s):\n - 19 changed function(s)/class(es)\n - 11 test gap(s)\n - Overall risk score: 0.55\n - Untested: _parse_numstat, compute_file_churn, compute_risk_score, ...",
"risk_score": 0.55,
"changed_functions": [
{ "name": "_parse_numstat", "file_path": ".../changes.py", "line_start": 178, "risk_score": 0.45 },
...
]
}
6ファイル・19関数の変更、テストギャップ11件、総合リスク 0.55、そして「まだテストされていない関数」の名前まで並びます。context_savings として「このレビューでトークンを43%節約できた」といった見積もりも返ってきました。同じコマンド・同じコードベースで、cloneの深さを変えただけでこの差です。
| cloneの状態 | detect-changesの結果 |
|---|---|
—depth 1(浅い) | WARNING 1行+変更0件・リスク0.00(実質、何も分からない) |
fetch —unshallow(全履歴) | 6ファイル・19関数・テストギャップ11・リスク0.55+未テスト関数一覧 |
「レビュー支援ツールを浅いcloneのCIに雑に組み込むと、警告を握りつぶした瞬間に”いつも変更なし”を返す置物になる」——これは自分がCIに入れる立場だったら絶対に踏んでいた地雷なので、先に踏めてよかったです。ここは改善の余地というより運用側の注意点ですが、個人的には「警告が出たらexitを非ゼロにしてくれてもいいのに」とは思いました。このあたりは好みが分かれるところだと思います。
グラフを絵で見る
もうひとつ触っておきたかったのが visualize です。--format html を付けると、3.8MBほどの単一HTMLが吐かれます(中身はD3.jsで、外部CDNを1本読むだけの自己完結ファイル)。これをブラウザで開くとこうなりました。

コミュニティ検出でクラスタリングされた塊が、fixtures-user や code-review-graph-tool、backend-graph、eval-benchmark といったラベル付きで浮かんでいます。円をダブルクリックすると中に潜って個別ノードまで下りられる作りで、下端に Nodes 3515 / Edges 25858 / Files 185 / Mode community がそのまま出ているのが分かります。status で見た数字と絵がちゃんと一致しているのは、当たり前のようで安心できるポイントでした。
同じ構造から wiki サブコマンドを叩くと、コミュニティ単位でMarkdownのページが17枚生成されました。グラフを人が読む用に落とす導線まで用意されているのは、なるほどよく出来ているなと思います。
試してみての所感
ライブラリ選定でいつも気にするのは「動かないときに理由が読めるか」なんですが、今回の detect-changes は、動かない(=空を返す)ときにちゃんと WARNING を1行残していました。私は最初その1行を軽く見て沼ったわけですが、裏を返せば手がかりは出してくれていたわけで、そこは誠実な作りだと感じます。
pip 一発・ローカル完結・APIキー不要で、185ファイルが5.5秒。この軽さなら、まず自分の手元の中規模リポジトリで build → visualize して構造を眺めるところから使ってみたいです。一方でCIに detect-changes を組み込むなら、cloneを浅くしない(あるいは警告を検知したら落とす)ラッパーを噛ませるのが前提だな、というのが今回の一番の学びでした。
もっと良い運用の仕方があったら教えてください。