chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:42:18 +08:00
commit 05f60106aa
288 changed files with 76871 additions and 0 deletions
+404
View File
@@ -0,0 +1,404 @@
# All Available Commands
## Skills and Slash Commands
These commands are installed for clients that support project skills or slash-command style workflows.
### `/code-review-graph:build-graph`
Build or update the knowledge graph.
- First time: performs a full build
- Subsequent: incremental update (only changed files)
### `/code-review-graph:review-delta`
Review only changes since last commit.
- Auto-detects changed files via git diff
- Computes blast radius (2-hop default)
- Generates structured review with guidance
### `/code-review-graph:review-pr`
Review a PR or branch diff.
- Uses main/master as base
- Full impact analysis across all PR commits
- Structured output with risk assessment
## MCP Tools
### Core Tools
#### `build_or_update_graph_tool`
```
full_rebuild: bool = False # True for full re-parse
repo_root: str | None # Auto-detected
base: str = "HEAD~1" # VCS diff base for incremental updates
postprocess: str = "full" # "full", "minimal", or "none"
recurse_submodules: bool | None # Falls back to CRG_RECURSE_SUBMODULES
```
#### `run_postprocess_tool`
```
flows: bool = True
communities: bool = True
fts: bool = True
repo_root: str | None
```
#### `get_minimal_context_tool`
```
task: str = "" # What you are doing
changed_files: list[str] | None # Auto-detected from VCS when omitted
repo_root: str | None
base: str = "HEAD~1"
```
#### `get_impact_radius_tool`
```
changed_files: list[str] | None # Auto-detected from VCS
max_depth: int = 2 # Hops in graph
repo_root: str | None
base: str = "HEAD~1"
detail_level: str = "standard" # "standard" or "minimal"
```
Relevant responses may include compact estimated `context_savings` metadata.
#### `query_graph_tool`
```
pattern: str # callers_of, callees_of, imports_of, importers_of,
# children_of, tests_for, inheritors_of, file_summary
target: str # Node name, qualified name, or file path
repo_root: str | None
detail_level: str = "standard" # "standard" or "minimal"
```
#### `get_review_context_tool`
```
changed_files: list[str] | None
max_depth: int = 2
include_source: bool = True
max_lines_per_file: int = 200
repo_root: str | None
base: str = "HEAD~1"
detail_level: str = "standard" # "standard" or "minimal"
```
Relevant responses may include compact estimated `context_savings` metadata.
#### `traverse_graph_tool`
```
query: str
depth: int = 3 # 1-6
mode: str = "bfs" # "bfs" or "dfs"
token_budget: int = 2000
repo_root: str | None
```
#### `semantic_search_nodes_tool`
```
query: str # Search string
kind: str | None # File, Class, Function, Type, Test
limit: int = 20
repo_root: str | None
model: str | None # Embedding model (falls back to CRG_EMBEDDING_MODEL env var)
provider: str | None # local, openai, google, minimax
detail_level: str = "standard"
```
#### `embed_graph_tool`
```
repo_root: str | None
model: str | None # Embedding model name
provider: str | None # local, openai, google, minimax
```
Local embeddings require: `pip install code-review-graph[embeddings]`. Cloud providers use stdlib HTTP clients and require their provider environment variables.
#### `list_graph_stats_tool`
```
repo_root: str | None
```
#### `find_large_functions_tool`
```
min_lines: int = 50 # Minimum line count threshold
kind: str | None # File, Class, Function, or Test
file_path_pattern: str | None # Filter by file path substring
limit: int = 50 # Max results to return
repo_root: str | None
```
#### `get_docs_section_tool`
```
section_name: str # usage, review-delta, review-pr, commands, legal, watch, embeddings, languages, troubleshooting
```
### Flow Tools
#### `list_flows_tool`
```
sort_by: str = "criticality" # criticality, depth, node_count, file_count, name
limit: int = 50
kind: str | None # Filter by entry point kind (e.g. "Test", "Function")
repo_root: str | None
detail_level: str = "standard"
```
#### `get_flow_tool`
```
flow_id: int | None # Database ID from list_flows_tool
flow_name: str | None # Name to search (partial match)
include_source: bool = False # Include source snippets for each step
repo_root: str | None
```
#### `get_affected_flows_tool`
```
changed_files: list[str] | None # Auto-detected from VCS
base: str = "HEAD~1"
repo_root: str | None
```
### Community Tools
#### `list_communities_tool`
```
sort_by: str = "size" # size, cohesion, name
min_size: int = 0
repo_root: str | None
detail_level: str = "standard"
```
#### `get_community_tool`
```
community_name: str | None # Name to search (partial match)
community_id: int | None # Database ID
include_members: bool = False
repo_root: str | None
```
#### `get_architecture_overview_tool`
```
repo_root: str | None
detail_level: str = "minimal" # "minimal" compact default, "standard" full detail
```
Minimal responses may include compact estimated `context_savings` metadata.
### Graph Health and Architecture Tools
#### `get_hub_nodes_tool`
```
top_n: int = 10
repo_root: str | None
```
#### `get_bridge_nodes_tool`
```
top_n: int = 10
repo_root: str | None
```
#### `get_knowledge_gaps_tool`
```
repo_root: str | None
```
#### `get_surprising_connections_tool`
```
top_n: int = 15
repo_root: str | None
```
#### `get_suggested_questions_tool`
```
repo_root: str | None
```
### Change Analysis and Refactoring Tools
#### `detect_changes_tool`
```
base: str = "HEAD~1"
changed_files: list[str] | None
include_source: bool = False
max_depth: int = 2
repo_root: str | None
detail_level: str = "standard"
```
Primary tool for code review. Maps changed files to affected functions, flows, communities, and test coverage gaps. Returns risk scores and prioritized review items.
Relevant responses may include compact estimated `context_savings` metadata.
#### `refactor_tool`
```
mode: str = "rename" # "rename", "dead_code", or "suggest"
old_name: str | None # (rename) Current symbol name
new_name: str | None # (rename) New name
kind: str | None # (dead_code) Function or Class
file_pattern: str | None # (dead_code) Filter by file path substring
repo_root: str | None
```
#### `apply_refactor_tool`
```
refactor_id: str # ID from prior refactor_tool call
repo_root: str | None
dry_run: bool = False # Return diff without writing files
```
### Wiki Tools
#### `generate_wiki_tool`
```
repo_root: str | None
force: bool = False # Regenerate all pages even if unchanged
```
#### `get_wiki_page_tool`
```
community_name: str # Community name to look up
repo_root: str | None
```
### Multi-Repo Tools
#### `list_repos_tool`
```
(no parameters)
```
#### `cross_repo_search_tool`
```
query: str
kind: str | None
limit: int = 20
```
## MCP Prompts (5 workflow templates)
### `review_changes`
Pre-commit review workflow using detect_changes, affected_flows, and test gaps.
```
base: str = "HEAD~1"
```
### `architecture_map`
Architecture documentation using communities, flows, and Mermaid diagrams.
### `debug_issue`
Guided debugging using search, flow tracing, and recent changes.
```
description: str = ""
```
### `onboard_developer`
New developer orientation using stats, architecture, and critical flows.
### `pre_merge_check`
PR readiness check with risk scoring, test gaps, and dead code detection.
```
base: str = "HEAD~1"
```
## CLI Commands
```bash
# Setup
code-review-graph install # Configure detected AI coding platforms (alias: init)
code-review-graph install --dry-run # Preview without writing files
code-review-graph install --platform codex # Configure one platform
# Build and update
code-review-graph build # Full build
code-review-graph build --skip-flows # Parse + signatures + FTS only
code-review-graph build --skip-postprocess # Raw parse only
code-review-graph update # Incremental update
code-review-graph update --base origin/main # Custom base ref
code-review-graph update --brief # Update graph + show risk panel
code-review-graph update --brief --verify # ...and cross-check vs tiktoken
code-review-graph postprocess # Re-run flows, communities, FTS
code-review-graph embed --provider local # Compute vector embeddings for semantic search
# Monitor and inspect
code-review-graph status # Graph statistics
code-review-graph watch # Auto-update on file changes
code-review-graph visualize # Generate interactive HTML graph
code-review-graph visualize --format graphml # Export GraphML
code-review-graph visualize --serve # Serve graph.html on localhost:8765
# Analysis
code-review-graph detect-changes # Risk-scored change analysis
code-review-graph detect-changes --base HEAD~3 # Custom base ref
code-review-graph detect-changes --brief # Compact panel with token-savings estimate
code-review-graph detect-changes --brief --verify # ...and cross-check vs tiktoken
# detect-changes vs update --brief — which one?
# • detect-changes --brief: read-only. Asks "what's the impact of my current
# changes against the existing graph?" Fast (~1s). Use this when the graph
# is already up to date (the default, if you have hooks installed).
# • update --brief: re-parses your changed files into the graph FIRST, then
# runs the same analysis at the end. Use this after a rebase, a big
# change set, or whenever you suspect the graph is stale.
# Both end with an identical "Token Savings" panel.
# Wiki
code-review-graph wiki # Generate markdown wiki from communities
# Multi-repo
code-review-graph register <path> [--alias name] # Register a repository
code-review-graph unregister <path_or_alias> # Remove from registry
code-review-graph repos # List registered repositories
# Daemon (multi-repo watcher) — included with install, no extra dependencies
code-review-graph daemon start [--foreground] # Start the watch daemon
code-review-graph daemon stop # Stop the daemon
code-review-graph daemon restart [--foreground] # Restart the daemon
code-review-graph daemon status # Show daemon status and repos
code-review-graph daemon logs [--repo ALIAS] [--follow] # View daemon or per-repo logs
code-review-graph daemon add <path> [--alias NAME] # Add a repo to daemon config
code-review-graph daemon remove <path_or_alias> # Remove a repo from daemon config
# Evaluation
code-review-graph eval # Run evaluation benchmarks
# Server
code-review-graph serve # Start MCP server (stdio)
code-review-graph serve --http # Streamable HTTP on localhost:5555
code-review-graph serve --tools query_graph_tool,detect_changes_tool # Tool allowlist
code-review-graph mcp # Alias for serve
```
## Standalone Daemon CLI (`crg-daemon`)
The `crg-daemon` command is included with every `code-review-graph` installation — no
separate install required. It is also available as a standalone entry point. It mirrors the
`code-review-graph daemon` subcommands:
```bash
crg-daemon start [--foreground] # Start the multi-repo watch daemon
crg-daemon stop # Stop the daemon and all watcher processes
crg-daemon restart [--foreground] # Restart (stop + start)
crg-daemon status # Show daemon status, repos, and process liveness
crg-daemon logs [--repo ALIAS] [-f] [-n N] # Tail daemon or per-repo log files
crg-daemon add <path> [--alias NAME] # Add a repository to watch.toml
crg-daemon remove <path_or_alias> # Remove a repository from watch.toml
```
### Configuration
The daemon reads its configuration from `~/.code-review-graph/watch.toml`:
```toml
session_name = "crg-watch" # logical daemon name
log_dir = "~/.code-review-graph/logs"
poll_interval = 2 # seconds between config file polls
[[repos]]
path = "/home/user/project-a"
alias = "project-a"
[[repos]]
path = "/home/user/project-b"
alias = "project-b"
```
The daemon spawns one `code-review-graph watch` child process per repo,
managed via `subprocess.Popen`. It monitors the config file for changes and
automatically reconciles child processes (starting/stopping as repos are
added or removed). Health checks run every 30 seconds and automatically
restart dead watchers. No external dependencies (tmux, screen, etc.) are
required.
+171
View File
@@ -0,0 +1,171 @@
# Custom Languages (Bring Your Own Language)
code-review-graph ships parsers for 30+ languages, but the
[tree-sitter-language-pack](https://github.com/Goldziher/tree-sitter-language-pack)
it depends on bundles many more grammars than the built-in list. If your repo
uses a language the graph does not cover yet — Erlang, Haskell, OCaml,
Fortran, Ada, Clojure, ... — you can teach the parser about it with a small
config file. No fork, no code changes.
## Quick start
Create `<repo_root>/.code-review-graph/languages.toml`:
```toml
[languages.erlang]
extensions = [".erl"]
grammar = "erlang"
function_node_types = ["function_clause"]
class_node_types = ["record_decl"]
import_node_types = ["import_attribute"]
call_node_types = ["call"]
comment = "Erlang via the bundled tree-sitter-erlang grammar"
```
Then rebuild:
```bash
uv run code-review-graph build
```
Files matching the configured extensions are now parsed with the named
grammar, and the resulting Function/Class nodes and CALLS/IMPORTS_FROM edges
flow through every downstream feature (impact radius, search, communities,
wiki, MCP tools) exactly like built-in languages. Nodes carry the custom
language name (here `erlang`) in their `language` field.
## Schema reference
Each custom language is one `[languages.<name>]` table.
| Key | Type | Required | Meaning |
|-----|------|----------|---------|
| `<name>` | table key | yes | Language identifier stored on every parsed node. Lowercase letters, digits, `_`, `-`; max 32 chars; must start with a letter. |
| `extensions` | list of strings | yes | File extensions to claim, each starting with a dot (e.g. `".erl"`). Matched case-insensitively. |
| `grammar` | string | yes | A grammar name shipped by `tree_sitter_language_pack` (probe availability — see below). |
| `function_node_types` | list of strings | no* | Tree-sitter node types that define functions/methods. Matching nodes become `Function` nodes (or `Test` nodes when the name/file looks like a test). |
| `class_node_types` | list of strings | no* | Node types that define classes/records/types. Matching nodes become `Class` nodes. |
| `import_node_types` | list of strings | no* | Node types for import/include statements. Each yields an `IMPORTS_FROM` edge. |
| `call_node_types` | list of strings | no* | Node types for call expressions. Each yields a `CALLS` edge from the enclosing function. |
| `comment` | string | no | Free-form note for humans; ignored by the parser. |
\* At least one of the four node-type lists must be non-empty, otherwise the
entry is skipped (there would be nothing to extract).
### Validation rules (safety first)
The loader never crashes a build. Anything invalid is skipped with a
`WARNING` log line:
- **Built-ins always win.** A custom language cannot claim a built-in
extension (`.py`, `.ts`, `.ex`, ...) and cannot reuse a built-in language
name (`python`, `elixir`, ...).
- `grammar` must load from `tree_sitter_language_pack`; unknown grammars are
skipped.
- Every extension must start with a dot.
- Two custom languages cannot claim the same extension (first one wins).
- At most **20** custom languages are loaded per repo.
- Malformed TOML disables custom languages for that build (with a warning).
## Finding the right node type names
Node type names are grammar-specific, so you need to look at the tree the
grammar actually produces. Two easy options:
**Option 1 — tree-sitter playground.** Paste a snippet into
<https://tree-sitter.github.io/tree-sitter/7-playground.html> and read the
node names off the parse tree (select the matching grammar first).
**Option 2 — probe locally with Python.** The exact grammar version your
build uses is the one in `tree_sitter_language_pack`, so probing locally is
the most reliable source of truth:
```bash
uv run python - <<'EOF'
import tree_sitter_language_pack as tslp
source = b"""
-module(math_utils).
add(A, B) -> helper(A) + B.
helper(X) -> X * 2.
"""
def dump(node, depth=0):
print(" " * depth + node.type, node.text.decode()[:40].replace("\n", " "))
for child in node.children:
dump(child, depth + 1)
dump(tslp.get_parser("erlang").parse(source).root_node)
EOF
```
Pick the node types that wrap whole definitions (`function_clause`, not the
inner `atom`) and whole call expressions (`call`, not the callee identifier).
## Worked example: Erlang end to end
`src/math_utils.erl`:
```erlang
-module(math_utils).
-export([add/2, scale/2]).
-import(lists, [map/2]).
-record(point, {x, y}).
add(A, B) ->
helper(A) + B.
helper(X) -> X * 2.
scale(Points, F) ->
lists:map(fun(P) -> add(P, F) end, Points).
```
With the `[languages.erlang]` config from the quick start, a build produces:
- `Function` nodes `add`, `helper`, `scale` (from `function_clause`),
each with `language = "erlang"`.
- A `Class` node `point` (from `record_decl`).
- `CALLS` edges `add → helper` and `scale → add`, resolved to their
same-file qualified names, plus `scale → lists:map` for the remote call.
- An `IMPORTS_FROM` edge targeting `lists` (from `import_attribute`).
- `CONTAINS` edges from the file to every definition.
## How extraction works (and its limits)
Custom languages run through the same generic tree-sitter walker as built-in
languages — there is no per-language code path to maintain. That keeps the
feature simple, but the generic heuristics have limits:
- **Name extraction uses the default name-field heuristics.** The walker
looks for a child node of a common identifier type (`identifier`, `name`,
`type_identifier`, ...) and falls back to the grammar's `name` field
(`node.child_by_field_name("name")`). Grammars that store definition names
in another shape (e.g. nested two levels deep with a non-standard field)
will produce unnamed — and therefore skipped — definitions.
- **Callee extraction probes common field names** (`function`, `callee`,
`expr`, `name`) and descends through curried applications. Exotic call
shapes may be missed.
- **Import targets** come from the grammar's `module`/`name`/`path`/`source`
field when present, otherwise the raw statement text is recorded.
- **No cross-file module resolution.** Import edges keep the module name as
written (e.g. `lists`); they are not resolved to file paths the way
built-in languages with dedicated resolvers are.
- **No language-specific extras**: things like decorator-based test
detection, framework annotations (Spring, Temporal), or SFC handling only
exist for built-in languages.
If a language needs deeper support than the generic walker can give, please
open an issue — config-driven support is the on-ramp, not the ceiling.
## Troubleshooting
- Run a build with `-v`/logging enabled and look for `languages.toml`
warnings — every skipped entry says exactly why it was skipped.
- Probe grammar availability:
`uv run python -c "import tree_sitter_language_pack as t; t.get_language('erlang')"`
(raises `LookupError` if the grammar is not bundled).
- The config is read when a parser is constructed (every `build`/`update`),
so config changes take effect on the next build — re-run
`uv run code-review-graph build` after editing.
+252
View File
@@ -0,0 +1,252 @@
# FAQ — how code-review-graph compares
Honest answers to the questions we get most often. Where another tool is genuinely
better for a job, this page says so.
- [How is this different from LSP and language servers?](#how-is-this-different-from-lsp-and-language-servers)
- [Isn't this just RAG?](#isnt-this-just-rag)
- [Why not just grep?](#why-not-just-grep)
- [How does it compare to Serena, codegraph, claude-context, and repomix?](#how-does-it-compare-to-serena-codegraph-claude-context-and-repomix)
- [When should I not use it?](#when-should-i-not-use-it)
- [Does it phone home?](#does-it-phone-home)
- [How do I verify it is working?](#how-do-i-verify-it-is-working)
- [How big a codebase justifies it?](#how-big-a-codebase-justifies-it)
- [How does it handle monorepos, git worktrees, and multiple repos?](#how-does-it-handle-monorepos-git-worktrees-and-multiple-repos)
---
## How is this different from LSP and language servers?
Language servers and code-review-graph (CRG) both build a structural model of your
code, but they optimize for different things.
**What LSP does better.** A language server is backed by a real compiler frontend (or
something close to it), so it gives you type-aware, semantically precise results:
exact go-to-definition through generics and overloads, find-references that
understands scoping, live diagnostics, completions, and renames that are safe by
construction. If you need a *provably complete* reference list for one symbol in one
language, an LSP server is the gold standard and CRG does not try to replace it.
**What CRG does differently:**
- **One persistent graph instead of per-language daemons.** Language servers run one
process per language and (with a few exceptions that cache an index on disk) rebuild
or revalidate state per session. CRG parses once with Tree-sitter, stores nodes and
edges in a single SQLite file (`.code-review-graph/graph.db`), and answers queries
across roughly 35 languages plus notebooks from one process — including cross-language
edges that no single LSP server models.
- **It survives sessions and commits.** The graph is updated incrementally (changed
files only, under 2 seconds on a ~2,900-file repo) rather than rebuilt per editor
session.
- **Review-oriented edges.** `tests_for`, execution flows, community membership,
risk-scored change analysis — relationships LSP does not model because they are not
needed for editing.
**The honest trade-off:** CRG's call resolution is AST-level and heuristic, not
compiler-backed. Dynamic dispatch, metaprogramming, and duck typing can produce
inferred or ambiguous edges — which is exactly why every edge carries a confidence
tier (`EXTRACTED` / `INFERRED` / `AMBIGUOUS`). LSP is more precise per symbol; CRG is
broader, persistent, and cheaper to query across the whole repo.
## Isn't this just RAG?
No. RAG splits your code into text chunks, embeds them, and retrieves chunks by
similarity to the query. That answers "find code that *talks about* X." It cannot
answer "who *calls* X" — similarity between two functions tells you nothing about
whether one invokes the other.
CRG stores **structural edges parsed from the AST**: calls, imports, inheritance,
test coverage. "Who calls `login()`" is a graph lookup, not a similarity guess.
Embeddings exist in CRG but they are optional and play a supporting role — one input
to hybrid search (FTS5 BM25 keyword + vector) used to find a *starting node*, after
which traversal follows real edges. Currently only function signatures are embedded
(~10 tokens per node), not bodies.
The benchmark that captures the difference is multi-hop retrieval: natural-language
query → anchor node → one-hop traversal (`callers_of`, `tests_for`, ...). CRG scores
0.909 across 11 hand-curated tasks on 6 real repos (see
[REPRODUCING.md](REPRODUCING.md)). Pure similarity retrieval has no equivalent of the
second hop.
**Where RAG-style search is better:** purely conceptual questions ("where is rate
limiting discussed?") over prose, comments, and docs. CRG's own keyword search
ranking is a documented weakness (MRR 0.35 — see the limitations section in the
[README](../README.md#benchmarks)).
## Why not just grep?
Fair question — Anthropic has been explicit that Claude Code deliberately ships
*without* a code index. Agentic search (glob, grep, targeted file reads) is always
exactly as fresh as your working tree, has no chunking or staleness failure modes,
and needs zero setup. For one-hop questions — "where is `parse_file` defined?" —
that approach works well, and CRG will not beat it by much.
The gap appears on **multi-hop structural questions**, where each hop costs the agent
another round of grep + read + reasoning, and token spend compounds:
- **Impact radius** — "what could break if I change this file?" requires callers,
dependents, *and* their tests. One `get_impact_radius` call returns all three.
- **Callers of callers** — transitive tracing via `traverse_graph` or repeated
`query_graph(pattern="callers_of")`, instead of N rounds of grepping for each
intermediate name (and grep matches *text*, so overloaded or re-exported names
produce false hits the agent must read to rule out).
- **Tests for** — `query_graph(pattern="tests_for")` maps code to covering tests via
parsed edges plus naming conventions, and `detect_changes` adds transitive test
coverage. Grep only finds tests that mention the name literally.
- **Affected flows** — "which execution paths does this change touch?" has no grep
equivalent at all.
The graph also persists: agentic search re-derives the same structure from scratch
every session, while CRG keeps it in SQLite and updates incrementally.
One honest caveat on the numbers: the whole-corpus token-reduction numbers (~82x median,
38x528x range) compare graph responses against reading the **whole corpus**, not
against a skilled agentic-grep session (see [REPRODUCING.md](REPRODUCING.md) for what
each benchmark measures). For single-hop lookups in a small repo, grep is cheap and
good. The multi-hop review workflow is where the graph earns its keep.
## How does it compare to Serena, codegraph, claude-context, and repomix?
These are good tools solving adjacent problems. Short factual comparison, based on
each project's public documentation (check upstream docs for current behavior):
| Tool | Approach | Persistence | External deps | Review focus |
|---|---|---|---|---|
| **code-review-graph** | Tree-sitter AST → structural graph (calls, imports, inheritance, tests) over MCP + CLI | SQLite in `.code-review-graph/`, incremental updates | None for the core; embeddings optional | Yes — blast radius, risk-scored change analysis, test-gap detection |
| **Serena** | LSP-backed symbol retrieval and editing tools over MCP | Language-server state plus per-project memories | A language server per language | General coding-agent toolkit, not review-specific |
| **codegraph** | AST/call-graph indexing over MCP (several projects share this name; details vary by implementation) | Varies by implementation | Varies by implementation | Generally retrieval-focused |
| **claude-context** | Chunk + embed semantic code search over MCP | Vector index in a vector database | Embedding provider + vector DB (cloud or self-hosted) | Search-focused, not review-specific |
| **repomix** | Packs the whole repo into one AI-friendly file | None — regenerated per run | Node.js | One-shot context packing; no structural queries |
Rough guidance: if you want symbol-precise *editing* tools, Serena's LSP approach is
a better fit. If you want semantic *search* and are happy running a vector store,
claude-context covers that. If your repo is small enough to paste wholesale into a
large context window, repomix is the simplest thing that works. CRG's niche is the
persistent structural graph for **review**: impact analysis, risk scoring, and
test-coverage tracing with no external services.
## When should I not use it?
Consistent with the limitations section in the [README](../README.md#benchmarks):
- **Repos under a few hundred files.** An agent can often just read everything
relevant directly; the graph's structural metadata adds overhead that a small repo
doesn't repay. See [How big a codebase justifies it?](#how-big-a-codebase-justifies-it)
- **Trivial single-file changes.** The graph response carries impact-radius edges and
source snippets, which can exceed the raw content of a one-file diff. This is
measured and documented (the formal `token_efficiency` benchmark reports ratios
below 1.0 for small commits — by design, see [REPRODUCING.md](REPRODUCING.md)).
- **One-off questions on a repo you won't revisit.** The build is fast (~10 seconds
for a 500-file project) but the payoff comes from *reuse* across queries and
sessions. For a single question, agentic search is fine.
- **Flow detection on JS/Go.** Entry-point detection is currently reliable mainly for
Python framework patterns; JavaScript and Go flow detection needs work (33% recall,
documented in the README limitations).
## Does it phone home?
No. There is zero telemetry. The graph is a SQLite file in `.code-review-graph/`
inside your repo, and the core build / review / search / MCP workflows run entirely
locally. The streamable-HTTP MCP transport binds to localhost by default.
The only network activity is opt-in:
- **Local embeddings** (`pip install code-review-graph[embeddings]`) download the
sentence-transformers model from HuggingFace on first use. Your code does not leave
the machine.
- **Cloud embeddings** (OpenAI-compatible, Google Gemini, MiniMax) send the text being
embedded — currently function signatures — to the provider you explicitly configure
via environment variables. CRG prints an egress warning unless you acknowledge it
with `CRG_ACCEPT_CLOUD_EMBEDDINGS=1`; the warning is skipped automatically when the
endpoint is localhost.
See [LEGAL.md](LEGAL.md) for the full privacy notes.
## How do I verify it is working?
1. **Check the graph exists and has content:**
```bash
code-review-graph status
```
You should see node/edge counts and graph statistics. Zero nodes means the build
didn't run or found nothing to parse.
2. **See the savings on a real change** — make any edit, then:
```bash
code-review-graph detect-changes --brief
```
This prints the risk summary and the boxed **Token Savings** panel against the
existing graph (read-only). Add `--verify` to cross-check the estimate against
OpenAI's `cl100k_base` tokenizer (requires `pip install tiktoken`). If you suspect
the graph is stale, `code-review-graph update --brief` re-parses changed files
first and prints the same panel.
3. **Check the MCP wiring** — in Claude Code, run `/mcp` and confirm the
`code-review-graph` server is connected with its tools listed. Then ask the
assistant something structural ("what calls `parse_file`?") and watch it use
`query_graph` instead of grepping.
If any of these fail, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
## How big a codebase justifies it?
This comes up often (see #414). Honest guidance, tied to the documented small-repo
overhead:
- **Below a few hundred files:** marginal. The graph builds in seconds and works
fine, but an agent can already hold most of the repo in context, and for trivial
diffs the structural response can cost more tokens than it saves (the documented
overhead regime — see [When should I not use it?](#when-should-i-not-use-it)).
- **A few hundred to a few thousand files:** this is where the benchmarks live. The
six evaluation repos range from 60 to ~1,100 files and show 38x528x reductions on
whole-corpus agent questions, with the caveat noted above about what that baseline
measures.
- **Multi-thousand-file repos and monorepos:** the strongest case. No agent can read
the corpus per question (FastAPI alone is ~950k tokens of source), re-deriving
structure by search every session is the dominant cost, and incremental updates
keep the graph fresh in under 2 seconds.
A second axis matters as much as file count: **how often you ask multi-file
questions**. A 300-file repo you review daily benefits more than a 3,000-file repo
you touch once.
## How does it handle monorepos, git worktrees, and multiple repos?
**Monorepos.** One graph per repository root by default — commands auto-detect the
root by walking up to the nearest `.git`, and in git repos only tracked files are
indexed (`git ls-files`), so gitignored build artifacts are skipped automatically.
Use a `.code-review-graphignore` file to exclude tracked paths (e.g. `vendor/**`,
generated code), or pass `--repo <path>` to point a command at a specific directory.
**Git worktrees.** Each worktree is detected as its own root, so each gets its own
`.code-review-graph/` database matching its checkout. Don't try to share one database
across worktrees at different commits — the graph reflects one working tree. If you
want the database outside the working tree entirely (ephemeral workspaces, network
shares), use `--data-dir <path>` on `build`/`update`/etc., or set the `CRG_DATA_DIR`
environment variable.
**Multiple repos.** A lightweight registry (stored at
`~/.code-review-graph/registry.json`) lets MCP clients search across projects:
```bash
code-review-graph register ~/work/api --alias api # add a repo (optional alias)
code-review-graph repos # list registered repos
code-review-graph unregister api # remove by path or alias
```
Once registered, the `list_repos_tool` and `cross_repo_search_tool` MCP tools work
across all of them. To keep several graphs fresh automatically, the bundled daemon watches
registered repos as child processes:
```bash
crg-daemon add ~/work/api --alias api
crg-daemon start
crg-daemon status
```
(Also available as `code-review-graph daemon start|stop|status`.) See
[COMMANDS.md](COMMANDS.md) for the full daemon reference.
+155
View File
@@ -0,0 +1,155 @@
# Features
## v2.3.6 (Current)
- **Custom languages without forking**: drop a `.code-review-graph/languages.toml` into your repo to index any grammar shipped by tree-sitter-language-pack — extension map plus node-type lists, validated and capped, with built-in languages always winning. See [CUSTOM_LANGUAGES.md](CUSTOM_LANGUAGES.md).
- **GitHub Action for risk-scored PR reviews**: composite `action.yml` builds/restores the graph from CI cache, runs `detect-changes` against the PR base, and upserts a sticky comment with risk table, affected flows, test gaps, and the Token Savings line. Optional `fail-on-risk` merge gate. Dogfooded on this repo via `.github/workflows/pr-review.yml`. See [GITHUB_ACTION.md](GITHUB_ACTION.md).
- **`agent_baseline` eval benchmark**: compares graph queries against a realistic grep-and-read-top-k agent baseline instead of the whole-corpus strawman; wired into all six pinned eval configs.
- **Co-change ground truth for `impact_accuracy`**: predictions are also graded against files actually co-changed in the same commit; the legacy metric is explicitly labelled "graph-derived (circular — upper bound)".
- **Weekly eval CI**: `.github/workflows/eval.yml` runs a report-only cron of the two smallest pinned configs with CSV artifacts and a job summary.
- **docs/FAQ.md**: how CRG compares to LSP, RAG, grep/agentic search, and adjacent tools; when NOT to use it; verification steps; monorepo/worktree and registry guidance.
- **Contribution scaffolding**: GitHub issue forms (bug/feature/platform), a PR template mirroring the CONTRIBUTING checklist, and dependabot config for pip + GitHub Actions.
- **Windows fixes**: `daemon status` no longer crashes with WinError 87 (#511), and CLI `detect-changes` maps diff paths to absolute native paths so it no longer reports 0 functions (#528).
- **Provider-name validation**: unknown embedding provider names raise a clear error listing valid providers instead of silently falling back to the local model.
- **Store-leak fixes**: the five analysis MCP tools and the wiki-page tool no longer leak SQLite connections (try/finally `store.close()`).
- **`fastmcp<4` cap**: the next fastmcp major can no longer silently break the server.
- **Worktree-safe git hooks**: `install` resolves the real hooks directory via `git rev-parse --git-path hooks`, so linked worktrees and `core.hooksPath` (husky) setups get a working pre-commit hook.
## v2.3.5
- **Token Savings panel on every brief CLI call**: `code-review-graph detect-changes --brief` and the new `code-review-graph update --brief` print a boxed `Token Savings` panel — full-context baseline, graph response, saved tokens, percent, and per-category breakdown (Functions / Tests / Risk / Other) that sums exactly to the graph response size.
- **`--verify` flag**: cross-checks the displayed numbers against OpenAI's `cl100k_base` tokenizer (the GPT-4 family). Adds a second `Verified (tiktoken)` row showing real token counts. Calibration across 222 mixed-language files shows the estimate is within ~1% of real tokens in aggregate.
- **`update --brief`**: incremental update + the same risk panel in one command. Distinct from `detect-changes --brief` (which is read-only against the existing graph) — use update when the graph might be stale (post-rebase, large change set).
- **`code-review-graph embed` CLI subcommand**: explicit shell-level access to embedding generation. Previously only reachable via MCP.
- **Deterministic eval pipeline**: all 6 eval configs pin upstream SHAs, `eval/runner.py` uses full clones with explicit `returncode` checks, and Leiden community detection uses a fixed seed (`CRG_LEIDEN_SEED=42`). Two runs on different machines produce identical numbers.
- **`multi_hop_retrieval` benchmark**: 11 hand-curated 2-step tool-chain tasks (`hybrid_search``query_graph`) across the 6 test repos. Average score 0.909.
- **Richer semantic search**: `embeddings._node_to_text` now includes the dotted form (`Module.Class.method`), word-split identifiers, and enclosing module directory. Search ranking on natural-language queries improved from 0.545 → 0.909 on the multi-hop benchmark.
- **Identifier-aware search boost**: `extract_query_identifiers` pulls dotted / snake_case / CamelCase tokens out of NL queries and boosts matching qualified-names ×2.0 in hybrid search.
- **Path normalization fix**: `eval/runner.py` now resolves repo paths absolutely before storing, so the eval-built graph matches the CLI/MCP-built graph and `update` doesn't create duplicate nodes for the same source location.
- **Test-gap dedup**: the `Untested:` line in the brief summary dedupes by bare name (defensive guard if duplicate qualified_names slip in).
- **FTS5 auto-rebuild in eval**: the eval framework now calls `run_post_processing` after `full_build`, so FTS5 is populated automatically instead of leaving the index empty.
## v2.3.4
- **Estimated context savings**: Review, impact, detect-changes, and compact architecture responses include tiny `context_savings` metadata (`estimated`, `saved_tokens`, `saved_percent`) where a baseline can be estimated.
- **Compact architecture overview by default**: `get_architecture_overview_tool` defaults to `detail_level="minimal"` to avoid huge member lists and per-edge payloads. Use `detail_level="standard"` for full detail.
- **Bounded change analysis**: `CRG_MAX_CHANGED_FUNCS`, `CRG_MAX_TRANSITIVE_FRONTIER`, and `CRG_TOOL_TIMEOUT` help keep large MCP review calls responsive.
- **Windows MCP reliability**: Local embedding models are pre-warmed on Windows before FastMCP starts worker dispatch to avoid semantic-search deadlocks.
- **Parser correctness**: Rust `#[test]` and common async test attributes now produce `Test` nodes.
- **Graph lookup correctness**: Review, impact, and file-summary tools resolve user-facing paths to stored graph paths; `callers_of` includes cross-file callers even when same-file callers exist.
- **Install/runtime reliability**: Generated Codex/Claude hooks drain stdin, bundled docs are available from wheels, missing local embeddings report unavailable status, and `.svn` roots pass validation.
- **CLI reliability**: `build --skip-postprocess` and `update --skip-flows` honor the requested post-processing level.
- **Broad parser surface**: Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, Vue/Svelte SFCs, Astro files parsed through the TypeScript parser, Jupyter/Databricks notebooks, and Perl XS files.
- **Local-first by design**: SQLite graph storage remains local, with no telemetry and no cloud-default behavior.
## v2.0.0
- **22 MCP tools** (up from 9): 13 new tools for flows, communities, architecture, refactoring, wiki, multi-repo, and risk-scored change detection.
- **5 MCP prompts**: `review_changes`, `architecture_map`, `debug_issue`, `onboard_developer`, `pre_merge_check` workflow templates.
- **18 languages** (up from 15): Added Dart, R, Perl support.
- **Execution flows**: Trace call chains from entry points (HTTP handlers, CLI commands, tests), sorted by criticality score.
- **Community detection**: Cluster related code entities via Leiden algorithm (igraph) or file-based grouping.
- **Architecture overview**: Auto-generated architecture map with module summaries and cross-community coupling warnings.
- **Risk-scored change detection**: `detect_changes` maps git diffs to affected functions, flows, communities, and test coverage gaps with priority ordering.
- **Refactoring tools**: Rename preview with edit list, dead code detection, community-driven refactoring suggestions.
- **Wiki generation**: Auto-generate markdown wiki pages for each community with optional LLM summaries (ollama).
- **Multi-repo registry**: Register multiple repositories, search across all of them with `cross_repo_search`.
- **Full-text search**: FTS5 virtual table with porter stemming for hybrid keyword + vector search.
- **Database migrations**: Versioned schema migrations (v1-v5) with automatic upgrade on startup.
- **Optional dependency groups**: `[embeddings]`, `[google-embeddings]`, `[communities]`, `[eval]`, `[wiki]`, `[all]`.
- **Evaluation framework**: Benchmark suite with matplotlib visualization.
- **TypeScript path resolution**: tsconfig.json paths/baseUrl alias resolution for imports.
- **486 tests** across 22 test files.
## v1.8.4
- **Multi-word AND search**: `search_nodes` now requires all words to match (case-insensitive), producing more precise results.
- **Call target resolution**: Bare call targets are resolved to qualified names using same-file definitions, improving `callers_of`/`callees_of` accuracy.
- **Impact radius pagination**: `get_impact_radius` returns `truncated` flag and `total_impacted` count; `max_results` parameter controls output size.
- **`find_large_functions_tool`**: New MCP tool to find functions, classes, or files exceeding a line-count threshold.
- **15 languages**: Added Vue SFC and Solidity support.
- **Documentation overhaul**: All docs updated with accurate language/tool counts, version references, and VS Code extension parity.
## v1.8.3
- **Parser recursion guard**: `_MAX_AST_DEPTH = 180` prevents stack overflow on deeply nested ASTs.
- **Module cache bound**: `_MODULE_CACHE_MAX = 15,000` with automatic eviction.
- **Embeddings thread safety**: `check_same_thread=False` on EmbeddingStore SQLite.
- **Embeddings retry logic**: Exponential backoff for Google Gemini API calls.
- **Visualization XSS hardening**: `</` escaped to `<\/` in JSON serialization.
- **CLI error handling**: Split broad `except` into specific handlers.
- **Git timeout**: Configurable via `CRG_GIT_TIMEOUT` env var.
- **Governance files**: CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md.
## v1.8.2
- **C# parsing fix**: Renamed language identifier from `c_sharp` to `csharp`.
- **Watch mode thread safety**: SQLite connections compatible with Python 3.10/3.11 watchdog threads.
- **Full rebuild cleanup**: Purges stale data from deleted files during full rebuild.
- **Dependency trim**: Removed unused `gitpython` dependency.
## v1.7.0
- **`install` command**: New primary entry point for setup (`code-review-graph install`). `init` remains as an alias.
- **`--dry-run` flag**: Preview what `install`/`init` would write without modifying files.
- **PyPI auto-publish**: GitHub releases now automatically publish to PyPI.
- **README rewrite**: Professional documentation with real benchmark data from httpx, FastAPI, and Next.js.
## v1.6.4
- **Portable MCP config**: `init` now generates `uvx`-based `.mcp.json` — no absolute paths, works on any machine with `uv` installed
- **Removed symlink workaround**: The `_safe_path` helper for spaces-in-paths is no longer needed with `uvx`
## v1.6.3
- **SessionStart hook**: Claude Code automatically prefers graph MCP tools over full codebase scans at session start
- **Marketplace ready**: plugin.json corrected for official Claude Code plugin marketplace submission
- **README cleanup**: Removed screenshot placeholders
## v1.6.2
- **24 audit fixes**: Critical bug fixes, performance improvements, parser enhancements, expanded test coverage
- **Parser: C/C++ support**: Full node extraction for C and C++ (classes, functions, imports, calls, inheritance)
- **Parser: name extraction**: Fixed for Kotlin, Swift (simple_identifier), Ruby (constant)
- **Performance**: NetworkX graph caching, batch edge queries, chunked embedding search, git subprocess timeouts
- **CI hardening**: Coverage enforcement (50%), bandit security scanning, mypy type checking
- **Tests**: +40 new tests for incremental updates, embeddings, and 7 new language fixtures
- **Docs**: API response schemas, ignore pattern documentation, fixed hook config reference
- **Accessibility**: ARIA labels throughout D3.js visualization
## v1.5.3
- **Spaces-in-path handling**: *(superseded in v1.6.4 by `uvx`-based config)* Previously used symlinks for spaces in paths
- **No git required**: `build`, `status`, `visualize`, `watch` now work on any directory without git
- **Plugin ready**: Skills registered in plugin.json, SKILL.md frontmatter fixed
- **File organization**: Generated files moved into `.code-review-graph/` directory (auto-created `.gitignore`, legacy migration)
- **Visualization density**: Starts collapsed (File nodes only), search bar, clickable edge type toggles, scale-aware layout for large graphs
- **Project cleanup**: Removed redundant `references/`, `agents/`, `settings.json`
## v1.4.0
- **`init` command**: Automatic `.mcp.json` setup for Claude Code integration
- **Interactive D3.js graph visualization**: `code-review-graph visualize` generates an HTML graph you can explore in-browser
- **Documentation overhaul**: Comprehensive docs audit across all reference files
## v1.3.0
- **Python version check with Docker fallback**: Automatically detects Python 3.10+ and suggests Docker if unavailable
- **Universal install**: `pip install code-review-graph` — no git clone needed
- **CLI entry point**: `code-review-graph` command available system-wide after pip install
## v1.2.0
- **Logging improvements**: Structured logging throughout the codebase
- **Watch debounce**: Smarter file-change detection in watch mode
- **tools.py fixes**: Bug fixes and reliability improvements for MCP tools
- **CI coverage**: GitHub Actions CI/CD pipeline with test coverage reporting
## v1.1.0
- **Watch mode**: `code-review-graph watch` — auto-rebuilds graph on file changes
- **Vector embeddings**: Optional `pip install .[embeddings]` for semantic code search
- **Go, Rust, Java verified**: 12+ languages with dedicated test coverage
- **47 tests passing**, 8 MCP tools registered
- README badges and cleaner install flow
## v1.0.0 (Foundation)
- **Persistent SQLite knowledge graph** — zero external dependencies
- **Tree-sitter multi-language parsing** — classes, functions, imports, calls, inheritance
- **Incremental updates** via `git diff` + automatic dependency cascade
- **Impact-radius / blast-radius analysis** — BFS through call/import/inheritance graph
- **6 MCP tools** for full graph interaction
- **3 review-first skills**: build-graph, review-delta, review-pr
- **PostToolUse hooks** (Write|Edit|Bash) for automatic background updates
- **FastMCP 3.0 compatible** stdio MCP server
## Privacy & Data
- Core graph data is stored locally
- Graph stored in `.code-review-graph/graph.db` (SQLite), auto-gitignored
- No telemetry; core graph/review workflows do not require network access
- Optional embedding and wiki features may call configured local or remote services when explicitly enabled
- Respects `.gitignore` and `.code-review-graphignore`
+166
View File
@@ -0,0 +1,166 @@
# GitHub Action: Risk-Scored PR Review
code-review-graph ships a composite GitHub Action (`action.yml` at the repo
root) that posts a risk-scored, graph-aware review comment on every pull
request — think of it as a hosted AI review bot (Greptile-style), except the
analysis is **local-first**: the knowledge graph is built and queried entirely
on your CI runner, and no source code is sent to any external service.
On each PR run the action:
1. Installs `code-review-graph` from PyPI.
2. Restores the cached `.code-review-graph/` SQLite graph (or builds it from
scratch on a cache miss) and incrementally re-parses the files changed by
the PR.
3. Runs `code-review-graph detect-changes --base origin/<base-branch>` to get
risk-scored functions, affected execution flows, and test gaps.
4. Renders a markdown report (via `scripts/render_pr_comment.py`) and upserts
a single sticky PR comment — the same comment is updated on every push, so
the PR thread is never spammed.
5. Optionally fails the job when the overall risk score crosses a threshold
(`fail-on-risk`).
## Quick start (external repositories)
```yaml
# .github/workflows/code-review-graph.yml
name: code-review-graph
on:
pull_request:
permissions:
contents: read
pull-requests: write
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: tirth8205/code-review-graph@v2.3.6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
```
That is the whole setup. The default `GITHUB_TOKEN` provided by Actions is
sufficient — no PAT, no API key, no third-party service.
To turn the review into a merge gate:
```yaml
- uses: tirth8205/code-review-graph@v2.3.6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
fail-on-risk: high
```
## Inputs
| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `github-token` | yes | — | Token used to post the sticky PR comment via the GitHub API. The workflow's default `GITHUB_TOKEN` works when the job has `pull-requests: write`. |
| `comment` | no | `true` | Post (and keep updated) the sticky PR comment. Set to `false` to run analysis/gating without commenting. |
| `fail-on-risk` | no | `none` | Fail the job when the overall risk score reaches a level: `none` (never fail), `high` (risk ≥ 0.70), `critical` (risk ≥ 0.85). |
| `python-version` | no | `3.12` | Python version used to run code-review-graph (3.10+ supported). |
### Risk levels
`detect-changes` produces a 0.01.0 overall risk score (max across changed
functions; see `code_review_graph/changes.py:compute_risk_score` for the
scoring factors: flow participation, community crossing, test coverage,
security-sensitive names, caller count). The action maps it to levels:
| Level | Score |
|-------|-------|
| low | < 0.40 |
| medium | 0.40 0.69 |
| high | 0.70 0.84 |
| critical | ≥ 0.85 |
## What the comment contains
- **Overall risk** score and level, with counts of changed functions,
affected flows, and test gaps.
- **Risk-scored changes** — a table of the top changed symbols ordered by
risk, with file:line locations and test-coverage status.
- **Affected execution flows** — which entry-point flows the change touches,
ordered by criticality.
- **Test gaps** — changed functions with no direct test coverage.
- **Token savings** — how many tokens the graph-backed report saved versus
reading every changed file in full. This is the same `context_savings`
estimate the CLI's Token Savings panel shows (a `chars / 4` approximation
labelled `estimated: true` — see [REPRODUCING.md](REPRODUCING.md) for the
calibration methodology).
- A `Powered by code-review-graph` footer.
The comment starts with a hidden HTML marker
(`<!-- code-review-graph-report -->`). The action looks the marker up via
`gh api` on each run and PATCHes the existing comment instead of creating a
new one (a "sticky" comment).
## Cache behavior
The action caches the `.code-review-graph/` directory (the SQLite graph
database) with `actions/cache`:
- **Key**: `code-review-graph-schema9-<runner.os>-<hashFiles(lockfiles)>`,
where the lockfile hash covers common Python/JS/Go/Rust/Ruby/PHP lockfiles
(`uv.lock`, `poetry.lock`, `requirements*.txt`, `package-lock.json`,
`go.sum`, `Cargo.lock`, …).
- **Schema segment**: `schema9` tracks the database schema version
(`LATEST_VERSION` in `code_review_graph/migrations.py`). It is bumped when
the schema changes so stale caches are never restored across incompatible
versions.
- **Restore keys**: fall back to any cache for the same OS and schema, so a
lockfile change still reuses the previous graph.
- **On cache hit**: the action runs `code-review-graph update --base
origin/<base-branch>`, which re-parses only the files that differ from the
PR's base ref. If the restored database turns out to be unusable, it falls
back to a full `build`.
- **On cache miss**: a full `code-review-graph build` runs (one-time cost;
subsequent PR runs are incremental).
## Security notes
- **Token scope**: the action needs only `pull-requests: write` (to post the
comment) and `contents: read` (for checkout). Grant exactly that in the
workflow's `permissions:` block — the examples above do. The token is used
for nothing except listing/creating/updating the one PR comment.
- **Local-first**: analysis runs entirely on the runner. No code, diff, or
metadata leaves GitHub's infrastructure; there is no external API, account,
or key.
- **Untrusted input**: all dynamic values (`github.base_ref`, the PR number,
action inputs) are passed to scripts through environment variables, never
interpolated into shell commands. The markdown renderer escapes
table/markup characters and strips control characters from symbol names
and file paths before they reach the comment body, on top of the
server-side `_sanitize_name()` sanitization.
- **Pinning**: when consuming the action from another repository, pin
`uses:` to a release tag or commit SHA rather than `@main`.
- **Fork PRs**: `pull_request` runs from forks receive a read-only
`GITHUB_TOKEN`, so the comment step will fail for fork PRs unless you use
`pull_request_target` — which checks out trusted base-branch workflow
code; understand [the security implications](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/)
before switching, or set `comment: false` for fork PRs.
## Dogfooding
This repository runs the action on its own PRs via
[`.github/workflows/pr-review.yml`](../.github/workflows/pr-review.yml),
which `uses: ./` (the local `action.yml`).
## Rendering script
The markdown rendering and risk gating logic lives in
[`scripts/render_pr_comment.py`](../scripts/render_pr_comment.py) (stdlib
only, unit-tested in `tests/test_action_render.py`) rather than inline YAML,
so it can be tested and reused:
```bash
code-review-graph detect-changes --base origin/main | \
python scripts/render_pr_comment.py # markdown to stdout
python scripts/render_pr_comment.py --input report.json \
--fail-on-risk high --quiet # gate only: exit 3 on breach
```
+15
View File
@@ -0,0 +1,15 @@
# Documentation Index
- [USAGE.md](USAGE.md) -- How to install and use
- [FAQ.md](FAQ.md) -- How it compares to LSP, RAG, grep, and similar tools; when not to use it
- [FEATURES.md](FEATURES.md) -- What's included, changelog
- [COMMANDS.md](COMMANDS.md) -- All 30 MCP tools, 5 MCP prompts, skills, and CLI commands
- [GITHUB_ACTION.md](GITHUB_ACTION.md) -- Risk-scored PR review comments via GitHub Actions
- [CUSTOM_LANGUAGES.md](CUSTOM_LANGUAGES.md) -- Bring your own language via `.code-review-graph/languages.toml`
- [LLM-OPTIMIZED-REFERENCE.md](LLM-OPTIMIZED-REFERENCE.md) -- Token-optimized reference for MCP-capable AI coding agents
- [architecture.md](architecture.md) -- System design and data flow
- [schema.md](schema.md) -- Graph node/edge schema, SQLite tables (including flows, communities, FTS5)
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) -- Common issues and fixes (including Windows/WSL)
- [REPRODUCING.md](REPRODUCING.md) -- Reproducing every benchmark number (pinned SHAs, seeded runs, tokenizer calibration)
- [ROADMAP.md](ROADMAP.md) -- Shipped and planned features
- [LEGAL.md](LEGAL.md) -- License and privacy
+16
View File
@@ -0,0 +1,16 @@
# Legal & Privacy
**License:** MIT (see [LICENSE](../LICENSE) in project root)
**Privacy:**
- Zero telemetry
- All graph data stored locally in `.code-review-graph/graph.db`
- Core graph build, review, search, and CLI/MCP workflows run locally
- Optional local embeddings may download a sentence-transformers model from HuggingFace when first used
- Optional cloud embedding providers (`openai`, `google`, `minimax`) send embedded source snippets to the configured provider only when explicitly selected
- Remote embedding providers print an egress warning unless `CRG_ACCEPT_CLOUD_EMBEDDINGS=1` is set
- Streamable HTTP MCP transport binds to localhost by default
**Data:** Core graph data stays on your machine. If you opt into a cloud embedding provider, the text being embedded leaves your machine under that provider's terms.
**Warranty:** Provided as-is, without warranty of any kind.
+71
View File
@@ -0,0 +1,71 @@
# LLM-OPTIMIZED REFERENCE -- code-review-graph v2.3.6
AI coding agents: Read ONLY the exact `<section>` you need. Never load the whole file.
<section name="usage">
Quick install: pip install code-review-graph
Then: code-review-graph install && code-review-graph build
First run: /code-review-graph:build-graph
After that use only delta/pr commands.
ALWAYS start with get_minimal_context_tool(task="your task") — returns ~100 tokens with risk, communities, flows, and suggested next tools.
Use detail_level="minimal" on all subsequent calls unless you need more detail.
When present, context_savings is an estimated compact hint, not exact tokenization.
</section>
<section name="review-delta">
1. Call get_minimal_context_tool(task="review changes") first.
2. If risk is low: detect_changes_tool(detail_level="minimal") → report summary.
3. If risk is medium/high: detect_changes_tool(detail_level="standard") → expand on high-risk items.
Target: ≤5 tool calls, ≤800 tokens total context.
</section>
<section name="review-pr">
Fetch PR diff -> detect_changes_tool -> get_affected_flows_tool -> structured review with blast-radius table and risk scores.
Never include full files unless explicitly asked.
</section>
<section name="commands">
Core MCP tools: get_minimal_context_tool, detect_changes_tool, get_review_context_tool, get_impact_radius_tool, query_graph_tool, semantic_search_nodes_tool, get_architecture_overview_tool, get_affected_flows_tool, list_flows_tool, list_communities_tool, refactor_tool, build_or_update_graph_tool, run_postprocess_tool, embed_graph_tool, list_graph_stats_tool, get_docs_section_tool
MCP prompts (5): review_changes, architecture_map, debug_issue, onboard_developer, pre_merge_check
Skills: build-graph, debug-issue, explore-codebase, refactor-safely, review-changes, review-delta, review-pr
CLI: code-review-graph [install|init|build|update|status|watch|visualize|serve|mcp|wiki|detect-changes|postprocess|embed|register|unregister|repos|eval|daemon]
Token efficiency: Prefer detail_level="minimal" where available. Always call get_minimal_context_tool first. Some review/context tools return compact estimated context_savings metadata.
</section>
<section name="legal">
MIT licence. Core graph/review workflows are local and there is no telemetry. DB file: .code-review-graph/graph.db. Optional cloud embeddings send embedded source snippets to the configured provider only when selected.
</section>
<section name="watch">
Run: code-review-graph watch (auto-updates graph on file save via watchdog)
Or use PostToolUse (Write|Edit|Bash) hooks for automatic background updates.
</section>
<section name="embeddings">
Optional: pip install code-review-graph[embeddings]
Then call embed_graph_tool to compute vectors.
semantic_search_nodes_tool auto-uses vectors when available, falls back to keyword + FTS5.
Providers: local sentence-transformers, OpenAI-compatible endpoints, Google Gemini, and MiniMax.
Configure via provider/model parameters, CRG_EMBEDDING_MODEL for local, or CRG_OPENAI_* for OpenAI-compatible endpoints.
</section>
<section name="languages">
Supported: Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, Vue/Svelte SFCs, Astro files parsed through the TypeScript parser, Jupyter/Databricks notebooks, and Perl XS files.
Parser: Tree-sitter via tree-sitter-language-pack
Custom languages: add .code-review-graph/languages.toml (extensions + node types per grammar) — no fork needed, see docs/CUSTOM_LANGUAGES.md. Built-ins cannot be overridden.
</section>
<section name="troubleshooting">
DB lock: SQLite WAL mode, auto-recovers. Only one build at a time.
Large repos: First build 30-60s. Incremental <2s. Add patterns to .code-review-graphignore.
Stale graph: Run /code-review-graph:build-graph manually.
Missing nodes: Check language support + ignore patterns. Use full_rebuild=True.
Windows/WSL: Use forward slashes in paths. Ensure uv is on PATH in WSL.
</section>
**Instruction to AI coding agents (always follow):**
When user asks anything about "code-review-graph", "how to use", "commands", "review-delta", etc.:
1. Call get_docs_section_tool with the exact section name.
2. Use ONLY that content + current graph state.
3. Never include full docs or source code in your reasoning.
This keeps documentation lookup compact and avoids loading broad reference files by default.
+429
View File
@@ -0,0 +1,429 @@
# Reproducing the Benchmarks
This document gives the exact commands to reproduce every benchmark number
shown in the README and the `diagrams/`. Two people running the recipe below
on different machines on different days should produce identical numbers,
within float rounding.
If you get different numbers, that's a bug — please file an issue.
## Verifying the "saved tokens" number
The CLI's `Token Savings` panel uses a `chars / 4` approximation labelled
`estimated: true`, not a model-specific tokenizer. The approximation is
designed to be both fast (no model load, no inference) and conservative.
### How to verify against a real tokenizer
```bash
pip install tiktoken
code-review-graph detect-changes --brief --verify
```
The panel grows a `Verified (tiktoken)` row showing the same calculation
done with OpenAI's `cl100k_base` tokenizer (the GPT-4 family). If the
estimate is significantly off, you'll see it immediately:
```text
┌───────────────────────── Token Savings ─────────────────────────┐
│ Full context would be: 12,921 tokens │
│ Graph context used: 762 tokens │
│ Saved: 12,159 tokens (~94%) │
│ Verified (tiktoken): 10,835 tokens (~93%) [11,611 → 776] │
│ Breakdown: Functions 244 · Tests 191 · Risk 244 · Other 83 │
└─────────────────────────────────────────────────────────────────┘
```
### Calibration result (committed)
A one-time calibration across 222 files / 2.2 MB of mixed source
(Python, JS, TS, Go, Rust, RST, MD) pulled from the 6 test repos:
| Repo | sample files | bytes | chars/4 estimate | tiktoken real | ratio est/real |
|---|---:|---:|---:|---:|---:|
| flask | 46 | 470,179 | 117,559 | 109,969 | 1.069 |
| fastapi | 38 | 156,224 | 39,072 | 34,897 | 1.120 |
| gin | 30 | 471,793 | 117,962 | 132,296 | 0.892 |
| express | 23 | 296,805 | 74,207 | 83,575 | 0.888 |
| httpx | 38 | 254,184 | 63,556 | 62,909 | 1.010 |
| code-review-graph | 47 | 539,206 | 134,820 | 120,760 | 1.116 |
| **OVERALL** | **222** | **2,188,391** | **547,176** | **544,406** | **1.005** |
`chars / 4` is within **+0.5%** of real GPT-4 tokens in aggregate. Per-repo
it swings between **-11%** (gin: lots of short Go identifiers) and **+12%**
(fastapi: heavy docstrings and type hints), but the **ratio** stabilizes
because both sides of the divide are equally biased.
Reproduce the calibration with the snippet in this commit's
`code_review_graph/context_savings.py:verify_with_tiktoken`, or
inline-run the `--verify` flag on any commit.
## What is and isn't deterministic
| Reproducible | Reason |
|---|---|
| Tree-sitter parsing | Pure function of input bytes |
| Node / edge counts | Deterministic upserts keyed by `qualified_name` |
| FTS5 BM25 scores | Deterministic |
| Embeddings via `all-MiniLM-L6-v2` on CPU | Model weights cache-pinned by SHA in HuggingFace cache |
| Leiden community IDs | Seeded — `_LEIDEN_SEED=42` in `communities.py`, override with `CRG_LEIDEN_SEED` env var |
| `naive_corpus_tokens` | Deterministic for a fixed git checkout |
| `git clone` at a pinned SHA | Determines the source-of-truth byte stream |
What used to make it **non**-reproducible (now fixed):
- `commit: HEAD` in every `code_review_graph/eval/configs/*.yaml` — replaced with the pinned latest test-commit SHA per repo
- `git clone --depth 50` silently fell back to wrong commits when the pinned SHAs were beyond the shallow window — now uses full clones with explicit `returncode` checks
- Leiden ran with an unseeded RNG — now seeded
- `nextjs.yaml` was a misnamed config evaluating this repo — renamed to `code-review-graph.yaml`
- FTS5 was created but never populated by the eval framework's `full_build` call — `code_review_graph/eval/runner.py` now calls `postprocessing.run_post_processing` directly
## Prerequisites
- Python 3.10 or newer
- `git` on PATH
- Network access (~600 MB to clone the 6 upstream repos)
- ~3 GB free disk
- For the embedding step: roughly 700 MB extra for `torch` + `sentence-transformers`
## Step 1 — Install with the right extras
```bash
git clone https://github.com/tirth8205/code-review-graph
cd code-review-graph
# eval extras: pyyaml + matplotlib (matplotlib only needed for `--report`)
# embeddings extras: sentence-transformers + numpy
uv sync --extra eval --extra embeddings # or: pip install -e ".[eval,embeddings]"
```
## Step 2 — Run the formal eval
This step clones 6 upstream repositories at pinned SHAs, builds a full graph
for each (parser + cross-file resolvers + signatures + FTS5 + flows + Leiden
communities), then runs the `token_efficiency`, `impact_accuracy`,
`agent_baseline`, and `multi_hop_retrieval` benchmarks.
```bash
uv run code-review-graph eval \
--benchmark token_efficiency,impact_accuracy,agent_baseline,multi_hop_retrieval
```
Failure semantics (applies to every benchmark): a thrown tool call is **not**
a measurement. The row is kept in the CSV with `status=error` for forensics,
but excluded from every aggregate. (Two historical bugs made failures look
like wins: a thrown `get_review_context` produced `graph_tokens=0` and a
ratio of `naive/1`, and a thrown `analyze_changes` silently set
`predicted = changed`, guaranteeing recall 1.0. Both are fixed; regression
tests live in `tests/test_eval.py`.)
Expected runtime on an M1/M2 Mac: roughly 815 minutes for the build phase,
plus seconds per benchmark.
Outputs:
- `evaluate/test_repos/{express,fastapi,flask,gin,httpx,code-review-graph}/`
- `evaluate/test_repos/<name>/.code-review-graph/graph.db`
- `evaluate/results/<name>_<benchmark>_<date>.csv`
## Step 3 — Generate embeddings (required for the standalone benchmark)
The standalone token benchmark ships with 5 hardcoded natural-language
questions. Without embeddings, hybrid search can't match them and the
benchmark silently returns 0× reduction ratios (a loud warning will print).
```bash
for repo in express fastapi flask gin httpx code-review-graph; do
uv run code-review-graph embed --repo "evaluate/test_repos/$repo"
done
```
Expected runtime: 25 minutes total. Vectors live inside the same `graph.db`.
## Step 4 — Run the standalone token benchmark
This benchmark compares **all source-file tokens** in the repo against
**5 search hits + a few neighbor edges** for each of 5 sample questions. The
ratio answers: *how many tokens does the graph let me skip on a typical
question?*
```bash
uv run python <<'PY'
import json
from pathlib import Path
from code_review_graph.graph import GraphStore
from code_review_graph.token_benchmark import run_token_benchmark
results = {}
for repo in sorted(Path("evaluate/test_repos").iterdir()):
db = repo / ".code-review-graph" / "graph.db"
if not db.exists():
continue
store = GraphStore(str(db))
try:
results[repo.name] = run_token_benchmark(store, repo)
finally:
store.close()
print(f"{'Repo':<22}{'naive_tokens':>16}{'avg_graph_tokens':>20}{'avg_ratio':>14}")
print("-" * 72)
for name, out in sorted(results.items(), key=lambda x: -x[1]["average_reduction_ratio"]):
pq = out["per_question"]
avg_graph = int(sum(r["graph_tokens"] for r in pq) / max(len(pq), 1))
print(f"{name:<22}{out['naive_corpus_tokens']:>16,}"
f"{avg_graph:>20,}{out['average_reduction_ratio']:>13.1f}×")
Path("evaluate/standalone_token_benchmark.json").write_text(json.dumps(results, indent=2))
PY
```
## Canonical numbers
<!-- BEGIN canonical-stats -->
Captured **2026-05-25** on macOS arm64, Python 3.11, sentence-transformers 5.5.1,
`all-MiniLM-L6-v2`, `CRG_LEIDEN_SEED=42`. If your numbers differ by more than
rounding, something in the chain has drifted — file an issue.
### Standalone token benchmark (`code_review_graph/token_benchmark.py`)
Each row is the average of 5 sample questions (`how does authentication work`,
`what is the main entry point`, `how are database connections managed`,
`what error handling patterns are used`, `how do tests verify core functionality`).
| Repo | snapshot SHA | naive_corpus_tokens | avg graph_tokens | avg ratio |
|---|---|---:|---:|---:|
| fastapi | `0227991a` | 951,071 | 2,169 | **528.4×** |
| code-review-graph | `84bde354` | 208,821 | 2,495 | **93.0×** |
| gin | `5c00df8a` | 166,868 | 1,990 | **91.8×** |
| flask | `a29f88ce` | 125,022 | 1,986 | **71.4×** |
| express | `b4ab7d65` | 135,955 | 3,465 | **40.6×** |
| httpx | `b55d4635` | 89,492 | 2,438 | **38.0×** |
Range across 6 repos: **38× 528×**. The numbers shifted down from a
previous capture because (a) the test repos are now wiped/re-cloned from
scratch — no leftover build artifacts or local caches inflate the naive
baseline; and (b) the embedding text per node became richer in this same
release (see `embeddings._node_to_text`), so the graph response itself is
slightly bigger. Both are correctness improvements over the prior numbers.
### Formal `token_efficiency` benchmark (`code_review_graph/eval/benchmarks/token_efficiency.py`)
A different denominator: just the **changed-file content** for each commit,
vs the full `get_review_context()` JSON. For small commits the response is
larger than the input (it carries impact-radius edges + source snippets), so
ratios here are intentionally < 1.0 — that is not a bug, it measures a
different thing than the standalone benchmark.
Raw per-commit CSVs in `evaluate/results/<repo>_token_efficiency_*.csv`.
### Impact accuracy (`code_review_graph/eval/benchmarks/impact_accuracy.py`)
13 commits across 6 repos. The benchmark emits two ground-truth modes side
by side, distinguished by the `ground_truth_mode` CSV column:
| Mode | Ground truth | What it tells you |
|---|---|---|
| `graph-derived (circular — upper bound)` | changed files + files with CALLS/IMPORTS_FROM edges into them — **derived from the same graph the predictor traverses** | An upper bound. Recall 1.0 here is partly true by construction, not independent evidence. |
| `co-change (same commit, seed excluded)` | the *other* files the author actually touched in the same commit, given a single seed file | Independent-ish evidence from git history. Expect substantially lower recall. |
The canonical numbers below were captured **in graph-derived mode only**
(the co-change mode did not exist at capture time). Treat the recall row as
a circular upper bound, not as "100% recall":
| Metric (graph-derived mode — circular upper bound) | Value |
|---|---|
| Recall (mean across 13 commits) | **1.000** (upper bound on every commit) |
| F1 (mean) | **0.714** |
| F1 (median) | 0.667 |
| F1 (min / max) | 0.455 / 1.000 |
Canonical co-change numbers will be added after the next full capture — we
do not quote them before measuring. Single-file commits are recorded with
`status=skipped` in co-change mode (there is nothing independent to grade
against).
The blast-radius analysis over-predicts in some commits (precision ≈ 0.30 in the
worst case, where 34 files are flagged for a 10-file change). That is
intentional: a missed dependency is worse than an extra reviewed file.
### Multi-hop retrieval (`code_review_graph/eval/benchmarks/multi_hop_retrieval.py`)
11 hand-curated tasks across the 6 repos. Each task is a 2-step tool chain:
1. `hybrid_search(nl_query, limit=10)` looks for a starting anchor node.
2. `query_graph(<traversal_pattern>, target=<anchor>)` walks one hop along
`callers_of` / `callees_of` / `tests_for` / `imports_of` / etc.
The task **scores 1.0** only if both the anchor is found in the top-K *and*
the expected neighbor names are returned by the traversal. **Scores 0.0**
otherwise (which collapses both "search missed the anchor" and "traversal
returned the wrong set" — split those by inspecting `anchor_found` and
`neighbor_recall` in the per-task CSV row).
| Repo | Task | Anchor found | Rank | Neighbor recall | Score |
|---|---|---|---:|---:|---:|
| code-review-graph | crg-parse-file-callers | yes | 0 | 1.00 | **1.00** |
| code-review-graph | crg-upsert-node-callers | yes | 4 | 1.00 | **1.00** |
| express | express-create-application-callees | yes | 1 | 1.00 | **1.00** |
| fastapi | fastapi-route-handler-callers | yes | 6 | 1.00 | **1.00** |
| fastapi | fastapi-get-dependant-callers | no | — | 0.00 | **0.00** |
| flask | flask-dispatch-callers | yes | 3 | 1.00 | **1.00** |
| flask | flask-exception-callers | yes | 5 | 1.00 | **1.00** |
| gin | gin-serve-http-callees | yes | 5 | 1.00 | **1.00** |
| gin | gin-context-next-callers | yes | 0 | 1.00 | **1.00** |
| httpx | httpx-client-request-callers | yes | 0 | 1.00 | **1.00** |
| httpx | httpx-async-request-tests | yes | 7 | 1.00 | **1.00** |
**Average score across 11 tasks: 0.909**. 10/11 tasks pass; the one remaining
miss (`fastapi-get-dependant-callers`) targets a function spelled `get_dependant`
("dependant" with an `a`) from a query phrased as "dependency declarations into
a tree" — there is no lexical overlap and no extractable identifier in the
query for the boosting heuristic to lock onto. Left as an honest miss; the
fix would be either query rewriting or a richer embedding model.
#### How the score went from 0.545 to 0.909 (the same-day fix)
The v1 scaffold first scored **0.545** (6/11). Two changes brought it to
**0.909** (10/11), both deterministic, both small, both committed in this
same session:
1. **`embeddings.py:_node_to_text`** — the embedded text per node used to be
just `"{name} {kind} in {parent}"`. It now also includes the dotted form
(`APIRoute.get_route_handler`), the identifier split into words
(`get route handler`), and the enclosing module directory (`routing`,
`fastapi`, `dependencies`). All re-embeddings are automatic — the text
hash changes, `EmbeddingStore.embed_nodes` re-embeds. See
`_split_identifier` for the casing/separator rules.
2. **`search.py:extract_query_identifiers`** — natural-language queries
like "Who advances the gin middleware chain via Context.Next" now have
their dotted / snake_case / CamelCase identifier tokens extracted. Search
results whose `qualified_name` contains any extracted identifier get a
2.0× boost. This pushed `Context.Next` from rank 11 to rank 0.
The remaining `fastapi-get-dependant-callers` failure cannot be fixed by
either change because the query doesn't share any identifier or substring
with the target — that's the boundary of the heuristic.
This benchmark is a v1 scaffold (11 tasks). The intent is to track the
**multi-hop tool chain** as the agent's actual usage pattern rather than just
single-shot retrieval. Adding more tasks: append `multi_hop_tasks:` entries
to any config under `code_review_graph/eval/configs/*.yaml` with the schema:
```yaml
multi_hop_tasks:
- id: my-task-id # required, unique
nl_query: "natural language" # required, what an agent would ask
anchor_qualified_suffix: # required, lowercased suffix of expected
"rel/path.py::owner.symbol" # qualified_name (case-insensitive endswith)
traversal_pattern: callers_of # one of callers_of|callees_of|imports_of|
# importers_of|tests_for|inheritors_of|children_of
expected_neighbor_names: # required, list of bare names that should
- "expected_one" # appear in the traversal result
k: 10 # optional, top-K depth for the search step
```
### Build stats
| Repo | Nodes | Edges | Flows | Communities | Embeddings | FTS idx rows |
|---|---:|---:|---:|---:|---:|---:|
| fastapi | 6,292 | 32,081 | 165 | 85 | 5,164 | 127 |
| express | 1,912 | 18,877 | 4 | 7 | 1,771 | 47 |
| gin | 1,589 | 17,237 | 114 | 41 | 1,491 | 29 |
| code-review-graph | 1,418 | 8,877 | 104 | 11 | 1,326 | 38 |
| flask | 1,415 | 8,259 | 78 | 13 | 1,329 | 35 |
| httpx | 1,261 | 8,228 | 128 | 5 | 1,193 | 34 |
Embeddings count is lower than node count because File nodes aren't
embedded. FTS idx rows are far lower than node count because FTS5 stores
inverted-index segments, not one row per indexed document.
<!-- END canonical-stats -->
## Agent baseline benchmark (`code_review_graph/eval/benchmarks/agent_baseline.py`)
The whole-corpus baseline in the standalone token benchmark is an upper
bound no real agent pays. This benchmark simulates what an agent actually
does without the graph:
1. Derive search terms from each question in the config's `agent_questions:`
list (identifier-shaped tokens via `search.extract_query_identifiers`,
plus plain keywords; falls back to the `search_queries` query strings
when absent).
2. Pure-python grep over the corpus (no external `rg`/`grep` binary),
ranking source files by total case-insensitive match count
(deterministic; ties break on path).
3. Read the top-3 files and token-count them (`chars/4`) as
`baseline_tokens`.
4. Compare against the graph-query cost for the same question (5 hybrid
search hits + up to 5 neighbor edges per hit — the same accounting as the
standalone benchmark).
Output: `evaluate/results/<repo>_agent_baseline_<date>.csv` with a
`baseline_to_graph_ratio` per question. Rows where either side is zero are
marked `status=no_graph_results` / `status=no_baseline_match` and excluded
from aggregates (`agent_baseline.aggregate`). No canonical capture exists
yet; numbers will be added to the canonical block above once captured —
they are not quoted before being measured.
## Weekly CI run (report-only)
`.github/workflows/eval.yml` runs every Monday at 06:23 UTC (plus manual
`workflow_dispatch`) against the two smallest pinned configs (`httpx`,
`flask`) with the `token_efficiency`, `impact_accuracy`, and
`agent_baseline` benchmarks. It uploads the CSVs as an artifact and writes
a job-summary table. It is deliberately **report-only**: regressions do not
fail the default branch yet.
## Which benchmark measures what
There are four different "token" benchmarks in the repo. They are all valid
but measure different scenarios:
| Benchmark | Naive baseline | Graph cost | Question answered |
|---|---|---|---|
| `code_review_graph/eval/benchmarks/token_efficiency.py` | sum of **changed-file content** for a specific commit | full `get_review_context()` JSON | "Is the graph cheaper than just reading the diffed files?" |
| `code_review_graph/eval/benchmarks/agent_baseline.py` | **grep top-3 files** for the question's identifiers | 5 search hits + 5 neighbor edges per question | "Is the graph cheaper than a realistic grep-and-read agent?" |
| `code_review_graph/eval/token_benchmark.py` | none — absolute per-workflow cost | sum of 5 MCP-tool responses | "How many tokens does a complete agent workflow cost?" |
| `code_review_graph/token_benchmark.py` (standalone) | sum of **all source files** in repo | 5 search hits + 5 neighbor edges per question | "Is the graph cheaper than reading the whole repo?" |
The `code_review_graph/eval/benchmarks/token_efficiency.py` numbers can be **less than 1.0×**
for small commits (`get_review_context` carries impact-radius metadata and
source snippets, which outweigh a tiny changed-file set). The standalone
benchmark numbers are **always large** because the baseline is the entire
repo — that is why the README leads with the median (~82×) and treats 528×
as the max, and why `agent_baseline` exists as the realistic middle ground.
Pick the one that matches the scenario you're talking about.
## Generating diagrams
The 9 diagrams in `diagrams/` are produced from `diagrams/generate_diagrams.py`.
Excalidraw source files (`.excalidraw`) are gitignored (`*.excalidraw` line in
`.gitignore`); only the rendered PNGs are tracked. Regenerate after a
benchmark refresh:
```bash
uv run python diagrams/generate_diagrams.py
# Open each .excalidraw at https://excalidraw.com to render/export
```
## Troubleshooting
**`git clone failed`** — Network or upstream rate-limit. The fix is a clean
retry; the eval doesn't auto-retry by design (loud failures > silent
fallback).
**`git checkout <sha> failed`** — Upstream rewrote history or removed the
SHA. File an issue with the failing config so we can re-pin.
**`No embeddings found in this graph`** warning during the standalone
benchmark — you skipped Step 3. Run it.
**Different community IDs between runs** — Make sure you're on the seeded
`communities.py`. Check `grep _LEIDEN_SEED code_review_graph/communities.py`.
You can override the seed via `CRG_LEIDEN_SEED=<int>` but all collaborators
must agree on the same value.
**Different `naive_corpus_tokens` than the canonical table** — Make sure
`git rev-parse HEAD` inside each `evaluate/test_repos/<name>` matches the
`commit:` field in the corresponding config file. If not, delete the clone
and let Step 2 re-clone at the pinned SHA.
+114
View File
@@ -0,0 +1,114 @@
# Roadmap
## Shipped
### v2.3.6
- **Custom languages without forking**: `.code-review-graph/languages.toml` maps extensions and node types to any tree-sitter-language-pack grammar (`docs/CUSTOM_LANGUAGES.md`)
- **GitHub Action** for risk-scored PR review comments: graph built/restored on the CI runner, sticky comment upserted per push, optional `fail-on-risk` merge gate; dogfooded via `.github/workflows/pr-review.yml` (`docs/GITHUB_ACTION.md`)
- **`agent_baseline` benchmark**: graph queries vs a realistic grep-and-read-top-k agent baseline, wired into all six pinned eval configs
- **Co-change ground truth** for `impact_accuracy`; the legacy graph-derived metric is labelled as a circular upper bound
- **Weekly eval CI**: report-only cron run of the two smallest configs (`.github/workflows/eval.yml`)
- **`docs/FAQ.md`**: comparisons with LSP, RAG, grep/agentic search, and adjacent tools, plus when-not-to-use guidance
- **Contribution scaffolding**: issue forms, PR template, dependabot config
- **Windows fixes** for `daemon status` (#511) and `detect-changes` path mapping (#528)
- **Reliability**: embedding provider-name validation, SQLite store-leak fixes in analysis/wiki tools, `fastmcp<4` cap, hooks installed via `git rev-parse --git-path hooks`
### v2.3.5
- **Token Savings panel** on `detect-changes --brief` and the new `update --brief` — boxed CLI output with per-category breakdown that sums exactly to the graph response size
- **`--verify` flag** cross-checks the displayed savings against OpenAI's `cl100k_base` tokenizer; calibration data committed in `docs/REPRODUCING.md` shows the estimate is within ~1% of real GPT-4 tokens in aggregate
- **`code-review-graph embed`** CLI subcommand for explicit embedding generation
- **Deterministic eval pipeline**: pinned upstream SHAs in every config, full clones with `returncode` checks, fixed-seed Leiden community detection (`CRG_LEIDEN_SEED`)
- **`multi_hop_retrieval` benchmark**: 11 curated 2-step tool-chain tasks; average score 0.909
- **Richer embedding text** and **identifier-aware search boost** lift multi-hop accuracy from 0.545 to 0.909
- **Path normalization fix** in the eval pipeline + test-gap dedup in the brief summary
- **`docs/REPRODUCING.md`**: end-to-end recipe with canonical numbers and tiktoken calibration table
- Demo GIF (`diagrams/context-savings-demo.gif`) showing both CLI surfaces and `--verify`
### v2.3.4
- 30 MCP tools and 5 MCP prompts
- Estimated context savings metadata for review, impact, detect-changes, and compact architecture responses
- Compact architecture overview by default to reduce large MCP payloads
- Bounded change-analysis controls for large diffs (`CRG_MAX_CHANGED_FUNCS`, `CRG_MAX_TRANSITIVE_FRONTIER`, `CRG_TOOL_TIMEOUT`)
- Windows FastMCP semantic-search deadlock mitigation
- Rust test detection and path lookup correctness fixes
- Documentation and release metadata refreshed for the 2.3.4 release
### v2.3.3
- Broad parser surface expansion across source languages, shell scripts, notebooks, and SFC-style files
- Additional AI coding platform install targets including Gemini CLI, Qwen, Kiro, Qoder, and GitHub Copilot variants
- Streamable HTTP MCP transport on localhost
- Parser/resolver, Windows, FastMCP, and daemon reliability fixes
- Community PR sweep and VS Code accessibility improvements
### v2.2.0
- Multi-repo watch daemon (`crg-daemon` / `code-review-graph daemon`)
- TOML-based daemon configuration (`~/.code-review-graph/watch.toml`)
- Child process management: one `code-review-graph watch` process per repo
- Config file watching with automatic reconciliation of watcher processes
- Daemonization with PID file management
- Health checking with automatic restart of dead watchers
- Standalone `crg-daemon` CLI entry point (7 subcommands)
- Integrated `daemon` subcommand group in main CLI
### v2.0.0
- 22 MCP tools (up from 9) and 5 MCP prompts
- 18 languages (added Dart, R, Perl)
- Execution flow detection with criticality scoring
- Community detection (Leiden algorithm via igraph, file-based fallback)
- Architecture overview with coupling warnings
- Risk-scored change detection (`detect_changes`)
- Refactoring tools (rename preview, dead code, suggestions)
- Wiki generation from community structure
- Multi-repo registry with cross-repo search
- FTS5 full-text search with porter stemming
- Database migrations (v1-v5)
- Evaluation framework with matplotlib visualization
- TypeScript tsconfig path alias resolution
- MiniMax embedding provider (embo-01)
- Optional dependency groups: `[embeddings]`, `[google-embeddings]`, `[communities]`, `[eval]`, `[wiki]`, `[all]`
- 486 tests across 22 test files
### v1.8.4
- Multi-word AND search, call target resolution, impact radius pagination
- `find_large_functions_tool`, Vue SFC and Solidity support
- Documentation overhaul
### v1.7.0
- `install` command as primary entry point (`init` kept as alias)
- `--dry-run` flag for previewing install/init changes
- Automatic PyPI publishing via GitHub Actions on release
- README rewrite with real benchmark data from httpx, FastAPI, and Next.js
### v1.6.x
- Portable `uvx`-based MCP config
- SessionStart hook for automatic graph tool preference
- 24 audit fixes: C/C++ support, performance, CI hardening
### v1.5.x
- Generated files in `.code-review-graph/` directory
- Visualization density: collapsed start, search, edge toggles
- Works without git
### v1.4.0
- `init` command, interactive D3.js visualization, `serve` command
### v1.3.0
- Universal pip install, CLI entry point, Python version check
### v1.1.0-v1.2.0
- Watch mode, vector embeddings, logging, CI coverage
### v1.0.0 (Foundation)
- Persistent SQLite knowledge graph, Tree-sitter parsing, incremental updates
- Impact radius analysis, 6 MCP tools, 3 skills
## Planned
- GitHub App / bot mode beyond the shipped GitHub Action (org-wide install, check runs)
- Team sync (shared graph via git-tracked DB)
- Performance optimization for monorepos (>50k files)
## Ongoing
- Additional language grammars as requested
- Integration updates as AI coding platforms evolve
+190
View File
@@ -0,0 +1,190 @@
# Troubleshooting
## Quick reference for common install/setup problems
Four issues account for most support questions. Check these first:
### 1. `Hooks use a matcher + hooks array` error in `.claude/settings.json`
**You're on a pre-v2.2.3 release.** v2.2.1 and v2.2.2 shipped a broken hook schema — flat `{matcher, command, timeout}` entries without the required nested `hooks: []` array, timeouts in milliseconds instead of seconds, and a `PreCommit` event that isn't a real Claude Code event. PR #208 (shipped in v2.2.3) rewrote the generator to emit the correct v1.x+ schema.
**Fix:**
```bash
pip install --upgrade code-review-graph # → v2.2.4 or later
cd /path/to/your/project
code-review-graph install # rewrites .claude/settings.json
```
The re-install merge-replaces the entire broken `hooks` block with the new nested format and drops a real git pre-commit hook into the hooks directory resolved via `git rev-parse --git-path hooks` — typically `.git/hooks/pre-commit`, but linked worktrees and `core.hooksPath` (husky) setups are handled too. That's where "check before commit" lives in v2.2.3+, not in Claude Code settings.
Valid Claude Code hook events are: `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`, `SubagentStop`, `SessionStart`, `SessionEnd`, `PreCompact`, `Notification`. There is no `PreCommit`.
### 2. `code-review-graph: command not found` after `pip install`
`pip install` put the console script into a `bin/` directory that isn't on your `$PATH`. Four fixes, in order of recommendation:
**Option 1 — Use `pipx` (cleanest):**
```bash
pip uninstall code-review-graph
pipx install code-review-graph
```
`pipx` installs CLI tools in an isolated venv. If the command is not found afterwards, run `pipx ensurepath` or add `~/.local/bin` to your PATH.
**Option 2 — Use `uvx` (no install needed):**
```bash
uvx code-review-graph install
uvx code-review-graph build
```
**Option 3 — Run it as a Python module (always works):**
```bash
python -m code_review_graph install
python -m code_review_graph build
```
**Option 4 — Fix PATH manually:**
```bash
pip show code-review-graph | grep Location
# Find the sibling `bin/` directory; on macOS user installs this is
# typically ~/Library/Python/3.X/bin. Add it to your shell rc:
echo 'export PATH="$HOME/Library/Python/3.12/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
```
### 3. Is code-review-graph project-scoped or user-scoped?
**Both** — four different pieces, each scoped differently:
| Piece | Scope | Where |
|-------------------------------|----------------|------------------------------------------------------------------|
| The Python package | User-scoped | Install once via `pip`/`pipx`/`uvx` |
| The graph database | Project-scoped | `.code-review-graph/graph.db` inside each project |
| MCP server config (`.mcp.json`) | Project-scoped | Claude Code launches one MCP server per project, with `cwd=<project>` |
| Multi-repo registry | User-scoped | `~/.code-review-graph/registry.json` (only for `cross_repo_search`) |
**TL;DR**: install the tool **once**, then run `code-review-graph install && code-review-graph build` inside **each** project you want graph-aware reviews in.
### 4. Using a venv? You must update `settings.json` manually
Claude Code hooks and MCP tool paths in `.claude/settings.json` are **hardcoded at install time**. If you switch to (or create) a virtual environment after running `code-review-graph install`, the paths will still point to the old interpreter and the server will silently fail or use the wrong Python.
**Fix — update the `command`/`args` in `.mcp.json` and any hook commands in `.claude/settings.json` to match your venv:**
```json
// .mcp.json — point to your venv's Python or uvx inside the venv
{
"mcpServers": {
"code-review-graph": {
"command": "/path/to/your/venv/bin/uvx",
"args": ["code-review-graph", "serve"]
}
}
}
```
Or simply re-run `code-review-graph install` **from within the activated venv** so the paths are regenerated correctly:
```bash
source .venv/bin/activate # activate your venv first
code-review-graph install # rewrites .mcp.json and hook paths
```
Then fully quit and reopen Claude Code so it picks up the new config.
### 5. "I built the graph but Claude Code doesn't see it in a new session"
Most likely causes, ranked:
1. **You didn't restart Claude Code after `install`.** Claude Code reads `.mcp.json` at startup — if you ran `install` in one session, fully quit and reopen Claude Code for the MCP server to register.
2. **New session's `cwd` is a different directory.** The MCP server is launched with `cwd=<project>` and it reads `.code-review-graph/graph.db` from there. If your new session opened in a parent folder or a different project, it won't find the graph you built.
3. **You ran `build` but not `install`.** `build` creates `graph.db`; `install` is what registers the MCP server with Claude Code via `.mcp.json`. You need both.
4. **MCP server is crashing on startup.** Run `/mcp` inside Claude Code to see server status, or check `~/Library/Logs/Claude/mcp*.log` on macOS.
**Quick checklist:**
```bash
cd /path/to/your/project
code-review-graph status # should print Files/Nodes/Edges from the built graph
ls .mcp.json # should exist
cat .mcp.json # should reference `code-review-graph serve`
# then: fully quit Claude Code and reopen it inside this project
```
If `status` shows the graph but `/mcp` in the new session doesn't list `code-review-graph`, the `.mcp.json` isn't in the session's `cwd` — re-run `code-review-graph install` from the correct project root.
---
## Database lock errors
The graph uses SQLite with WAL mode. If you see lock errors:
- Ensure only one build process runs at a time
- The database auto-recovers; just retry
- Delete `.code-review-graph/graph.db-wal` and `.code-review-graph/graph.db-shm` if corrupt
## Large repositories (>10k files)
- First build may take 30-60 seconds
- Subsequent incremental updates are fast (<2s)
- Add more ignore patterns to `.code-review-graphignore`:
```
generated/**
vendor/**
*.min.js
```
## Missing nodes after build
- Check that the file's language is supported (see [FEATURES.md](FEATURES.md))
- Check that the file isn't matched by an ignore pattern
- Run with `full_rebuild=True` to force a complete re-parse
## Graph seems stale
- Hooks auto-update on edit/commit
- If stale, run `/code-review-graph:build-graph` manually
- Check that hooks are configured in `.claude/settings.json` (re-run `code-review-graph install` to regenerate)
## Embeddings not working
- Install with: `pip install code-review-graph[embeddings]`
- Run `embed_graph_tool` to compute vectors
- First embedding run downloads the model (~90MB, one time)
## MCP server won't start
- Verify `uv` is installed (`uv --version`; install with `pip install uv` or `brew install uv`)
- Check that `uvx code-review-graph serve` runs without errors
- If using a custom `.mcp.json`, ensure it uses `"command": "uvx"` with `"args": ["code-review-graph", "serve"]`
- Re-run `code-review-graph install` to regenerate the config
## Windows / WSL
- Upgrade to v2.3.6+ if `daemon status` crashes with WinError 87 (#511) or CLI `detect-changes` maps 0 functions on Windows (#528) — both are fixed there
- Use forward slashes in paths when passing `repo_root` to MCP tools
- In WSL, ensure `uv` is installed inside WSL (not the Windows version): `curl -LsSf https://astral.sh/uv/install.sh | sh`
- If `uv` is not found after install, add `~/.cargo/bin` to your PATH
- File watching (`code-review-graph watch`) may have delays on WSL1 due to filesystem event limitations; WSL2 is recommended
- On Windows native (non-WSL), long path support may need to be enabled: `git config --system core.longpaths true`
## Community detection requires igraph
- Install with: `pip install code-review-graph[communities]`
- Without igraph, community detection falls back to file-based grouping (less precise but functional)
## Wiki generation with LLM summaries
- Install with: `pip install code-review-graph[wiki]`
- Requires a running Ollama instance for LLM-powered summaries
- Without Ollama, wiki pages are generated with structural information only (no prose summaries)
## Optional dependency groups
If a tool returns an ImportError, install the relevant optional group:
- `pip install code-review-graph[embeddings]` for semantic search
- `pip install code-review-graph[google-embeddings]` for Google Gemini embeddings
- OpenAI-compatible and MiniMax embeddings use stdlib HTTP clients and require only their environment variables
- `pip install code-review-graph[communities]` for igraph-based community detection
- `pip install code-review-graph[enrichment]` for Python call-resolution enrichment via Jedi
- `pip install code-review-graph[eval]` for evaluation benchmarks (matplotlib)
- `pip install code-review-graph[wiki]` for wiki LLM summaries (ollama)
- `pip install code-review-graph[all]` for everything
+154
View File
@@ -0,0 +1,154 @@
# Code Review Graph — User Guide
**Applies to:** v2.3.6
## Installation
```bash
pip install code-review-graph
code-review-graph install # auto-detects and configures all supported platforms
code-review-graph build # parse your codebase
```
`install` detects which AI coding tools you have, writes the correct MCP configuration for each one, and installs platform-native hooks where supported. Restart your editor/tool after installing.
To target a specific platform instead of auto-detecting all:
```bash
code-review-graph install --platform codex
code-review-graph install --platform cursor
code-review-graph install --platform claude-code
```
### Supported Platforms
| Platform | Config file |
|----------|-------------|
| **Codex** | `~/.codex/config.toml` + `~/.codex/hooks.json` |
| **Claude Code** | `.mcp.json` + `.claude/settings.json` |
| **Cursor** | `.cursor/mcp.json` |
| **Windsurf** | `~/.codeium/windsurf/mcp_config.json` |
| **Zed** | `.zed/settings.json` |
| **Continue** | `.continue/config.json` |
| **OpenCode** | `.opencode.json` |
| **Antigravity** | `~/.gemini/antigravity/mcp_config.json` |
| **Gemini CLI** | `.gemini/settings.json` |
| **Qwen Code** | `~/.qwen/settings.json` |
| **Kiro** | `.kiro/settings/mcp.json` |
| **Qoder** | `.qoder/mcp.json` |
| **GitHub Copilot** | `.vscode/mcp.json` |
| **GitHub Copilot CLI** | `~/.copilot/mcp-config.json` |
## Core Workflow
### 1. Build the graph (first time only)
```
/code-review-graph:build-graph
```
Parses your entire codebase. Takes ~10s for 500 files.
### 2. Review changes (daily use)
```
/code-review-graph:review-delta
```
Reviews only files changed since last commit plus the graph-derived impact radius. Relevant review and impact responses include compact estimated `context_savings` metadata. Across the 6 benchmark repositories, graph queries use ~82x fewer tokens per question (median; range 38x528x) than reading the whole corpus — see the [README benchmarks](../README.md#benchmarks) and [REPRODUCING.md](REPRODUCING.md) for the methodology.
### 3. Review a PR
```
/code-review-graph:review-pr
```
Comprehensive structural review of a branch diff with blast-radius analysis.
### 4. Watch mode (optional)
```bash
code-review-graph watch
```
Auto-updates the graph on every file save. Zero manual work.
### 5. Visualize the graph (optional)
```bash
code-review-graph visualize
open .code-review-graph/graph.html
```
Interactive D3.js force-directed graph. Starts collapsed (File nodes only) — click a file to expand its children. Use the search bar to filter, and click legend edge types to toggle visibility.
### 6. Semantic search (optional)
```bash
pip install "code-review-graph[embeddings]"
```
Then use `embed_graph_tool` to compute vectors. `semantic_search_nodes_tool` automatically uses vector similarity when matching embeddings are available and falls back to keyword/FTS search otherwise.
Embedding providers are local sentence-transformers, OpenAI-compatible endpoints, Google Gemini, and MiniMax. Local embeddings use `CRG_EMBEDDING_MODEL`; OpenAI-compatible providers use `CRG_OPENAI_BASE_URL`, `CRG_OPENAI_API_KEY`, and `CRG_OPENAI_MODEL`. Cloud providers are opt-in and print an egress warning unless `CRG_ACCEPT_CLOUD_EMBEDDINGS=1` is set.
### 7. Detect changes with risk scoring (v2)
```
Ask your MCP client: "Review my recent changes with risk scoring"
```
Uses `detect_changes_tool` to map diffs to affected functions, flows, communities, and test gaps.
### 8. Explore architecture (v2)
```
Ask your MCP client: "Show me the architecture of this project"
```
Uses `get_architecture_overview_tool` for community-based architecture map with coupling warnings.
### 9. Generate wiki (v2)
```bash
code-review-graph wiki
```
Creates markdown wiki pages for each detected community in `.code-review-graph/wiki/`.
### 10. Multi-repo search (v2)
```bash
code-review-graph register /path/to/other/repo --alias mylib
```
Then use `cross_repo_search_tool` to search across all registered repositories.
## Context Savings
CRG reduces review context by sending graph-derived structural context instead of broad file dumps. The exact reduction depends on the repository and change shape. The evaluation runner reports the current benchmark data used in the README:
```bash
code-review-graph eval --all
```
Since v2.3.4, review and impact tools include compact `context_savings` metadata. In v2.3.5 the CLI surfaces this as a boxed `Token Savings` panel on both `detect-changes --brief` and `update --brief`, with a per-category breakdown (Functions / Tests / Risk / Other) that sums exactly to the graph response size. Add `--verify` to cross-check the displayed numbers against OpenAI's `cl100k_base` tokenizer (requires `pip install tiktoken`). All numbers are labelled estimated because they use a conservative approximation rather than model-specific tokenisation; calibration shows the estimate stays within ~1% of real GPT-4 tokens in aggregate. Small single-file changes can occasionally use more context than the raw file because graph metadata has overhead.
## Supported Languages
The parser currently covers Python, JavaScript, TypeScript/TSX, Go, Rust, Java, C/C++, C#, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, Vue/Svelte single-file components, Astro files parsed through the TypeScript parser, Jupyter/Databricks notebooks (`.ipynb`), and Perl XS files (`.xs`).
Extension-less scripts are detected by shebang for common bash/sh/zsh/ksh/dash/ash, Python, Node, Ruby, Perl, Lua, Rscript, and PHP interpreters.
Languages not covered yet can be added without a fork via a `.code-review-graph/languages.toml` config — see [CUSTOM_LANGUAGES.md](CUSTOM_LANGUAGES.md).
## What Gets Indexed
- **Nodes**: Files, Classes, Functions/Methods, Types, Tests
- **Edges**: CALLS, IMPORTS_FROM, INHERITS, IMPLEMENTS, CONTAINS, TESTED_BY, DEPENDS_ON
See [schema.md](schema.md) for full details.
## Ignore Patterns
By default, these paths are excluded from indexing:
```
.code-review-graph/** node_modules/** .git/**
__pycache__/** *.pyc .venv/**
venv/** dist/** build/**
.next/** target/** *.min.js
*.min.css *.map *.lock
package-lock.json yarn.lock *.db
*.sqlite *.db-journal
```
To add custom patterns, create a `.code-review-graphignore` file in your repo root (same syntax as `.gitignore`):
```
generated/**
vendor/**
*.generated.ts
```
In git repos, indexing is based on tracked files (`git ls-files`), so gitignored files are skipped automatically. Use `.code-review-graphignore` to exclude tracked files or when git isn't available.
+120
View File
@@ -0,0 +1,120 @@
# Architecture
## System Overview
`code-review-graph` is a local-first code intelligence graph exposed through a CLI and MCP server. It maintains a persistent, incrementally updated knowledge graph of a codebase so AI coding tools can review changes with structural context instead of reading broad file dumps. Claude Code is supported, but it is one client among several.
## Component Diagram
```
┌──────────────────────────────────────────────────────────────┐
│ AI coding clients / CLI │
│ │
│ MCP clients Hooks / watch mode │
│ ├── Codex └── incremental update │
│ ├── Claude Code │
│ ├── Cursor, Windsurf, Zed, Continue │
│ └── Gemini CLI, Qwen, Qoder, Copilot, OpenCode │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────────────────────────────────────┐ │
│ │ MCP Server (stdio or localhost HTTP) │ │
│ │ │ │
│ │ 30 MCP Tools + 5 MCP Prompts │ │
│ │ ├── Core: build, impact, query, review, │ │
│ │ │ search, traverse, embed, stats, docs │ │
│ │ ├── Flows: list, get, affected │ │
│ │ ├── Communities: list, get, architecture │ │
│ │ ├── Analysis: detect_changes, refactor, │ │
│ │ │ apply_refactor, hotspots, gaps │ │
│ │ ├── Wiki: generate, get_page │ │
│ │ └── Multi-repo: list_repos, cross_search │ │
│ └────────────────┬───────────────────────────┘ │
└───────────────────┼──────────────────────────────────────────┘
┌───────────┼───────────────┐
▼ ▼ ▼
┌─────────┐ ┌─────────┐ ┌─────────────┐
│ Parser │ │ Graph │ │ Incremental │
│ │ │ Store │ │ Engine │
└────┬────┘ └────┬────┘ └──────┬──────┘
│ │ │
▼ ▼ ▼
Tree-sitter SQLite DB git/svn diff
grammars (.code-review- subprocess
graph/
graph.db)
```
## Data Flow
### Full Build
1. `collect_all_files()` gathers tracked files (`git ls-files`) and applies `.code-review-graphignore` (gitignored files are skipped automatically when git is available)
2. For each file, `CodeParser.parse_file()` uses Tree-sitter to extract AST
3. AST walker identifies structural nodes (classes, functions, imports) and edges (calls, inheritance)
4. `GraphStore.store_file_nodes_edges()` persists to SQLite with file hash for change detection
5. Metadata updated with timestamp
### Incremental Update
1. `get_changed_files()` uses VCS metadata to identify changed files (git diff by default, with SVN support in the incremental layer)
2. `find_dependents()` queries the graph for files importing the changed files
3. Changed + dependent files are re-parsed (others skipped via hash comparison)
4. Only affected rows in SQLite are updated
### Review Context Generation
1. Changed files identified (git diff or explicit list)
2. `get_impact_radius()` performs BFS from changed nodes through the graph
3. Source snippets extracted for changed areas only
4. Review guidance generated (test coverage gaps, wide blast radius warnings)
5. Assembled into a structured, token-efficient context for MCP clients and the CLI
6. Where a cheap baseline can be estimated, compact `context_savings` metadata is attached as an estimate rather than an exact tokenisation
## Storage
### SQLite Schema
- **nodes** table: id, kind, name, qualified_name, file_path, line_start/end, language, community_id, etc.
- **edges** table: id, kind, source_qualified, target_qualified, file_path, line
- **metadata** table: key-value pairs (last_updated, build_type, schema_version)
- **flows** table: id, name, entry_point_id, depth, node_count, file_count, criticality, path_json
- **flow_memberships** table: flow_id, node_id, position
- **communities** table: id, name, level, parent_id, cohesion, size, dominant_language, description
- **nodes_fts** (FTS5 virtual table): full-text search on name, qualified_name, file_path, signature
- **community_summaries**, **flow_snapshots**, **risk_index** tables: compact precomputed summaries for token-efficient queries
- **embeddings** table (separate DB): qualified_name, vector, text_hash, provider
Indexes on qualified_name, file_path, edge source/target, criticality, community_id, and cohesion for fast lookups.
WAL mode enabled for concurrent read access during updates.
### Qualified Names
Nodes are uniquely identified by qualified names:
- Files: absolute path (e.g., `/repo/src/auth.py`)
- Functions: `file_path::function_name` (e.g., `/repo/src/auth.py::authenticate`)
- Methods: `file_path::ClassName.method_name` (e.g., `/repo/src/auth.py::AuthService.login`)
## Parsing Strategy
Tree-sitter provides language-agnostic AST access. The parser:
1. Walks the AST recursively
2. Pattern-matches on node types (language-specific mappings in `_CLASS_TYPES`, `_FUNCTION_TYPES`, etc.)
3. Extracts names, parameters, return types, base classes
4. Identifies calls within function bodies
5. Resolves imports to module paths
This approach is more robust than tree-sitter queries across grammar versions.
## Visualization
The `visualization.py` module generates an interactive D3.js force-directed graph as a self-contained HTML file. It reads all nodes and edges from the SQLite graph store and renders them in the browser, allowing developers to visually explore code relationships, filter by node kind, and inspect dependencies.
## Impact Analysis Algorithm
BFS from seed nodes (changed files' contents):
1. Seed = all qualified names in changed files
2. For each node in frontier:
- Follow forward edges (what this node affects)
- Follow reverse edges (what depends on this node)
3. Expand up to `max_depth` hops (default: 2)
4. Collect all reached nodes as "impacted"
This captures both downstream effects (things that call changed code) and upstream context (things that the changed code depends on).
+276
View File
@@ -0,0 +1,276 @@
# Knowledge Graph Schema
## Node Types
### File
Represents a source code file.
| Property | Type | Description |
|----------|------|-------------|
| name | string | Absolute file path |
| file_path | string | Same as name for File nodes |
| language | string | Detected language (python, typescript, go, etc.) |
| line_start | int | Always 1 |
| line_end | int | Total line count |
| file_hash | string | SHA-256 of file contents (for change detection) |
### Class
Represents a class, struct, interface, enum, or module definition.
| Property | Type | Description |
|----------|------|-------------|
| name | string | Class name |
| file_path | string | File containing the class |
| line_start | int | Definition start line |
| line_end | int | Definition end line |
| language | string | Source language |
| parent_name | string? | Enclosing class (for nested classes) |
| modifiers | string? | Access modifiers (public, abstract, etc.) |
### Function
Represents a function, method, or constructor definition.
| Property | Type | Description |
|----------|------|-------------|
| name | string | Function name |
| file_path | string | File containing the function |
| line_start | int | Definition start line |
| line_end | int | Definition end line |
| language | string | Source language |
| parent_name | string? | Enclosing class (for methods) |
| params | string? | Parameter list as source text |
| return_type | string? | Return type annotation |
| is_test | bool | Whether this is a test function |
### Test
Same schema as Function, but `kind = "Test"` and `is_test = true`. Identified by:
- Name starts with `test_` or `Test`
- Name ends with `_test` or `_spec`
- File matches test file patterns (`test_*.py`, `*.test.ts`, `*_test.go`, etc.)
- Language-specific test markers where supported, such as common Rust test attributes
### Type
Represents a type alias, interface, enum, struct-like type, or parser-specific type construct where the language exposes one.
| Property | Type | Description |
|----------|------|-------------|
| name | string | Type name |
| file_path | string | File containing the type |
| line_start | int | Definition start line |
| line_end | int | Definition end line |
## Edge Types
### CALLS
A function calls another function.
| Property | Type | Description |
|----------|------|-------------|
| source | string | Qualified name of the caller |
| target | string | Name of the called function (may be unqualified) |
| file_path | string | File where the call occurs |
| line | int | Line number of the call |
### IMPORTS_FROM
A file imports from another module or file.
| Property | Type | Description |
|----------|------|-------------|
| source | string | Importing file path |
| target | string | Imported module/path |
| file_path | string | Same as source |
| line | int | Line number of the import |
### INHERITS
A class extends/inherits from another class.
| Property | Type | Description |
|----------|------|-------------|
| source | string | Child class qualified name |
| target | string | Parent class name |
| file_path | string | File containing the child class |
### IMPLEMENTS
A class implements an interface (Java, C#, TypeScript, Go).
| Property | Type | Description |
|----------|------|-------------|
| source | string | Implementing class |
| target | string | Interface name |
### CONTAINS
Structural containment: a file contains a class, a class contains a method.
| Property | Type | Description |
|----------|------|-------------|
| source | string | Container (file path or class qualified name) |
| target | string | Contained node qualified name |
### TESTED_BY
A function is tested by a test function.
| Property | Type | Description |
|----------|------|-------------|
| source | string | Function being tested |
| target | string | Test function qualified name |
### DEPENDS_ON
General dependency relationship (used for non-specific dependencies).
### REFERENCES
A value-level reference to another symbol, often used for function-as-value patterns such as callback maps, arrays, or assignment.
### INJECTS
A dependency-injection relationship, currently used by Java/Spring enrichment for injected fields and constructor parameters.
### CONSUMES / PRODUCES
Data or event flow relationships emitted by specialised parsers when a source consumes or produces a named resource.
### TEMPORAL_STUB
Temporal dependency placeholder emitted by specialised parsers when a time/order relationship is detected but cannot be resolved to a stronger edge type.
## Qualified Name Format
Nodes are uniquely identified by qualified names:
```
# File node
/absolute/path/to/file.py
# Top-level function
/absolute/path/to/file.py::function_name
# Method in a class
/absolute/path/to/file.py::ClassName.method_name
# Nested class method
/absolute/path/to/file.py::OuterClass.InnerClass.method_name
```
## SQLite Tables
```sql
-- Nodes table
CREATE TABLE nodes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
kind TEXT NOT NULL,
name TEXT NOT NULL,
qualified_name TEXT NOT NULL UNIQUE,
file_path TEXT NOT NULL,
line_start INTEGER,
line_end INTEGER,
language TEXT,
parent_name TEXT,
params TEXT,
return_type TEXT,
modifiers TEXT,
is_test INTEGER DEFAULT 0,
file_hash TEXT,
extra TEXT DEFAULT '{}',
community_id INTEGER,
updated_at REAL NOT NULL
);
-- Edges table
CREATE TABLE edges (
id INTEGER PRIMARY KEY AUTOINCREMENT,
kind TEXT NOT NULL,
source_qualified TEXT NOT NULL,
target_qualified TEXT NOT NULL,
file_path TEXT NOT NULL,
line INTEGER DEFAULT 0,
extra TEXT DEFAULT '{}',
confidence REAL DEFAULT 1.0,
confidence_tier TEXT DEFAULT 'EXTRACTED',
updated_at REAL NOT NULL
);
-- Metadata table
CREATE TABLE metadata (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
-- Flows table (v2.0)
CREATE TABLE flows (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
entry_point_id INTEGER NOT NULL,
depth INTEGER NOT NULL,
node_count INTEGER NOT NULL,
file_count INTEGER NOT NULL,
criticality REAL NOT NULL DEFAULT 0.0,
path_json TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Flow memberships table (v2.0)
CREATE TABLE flow_memberships (
flow_id INTEGER NOT NULL,
node_id INTEGER NOT NULL,
position INTEGER NOT NULL,
PRIMARY KEY (flow_id, node_id)
);
-- Communities table (v2.0)
CREATE TABLE communities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
level INTEGER NOT NULL DEFAULT 0,
parent_id INTEGER,
cohesion REAL NOT NULL DEFAULT 0.0,
size INTEGER NOT NULL DEFAULT 0,
dominant_language TEXT,
description TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- Full-text search virtual table (v2.0)
CREATE VIRTUAL TABLE nodes_fts USING fts5(
name, qualified_name, file_path, signature,
content='nodes', content_rowid='rowid',
tokenize='porter unicode61'
);
-- Token-efficient summary tables (v6)
CREATE TABLE community_summaries (
community_id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
purpose TEXT DEFAULT '',
key_symbols TEXT DEFAULT '[]',
risk TEXT DEFAULT 'unknown',
size INTEGER DEFAULT 0,
dominant_language TEXT DEFAULT ''
);
CREATE TABLE flow_snapshots (
flow_id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
entry_point TEXT NOT NULL,
critical_path TEXT DEFAULT '[]',
criticality REAL DEFAULT 0.0,
node_count INTEGER DEFAULT 0,
file_count INTEGER DEFAULT 0
);
CREATE TABLE risk_index (
node_id INTEGER PRIMARY KEY,
qualified_name TEXT NOT NULL,
risk_score REAL DEFAULT 0.0,
caller_count INTEGER DEFAULT 0,
test_coverage TEXT DEFAULT 'unknown',
security_relevant INTEGER DEFAULT 0,
last_computed TEXT DEFAULT ''
);
-- Embeddings table, stored in the embeddings database
CREATE TABLE embeddings (
qualified_name TEXT PRIMARY KEY,
vector BLOB NOT NULL,
text_hash TEXT NOT NULL,
provider TEXT NOT NULL DEFAULT 'unknown'
);
```
Indexes include qualified-name, file-path, node-kind, edge source/target/kind, community, flow criticality, risk score, compound edge lookup indexes, and the composite edge upsert index.