## File: README.md

<h1 align="center">code-review-graph</h1>












AI coding tools can end up re-reading large parts of your codebase on review tasks. `code-review-graph` fixes that. It builds a structural map of your code with [Tree-sitter](https://tree-sitter.github.io/tree-sitter/), tracks changes incrementally, and gives your AI assistant precise context via [MCP](https://modelcontextprotocol.io/) so it reads only what matters.



---

## Quick Start

```bash
pip install code-review-graph                     # or: pipx install code-review-graph
code-review-graph install          # auto-detects and configures all supported platforms
code-review-graph build            # parse your codebase
```

One command sets up everything. `install` detects which AI coding tools you have, writes the correct MCP configuration for each one, installs platform-native hooks/skills where supported, and injects graph-aware instructions into your platform rules. It auto-detects whether you installed via `uvx` or `pip`/`pipx` and generates the right config. Restart your editor/tool after installing.



To target a specific platform:

```bash
code-review-graph install --platform codex       # configure only Codex
code-review-graph install --platform cursor      # configure only Cursor
code-review-graph install --platform claude-code  # configure only Claude Code
code-review-graph install --platform gemini-cli   # configure only Gemini CLI
code-review-graph install --platform antigravity   # configure only Antigravity
code-review-graph install --platform windsurf     # configure only Windsurf
code-review-graph install --platform zed          # configure only Zed
code-review-graph install --platform continue     # configure only Continue
code-review-graph install --platform opencode     # configure only OpenCode
code-review-graph install --platform qwen         # configure only Qwen
code-review-graph install --platform qoder        # configure only Qoder
code-review-graph install --platform kiro         # configure only Kiro
code-review-graph install --platform copilot      # configure only GitHub Copilot (VS Code)
code-review-graph install --platform copilot-cli  # configure only GitHub Copilot CLI
code-review-graph install --platform codebuddy    # configure only CodeBuddy Code
code-review-graph install --platform hermes       # configure only Hermes Agent
```

Requires Python 3.10+. For the best experience, install [uv](https://docs.astral.sh/uv/) (the MCP config will use `uvx` if available, otherwise falls back to the `code-review-graph` command directly).

To remove CRG from a Git or SVN project, use the symmetric uninstall command
from anywhere inside its working tree. The target is normalized to the working
tree root, and non-repository directories are refused. It removes only
CRG-owned files and entries; unrelated MCP servers, hooks, skills, and JSONC
comments remain untouched. Shared configuration changes use atomic replacement
so a failed write leaves the original file intact.

```bash
code-review-graph uninstall --dry-run    # preview every action; write nothing
code-review-graph uninstall              # preview, ask for confirmation, then apply
code-review-graph uninstall --yes        # apply without prompting
code-review-graph uninstall --all-repos  # also clean every registered repository
code-review-graph uninstall --keep-data  # remove integrations but keep graph databases
code-review-graph uninstall --keep-user-configs --repo .  # clean this project only
```

Then open your project and ask your AI assistant:

```
Build the code review graph for this project
```

The initial build takes ~10 seconds for a 500-file project. After that, watch mode and supported hooks can keep the graph updated automatically.


## How It Works



Your repository is parsed into an AST with Tree-sitter, stored as a graph of nodes (functions, classes, imports) and edges (calls, inheritance, test coverage), then queried at review time to compute the minimal set of files your AI assistant needs to read.



### Blast-radius analysis

When a file changes, the graph traces every caller, dependent, and test that could be affected. This is the "blast radius" of the change. Your AI reads only these files instead of scanning the whole project.



### Incremental updates in seconds

When hooks or watch mode are enabled, file saves and supported commit hooks trigger incremental updates. The graph diffs changed files, finds their dependents through the graph's own import and call edges, and re-parses only the files whose SHA-256 hash actually changed. On a ~3,000-file project (django) a two-file edit re-indexes in about 2.5 seconds on the path the hooks use, of which ~1.4 s is process start-up; a no-op update costs only that start-up. See [Incremental update latency](docs/REPRODUCING.md#incremental-update-latency) for the full measurement.



### Whole codebase or targeted answer?

The bigger the repository, the more token waste hurts. Instead of feeding a whole corpus to the model, the graph returns an answer-shaped slice of it: on this repository, 208,821 source tokens become ~3,190 tokens per question.



### Broad language coverage + Jupyter notebooks



Parser support covers functions, classes, imports, call sites, inheritance, and test detection across the current parser surface, using Tree-sitter where available and targeted fallbacks where needed. Current support includes Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, VB.NET, 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, Terraform/OpenTofu structure (`.tf`; generic `.hcl` files are recognized as file nodes), Ansible playbooks/roles/tasks, Vue/Svelte SFCs, Astro files parsed through the TypeScript parser, Jupyter/Databricks notebooks (`.ipynb`), and Perl XS files (`.xs`). Generic YAML is not treated as source code.

PHP projects additionally get repository-bounded Composer PSR-4 resolution,
Blade template references, and Laravel Route/Eloquent semantic edges when the
source includes explicit framework imports, model inheritance, and receiver
evidence.

### Add your own language (no fork needed)

If your repo uses a language the parser does not cover yet, drop a `languages.toml` into `.code-review-graph/` mapping file extensions to any grammar bundled in `tree_sitter_language_pack`, plus the tree-sitter node types for functions, classes, imports, and calls:

```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"]
```

The generic tree-sitter walker handles extraction from there — no code changes, and built-in languages can never be overridden. See [docs/CUSTOM_LANGUAGES.md](docs/CUSTOM_LANGUAGES.md) for the schema reference, validation rules, and a worked end-to-end example.

### Risk-scored PR reviews in CI (GitHub Action)

The same analysis runs as a composite GitHub Action — and it stays local-first: the knowledge graph is built and queried entirely on your CI runner, with no source code sent to any external service. On each pull request the action posts a single sticky comment with risk-scored functions, affected execution flows, and test gaps, updated in place on every push. An optional `fail-on-risk` input turns the review into a merge gate.

```yaml
# .github/workflows/code-review-graph.yml
on:
  pull_request:

permissions:
  contents: read
  pull-requests: write

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: tirth8205/code-review-graph@v2.3.6
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }}
```

See [docs/GITHUB_ACTION.md](docs/GITHUB_ACTION.md) for inputs, risk levels, and caching details, or the dogfood workflow this repo runs on itself in [`.github/workflows/pr-review.yml`](.github/workflows/pr-review.yml).

---

## Benchmarks



**Headline number: the median per-question token reduction across the 6 repos is ~65x** (whole-corpus baseline vs graph query). The **376x maximum** is a single best-case repo (fastapi, the largest corpus) — not the typical result.

All numbers come from the automated evaluation runner against 6 real open-source repositories (13 commits total). Every config pins an upstream SHA, the Leiden community detector runs with a fixed seed, and embeddings are deterministic on CPU — so two runs on different machines produce identical numbers. The full reproduction recipe with expected outputs is in [`docs/REPRODUCING.md`](docs/REPRODUCING.md). A weekly report-only run on the two smallest configs lives in [`.github/workflows/eval.yml`](.github/workflows/eval.yml).


**Token efficiency: ~65x median per-question reduction (range 36x – 376x; whole-corpus vs graph query)**


For a typical agent question (`"how does authentication work"`, `"what is the main entry point"`, etc.), the graph returns ~2,000–3,500 tokens of targeted search hits + neighbor edges instead of forcing the agent to read every source file. The table below averages over the 5 sample questions defined in `code_review_graph/token_benchmark.py`.

| Repo | Snapshot SHA | naive_corpus_tokens | avg graph_tokens | Reduction |
|------|---|-----------------:|----------------:|----------:|
| fastapi | `22381558` | 948,793 | 2,653 | **375.6x** |
| flask | `a29f88ce` | 143,594 | 2,196 | **71.0x** |
| code-review-graph | `84bde354` | 208,821 | 3,190 | **68.1x** |
| gin | `5c00df8a` | 166,868 | 2,766 | **61.9x** |
| httpx | `b55d4635` | 142,356 | 2,661 | **60.6x** |
| express | `b4ab7d65` | 136,052 | 3,936 | **36.0x** |

Median per-question reduction across the 6 repos: **~65x**. The range is 36x – 376x, where **376x is the best case** (fastapi, the largest corpus), not the headline.

> Re-captured 2026-08-02 from clean clones at the pinned SHAs (crg 2.3.7, local `all-MiniLM-L6-v2` embeddings). These numbers are lower than the 2026-05-25 capture they replace: the graph response grew as node embedding text became richer, so `avg graph_tokens` rose across every repo. fastapi is now measured at its current pin `22381558` rather than the retired `0227991a`.

The whole-corpus baseline above is an upper bound no real agent pays: a competent agent greps for identifiers and reads only the best-matching files. The `agent_baseline` eval benchmark measures that realistic baseline — a pure-python grep over the corpus, top-3 files by match count, token-counted and compared to the graph query cost (`evaluate/results/<repo>_agent_baseline_*.csv`).

The formal `eval/benchmarks/token_efficiency.py` benchmark measures a different scenario — full `get_review_context()` JSON versus just the changed-file content of a commit — and reports ratios below 1 for small commits, because the review-context response carries impact-radius edges plus source snippets that exceed a tiny single-file diff. That is not a bug; the two benchmarks answer different questions. See [`docs/REPRODUCING.md`](docs/REPRODUCING.md) for the full methodology.

Since v2.3.4, review and impact tools attach a compact `context_savings` estimate so MCP clients can see the approximate context saved per call. In v2.3.5 the CLI surfaces this as the boxed `Token Savings` panel shown above (see "Token Savings panel" in the Usage section) and adds `--verify` to cross-check against OpenAI's `cl100k_base` tokenizer. Calibration data in [`docs/REPRODUCING.md`](docs/REPRODUCING.md) shows the estimate is within ~1% of real GPT-4 tokens in aggregate across 222 sample files.




**Impact accuracy: 0.69 average F1 against graph-derived ground truth (recall 1.0 is a circular upper bound, not "100% recall")**


Blast-radius analysis recovers every file in the ground truth on all 13 evaluation commits — **but read that as an upper bound, not as "100% recall"**: in this mode the ground truth (changed files + files with call/import edges into them) is derived from the same graph the predictor traverses, so it is circular by construction. The over-prediction visible in the precision column is a deliberate trade-off: better to flag too many files than miss a broken dependency.

| Repo | Commits | Avg F1 | Avg Precision | Recall (graph-derived upper bound) |
|------|--------:|-------:|--------------:|-------:|
| httpx | 2 | 0.863 | 0.785 | 1.0 |
| code-review-graph | 2 | 0.734 | 0.584 | 1.0 |
| fastapi | 2 | 0.697 | 0.539 | 1.0 |
| express | 2 | 0.667 | 0.500 | 1.0 |
| flask | 2 | 0.633 | 0.485 | 1.0 |
| gin | 3 | 0.609 | 0.439 | 1.0 |
| **Average** | **13** | **0.693** | **0.546** | **1.000** |

The benchmark also runs an honest **co-change mode**: the predictor is seeded with a single changed file and graded against the *other* files the author actually touched in the same commit — independent-ish evidence from git history, not from the graph. Both modes appear side by side in the result CSVs (`ground_truth_mode` column). As of the 2026-08-02 capture that mode returns `predicted_files = 0` on every graded commit, so it is not yet a usable measurement and no co-change number is quoted here — the harness needs fixing before the mode says anything about accuracy.




**Build performance**


| Repo | Files | Nodes | Edges | Flow Detection | Search Latency |
|------|------:|------:|------:|---------------:|---------------:|
| express | 141 | 1,910 | 17,553 | 106ms | 0.7ms |
| fastapi | 1,122 | 6,285 | 27,117 | 128ms | 1.5ms |
| flask | 83 | 1,446 | 7,974 | 95ms | 0.7ms |
| gin | 99 | 1,286 | 16,762 | 111ms | 0.5ms |
| httpx | 60 | 1,253 | 7,896 | 96ms | 0.4ms |



### Limitations and known weaknesses

- **Impact "recall 1.0" is graph-derived and circular:** the historical ground truth comes from the same graph edges the predictor walks, so it is an upper bound by construction. The honest co-change mode (grade against files actually co-changed in the same commit) is measured alongside it; expect those numbers to be substantially lower.
- **Small single-file changes:** Graph context can exceed naive file reads for trivial edits (see express results above). The overhead is the structural metadata that enables multi-file analysis.
- **Search quality (MRR 0.35):** Keyword search finds the right result in the top-4 for most queries, but ranking needs improvement. Express queries return 0 hits due to module-pattern naming.
- **Flow detection (33% recall):** Framework and conventional entry patterns are strongest for Python and PHP/Laravel. JavaScript and Go flow detection needs work.
- **Precision vs recall trade-off:** Impact analysis is deliberately conservative. It flags files that *might* be affected, which means some false positives in large dependency graphs.

---

## Features

| Feature | Details |
|---------|---------|
| **Incremental updates** | Re-parses only the files whose hash changed. On a ~3,000-file repo a two-file edit takes ~2.5s on the hook path ([measured](docs/REPRODUCING.md#incremental-update-latency)). |
| **Broad language + notebook support** | Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, VB.NET, 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, Terraform/OpenTofu structure (`.tf`; generic `.hcl` files are file-only), Ansible playbooks/roles/tasks, Vue/Svelte SFCs, Astro files parsed through the TypeScript parser, Jupyter/Databricks (.ipynb), and Perl XS (.xs) |
| **Framework-aware PHP parsing** | Repository-bounded Composer PSR-4 imports, Blade template references, and evidence-gated Laravel Route-to-controller and Eloquent relationship edges |
| **Blast-radius analysis** | Shows which functions, classes, and files are likely affected by a change |
| **Auto-update hooks** | Hooks and watch mode can update the graph on file saves and supported commit hooks |
| **Semantic search** | Optional vector embeddings via sentence-transformers, Google Gemini, MiniMax, or any OpenAI-compatible endpoint (real OpenAI, Azure, new-api, LiteLLM, vLLM, LocalAI) |
| **Interactive visualisation** | D3.js force-directed graph with search, community legend toggles, and degree-scaled nodes |
| **Hub & bridge detection** | Find most-connected nodes and architectural chokepoints via betweenness centrality |
| **Surprise scoring** | Detect unexpected coupling: cross-community, cross-language, peripheral-to-hub edges |
| **Knowledge gap analysis** | Identify isolated nodes, untested hotspots, thin communities, and structural weaknesses |
| **Suggested questions** | Auto-generated review questions from graph analysis (bridges, hubs, surprises) |
| **Edge confidence** | Three-tier confidence scoring (EXTRACTED/INFERRED/AMBIGUOUS) with float scores on edges |
| **Graph traversal** | Free-form BFS/DFS exploration from any node with configurable depth and token budget |
| **Export formats** | GraphML (Gephi/yEd), Neo4j Cypher, Obsidian vault with wikilinks, SVG static graph |
| **Graph diff** | Compare graph snapshots over time: new/removed nodes, edges, community changes |
| **Token benchmarking** | Measure naive full-corpus tokens vs graph query tokens with per-question ratios |
| **Estimated context savings** | Compact `context_savings` metadata on relevant MCP/CLI review outputs, labelled as estimated and kept to three small fields |
| **Memory loop** | Persist Q&A results as markdown for re-ingestion, so the graph grows from queries |
| **Community auto-split** | Oversized communities (>25% of graph) are recursively split via Leiden |
| **Execution flows** | Trace call chains from entry points, sorted by weighted criticality |
| **Community detection** | Cluster related code via Leiden algorithm with resolution scaling for large graphs |
| **Architecture overview** | Auto-generated architecture map with coupling warnings |
| **Risk-scored reviews** | `detect_changes` maps diffs to affected functions, flows, and test gaps |
| **Custom languages** | Add new languages via `.code-review-graph/languages.toml` — no fork or code changes needed |
| **GitHub Action** | Sticky risk-scored PR review comments in CI, with an optional `fail-on-risk` merge gate |
| **Refactoring tools** | Rename preview, framework-aware dead code detection, community-driven suggestions |
| **Wiki generation** | Auto-generate markdown wiki from community structure |
| **Multi-repo registry** | Register multiple repos, search across all of them |
| **Multi-repo daemon** | `crg-daemon` watches multiple repos as child processes, with health checks and auto-restart |
| **MCP prompts** | 5 workflow templates: review, architecture, debug, onboard, pre-merge |
| **Full-text search** | FTS5-powered hybrid search combining keyword and vector similarity |
| **Local storage** | SQLite file in `.code-review-graph/`. Core graph storage needs no external database or cloud service. |
| **Watch mode** | Continuous graph updates as you work |

---

## Usage


**Slash commands**


| Command | Description |
|---------|-------------|
| `/code-review-graph:build-graph` | Build or rebuild the code graph |
| `/code-review-graph:review-delta` | Review changes since last commit |
| `/code-review-graph:review-pr` | Full PR review with blast-radius analysis |




**CLI reference**


```bash
code-review-graph install          # Auto-detect and configure all platforms
code-review-graph install --platform <name>  # Target a specific platform
code-review-graph uninstall --dry-run  # Preview safe removal of installed artifacts
code-review-graph build            # Parse entire codebase
code-review-graph update           # Incremental update (changed files only)
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 json      # Export local graph data as JSON
code-review-graph visualize --format graphml   # Export as GraphML
code-review-graph visualize --format svg       # Export as SVG
code-review-graph visualize --format obsidian  # Export as Obsidian vault
code-review-graph visualize --format cypher    # Export as Neo4j Cypher
code-review-graph wiki             # Generate markdown wiki from communities
code-review-graph detect-changes --brief         # Risk panel + token savings (read-only)
code-review-graph update --brief                 # Refresh graph + same panel
code-review-graph detect-changes --brief --verify  # Cross-check vs tiktoken
code-review-graph register   # Register repo in multi-repo registry
code-review-graph unregister   # Remove repo from registry
code-review-graph repos            # List registered repositories
code-review-graph daemon start     # Start multi-repo watch daemon
code-review-graph daemon stop      # Stop the daemon
code-review-graph daemon status    # Show daemon status and repos
code-review-graph eval             # Run evaluation benchmarks
code-review-graph serve            # Start MCP server
```

JSON exports stay inside the local graph data directory, which Git ignores by
default. They can contain absolute paths and code-structure metadata, so inspect
and sanitize an export before publishing it outside your machine.




**Token Savings panel: `detect-changes --brief` vs `update --brief`**


Both commands print the same compact panel showing how many tokens the
graph saved you compared to handing the changed files to an agent raw.
They differ in **one** thing: whether the graph gets refreshed first.

```text
┌─────────────────────── Token Savings ────────────────────────┐
│ Full context would be:     12,921 tokens                     │
│ Graph context used:           762 tokens                     │
│ Saved:                     12,159 tokens (~94%)              │
│ Breakdown: Functions 244 · Tests 191 · Risk 244 · Other 83   │
└──────────────────────────────────────────────────────────────┘
```

| Command | What it does | When to use |
|---|---|---|
| `detect-changes --brief` | **Read-only.** Looks at your current changes, queries the **existing** graph, prints the panel. ~1 sec. | Most of the time — the hooks (or `crg-daemon`) keep the graph fresh in the background, so this is enough. |
| `update --brief` | **Re-parses your changed files into the graph first**, then prints the same panel. ~5 sec. | After a rebase, a large change set, or any time you suspect the graph is stale. |

Both end with the **same panel** because both call the same `analyze_changes()` step at the end. The difference is whether the graph itself got refreshed before that analysis ran.

Add `--verify` to either command to cross-check the displayed numbers against OpenAI's `cl100k_base` tokenizer (the GPT-4 family). Requires `pip install tiktoken`. The estimate stays within ~1% of real tokens on a typical change set — see [`docs/REPRODUCING.md`](docs/REPRODUCING.md) for the calibration data.

The same `context_savings` metadata is also attached automatically to the JSON responses of `get_impact_radius`, `get_review_context`, `detect_changes`, and `get_architecture_overview` MCP tools, so AI agents can surface the savings to humans in chat without any extra prompting.




**Multi-repo daemon**


If your editor doesn't support hooks (e.g. Cursor, OpenCode), or you just want your
graph to stay fresh in the background without any editor integration, the daemon is
for you. It watches your repos for file changes and automatically rebuilds the graph
— no manual `build` or `update` commands needed.

The daemon is included with `code-review-graph` — no separate install required.

**Quick setup:**

```bash
# 1. Register the repos you want to watch
crg-daemon add ~/project-a --alias proj-a
crg-daemon add ~/project-b

# 2. Start the daemon (runs in the background)
crg-daemon start

# 3. That's it — graphs stay up to date automatically
crg-daemon status                 # check daemon and per-repo watcher status
crg-daemon logs --repo proj-a -f  # tail logs for a specific repo
crg-daemon stop                   # stop daemon and all watcher processes
```

Also available as `code-review-graph daemon start|stop|status|...`.

Under the hood, `crg-daemon add` writes to a TOML config file at
`~/.code-review-graph/watch.toml`. You can also edit this file directly:

```toml
[[repos]]
path = "/home/user/project-a"
alias = "proj-a"

[[repos]]
path = "/home/user/project-b"
alias = "project-b"
```

The daemon monitors this config file for changes and automatically starts/stops
watcher processes as repos are added or removed. Health checks every 30 seconds
restart dead watchers. No external dependencies required.

See [docs/COMMANDS.md](docs/COMMANDS.md#standalone-daemon-cli-crg-daemon) for the
full config reference and all available options.




**30 MCP tools**


Your AI assistant uses these automatically once the graph is built.

| Tool | Description |
|------|-------------|
| `build_or_update_graph_tool` | Build or incrementally update the graph |
| `run_postprocess_tool` | Re-run flow detection, community detection, and FTS indexing |
| `get_minimal_context_tool` | Ultra-compact context (~100 tokens) — call this first |
| `get_impact_radius_tool` | Blast radius of changed files |
| `get_review_context_tool` | Token-optimised review context with structural summary |
| `query_graph_tool` | Callers, callees, tests, imports, inheritance queries |
| `traverse_graph_tool` | BFS/DFS traversal from any node with token budget |
| `semantic_search_nodes_tool` | Search code entities by name or meaning |
| `embed_graph_tool` | Compute vector embeddings for semantic search |
| `list_graph_stats_tool` | Graph size and health |
| `get_docs_section_tool` | Retrieve documentation sections |
| `find_large_functions_tool` | Find functions/classes exceeding a line-count threshold |
| `list_flows_tool` | List execution flows sorted by criticality |
| `get_flow_tool` | Get details of a single execution flow |
| `get_affected_flows_tool` | Find flows affected by changed files |
| `list_communities_tool` | List detected code communities |
| `get_community_tool` | Get details of a single community |
| `get_architecture_overview_tool` | Architecture overview from community structure |
| `detect_changes_tool` | Risk-scored change impact analysis for code review |
| `get_hub_nodes_tool` | Find most-connected nodes (architectural hotspots) |
| `get_bridge_nodes_tool` | Find chokepoints via betweenness centrality |
| `get_knowledge_gaps_tool` | Identify structural weaknesses and untested hotspots |
| `get_surprising_connections_tool` | Detect unexpected cross-community coupling |
| `get_suggested_questions_tool` | Auto-generated review questions from analysis |
| `refactor_tool` | Rename preview, dead code detection, suggestions |
| `apply_refactor_tool` | Apply a previously previewed refactoring |
| `generate_wiki_tool` | Generate markdown wiki from communities |
| `get_wiki_page_tool` | Retrieve a specific wiki page |
| `list_repos_tool` | List registered repositories |
| `cross_repo_search_tool` | Search across all registered repositories |

**MCP Prompts** (5 workflow templates):
`review_changes`, `architecture_map`, `debug_issue`, `onboard_developer`, `pre_merge_check`




**Configuration**


To exclude paths from indexing, create a `.code-review-graphignore` file in your repository root:

```
generated/**
*.generated.ts
vendor/**
node_modules/**
```

Note: in git repos, only tracked files are indexed (`git ls-files`), so gitignored files are skipped automatically. Use `.code-review-graphignore` to exclude tracked files or when git isn't available.

Optional dependency groups:

```bash
pip install "code-review-graph[embeddings]"          # Local vector embeddings (sentence-transformers)
pip install "code-review-graph[google-embeddings]"   # Google Gemini embeddings
pip install "code-review-graph[communities]"         # Community detection (igraph)
pip install "code-review-graph[enrichment]"          # Python call-resolution enrichment (Jedi)
pip install "code-review-graph[eval]"                # Evaluation benchmarks (matplotlib)
pip install "code-review-graph[wiki]"                # Wiki generation with LLM summaries (ollama)
pip install "code-review-graph[all]"                 # All optional dependencies
```

### Environment Variables

| Variable | Description | Default |
|----------|-------------|---------|
| `CRG_GIT_TIMEOUT` | Timeout in seconds for Git operations | `30` |
| `CRG_DATA_DIR` | Override directory for graph databases and generated graph artefacts | - |
| `CRG_EMBEDDING_MODEL` | Default model for vector embeddings | `all-MiniLM-L6-v2` |
| `CRG_ACCEPT_CLOUD_EMBEDDINGS` | Suppress the cloud embedding egress warning after explicit acknowledgement | - |
| `CRG_ALLOW_REMOTE_CODE` | Allow HuggingFace models that require `trust_remote_code=True` | `0` |
| `CRG_MAX_IMPACT_NODES` | Maximum nodes to include in impact analysis | `500` |
| `CRG_MAX_IMPACT_DEPTH` | Search depth for blast-radius analysis | `2` |
| `CRG_MAX_BFS_DEPTH` | Maximum depth for graph traversal | `15` |
| `CRG_MAX_CHANGED_FUNCS` | Maximum changed functions analysed in one change report | `500` |
| `CRG_MAX_TRANSITIVE_FRONTIER` | Maximum frontier size for transitive caller/callee expansion | `50` |
| `CRG_TOOL_TIMEOUT` | Optional timeout in seconds for bounded MCP tools (`0` disables timeout) | `0` |
| `CRG_RECURSE_SUBMODULES` | Include git submodules in file collection when set to `1`, `true`, or `yes` | - |
| `CRG_TOOLS` | Comma-separated allowlist of MCP tools to expose when serving | - |
| `GOOGLE_API_KEY` | API key for Google Gemini embeddings | - |
| `MINIMAX_API_KEY` | API key for MiniMax embeddings | - |
| `VOYAGE_API_KEY` | API key for Voyage embeddings | - |
| `CRG_VOYAGE_MODEL` | Model name for Voyage embeddings | `voyage-code-3` |
| `CRG_VOYAGE_OUTPUT_DIMENSION` | Output dimension for Voyage embeddings | `1024` |
| `CRG_VOYAGE_OUTPUT_DTYPE` | Output dtype for Voyage embeddings | `float` |
| `CRG_VOYAGE_BASE_URL` | Voyage embeddings endpoint | `https://api.voyageai.com/v1` |
| `CRG_VOYAGE_BATCH_SIZE` | Batch size for Voyage embedding requests | `100` |
| `CRG_VOYAGE_MIN_INTERVAL_SEC` | Minimum delay between Voyage requests | `0` |
| `CRG_OPENAI_BASE_URL` | OpenAI-compatible embeddings endpoint | - |
| `CRG_OPENAI_API_KEY` | API key for OpenAI-compatible embeddings | - |
| `CRG_OPENAI_MODEL` | Model name for OpenAI-compatible embeddings | - |
| `CRG_OPENAI_DIMENSION` | Pin embedding dimension (v3 models support reduction) | - |
| `NO_COLOR` | If set, disables ANSI colors in terminal | - |
| `CRG_SERIAL_PARSE` | If `1`, disables parallel parsing (use for debugging) | - |

OpenAI-compatible embeddings (real OpenAI, Azure, or any self-hosted gateway like
new-api / LiteLLM / vLLM / LocalAI / Ollama in openai mode) need no extra install —
just set the environment variables and pass `provider="openai"` to `embed_graph`:

```bash
export CRG_OPENAI_BASE_URL=http://127.0.0.1:3000/v1     # or https://api.openai.com/v1
export CRG_OPENAI_API_KEY=sk-...
export CRG_OPENAI_MODEL=text-embedding-3-small          # whatever your gateway serves
# optional:
export CRG_OPENAI_DIMENSION=1536                        # pin dim (v3 models support reduction)
export CRG_OPENAI_BATCH_SIZE=100                        # lower for gateways with tight limits
                                                        # (e.g. Qwen text-embedding-v4 caps at 10)
```

The cloud-egress warning is auto-skipped when the base URL points to localhost
(`127.0.0.1`, `localhost`, `0.0.0.0`, `::1`).

Voyage embeddings need no extra install. Set `VOYAGE_API_KEY` and pass
`provider="voyage"` to `embed_graph`; the default model is `voyage-code-3`:

```bash
export VOYAGE_API_KEY=pa-...
export CRG_ACCEPT_CLOUD_EMBEDDINGS=1
code-review-graph embed --provider voyage --model voyage-code-3
```

> **Model selection tip.** Avoid `-preview` / `-beta` / `-exp` model IDs
> (e.g. `google/gemini-embedding-2-preview`) for anything you plan to keep
> long-term — preview models can change weights (different dimension → full
> re-embed required) or be deprecated without notice. Prefer stable GA
> releases such as `text-embedding-3-small` / `text-embedding-3-large` (OpenAI),
> `Qwen/Qwen3-Embedding-8B` (via self-hosted vLLM / LocalAI), or
> `gemini-embedding-001` (via the native Gemini provider, which requires
> `GOOGLE_API_KEY` instead of the OpenAI-compatible path).
>
> `code-review-graph` embeds identifiers, signatures, structural context, and a
> bounded first-paragraph docstring/doc-comment summary. It does not transmit
> function bodies. Graphs created before documentation extraction was added
> need one full `code-review-graph build` before re-embedding so every file is
> reparsed. Routine builds never refresh embeddings by default. To refresh an
> existing index after a build, explicitly pass both `--embedding-provider`
> and `--embedding-model`; cloud choices may transmit this source-derived text
> and incur API cost.

#### Tool Filtering

CRG exposes 30 MCP tools by default. In token-constrained environments, you can
limit the server to a subset of tools using `--tools` or the `CRG_TOOLS`
environment variable:

```bash
# Via CLI flag
code-review-graph serve --tools query_graph_tool,semantic_search_nodes_tool,detect_changes_tool

# Via environment variable
CRG_TOOLS=query_graph_tool,semantic_search_nodes_tool code-review-graph serve
```

The CLI flag takes precedence over the environment variable. When neither is set,
all tools are available. This is especially useful for MCP client configurations:

```json
{
  "mcpServers": {
    "code-review-graph": {
      "command": "code-review-graph",
      "args": ["serve", "--tools", "query_graph_tool,semantic_search_nodes_tool,detect_changes_tool,get_review_context_tool"]
    }
  }
}
```



---

## FAQ & how it compares

Short, honest answers in [docs/FAQ.md](docs/FAQ.md):

- [vs LSP / language servers](docs/FAQ.md#how-is-this-different-from-lsp-and-language-servers) — one persistent cross-language graph instead of per-language daemons; LSP stays more precise per symbol.
- [vs RAG / embeddings](docs/FAQ.md#isnt-this-just-rag) — structural edges parsed from the AST, not similarity chunks; embeddings are optional and only assist search.
- [vs grep / agentic search](docs/FAQ.md#why-not-just-grep) — grep wins on one-hop lookups; the graph wins on multi-hop questions (impact radius, callers-of-callers, tests-for, affected flows).
- [vs Serena, codegraph, claude-context, repomix](docs/FAQ.md#how-does-it-compare-to-serena-codegraph-claude-context-and-repomix) — factual comparison table.
- [When NOT to use it](docs/FAQ.md#when-should-i-not-use-it) — small repos, trivial single-file diffs, one-off questions.
- [Does it phone home?](docs/FAQ.md#does-it-phone-home) — no; zero telemetry, cloud embeddings are opt-in.
- [How do I verify it is working?](docs/FAQ.md#how-do-i-verify-it-is-working) — `status`, `detect-changes --brief`, `/mcp`.

## Troubleshooting

### `pip` / `pipx` cannot download `hatchling` (or `Errno 9` / `Bad file descriptor` to PyPI)

Installing from a **source tree** (for example `pipx install .`) needs build dependencies from **PyPI** (for example `hatchling`). If you see `Could not find a version that satisfies the requirement hatchling` after connection warnings, the Python/pip in that **terminal** may not be able to open an HTTPS client to `pypi.org` (sometimes seen in an integrated editor terminal; less often system-wide with VPN, firewall, or proxy).

**Options:**

1. Run the same command from **macOS Terminal.app** (or iTerm) instead of the IDE’s terminal, then retry `pipx install .` or `pipx install "git+https://..."` .
2. Use **[uv](https://docs.astral.sh/uv/)** to install the CLI from a checkout (uses different download machinery than `pip` in many cases):

   ```bash
   cd /path/to/code-review-graph
   uv tool install . --force
   ```

3. For **development in a clone** without a global install, use `uv sync` and `uv run code-review-graph …` (or activate `.venv` after `uv sync`).

**Diagnose (optional):** `python3 scripts/diagnose_pypi_connectivity.py` — if it prints `FAILED`, the issue is environment/network, not a wrong package name in this repo.

### Windows Configuration Issues (Invalid JSON / Connection Closed)
If you are using Windows and encounter `Invalid JSON: EOF while parsing` or `MCP error -32000: Connection closed` when connecting via Claude Code, do not use the `cmd /c` wrapper in your config.

Ensure `fastmcp` is updated to at least `3.2.4+`. Then, configure your `~/.claude.json` to execute the `.exe` directly and pass the UTF-8 environment variable via the config:

```json
"code-review-graph": {
  "command": "C:\\path\\to\\your\\venv\\Scripts\\code-review-graph.exe",
  "args": ["serve", "--repo", "C:\\path\\to\\your\\project"],
  "env": { "PYTHONUTF8": "1" }
}
```

## Contributing

```bash
git clone https://github.com/tirth8205/code-review-graph.git
cd code-review-graph
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest
```


**Adding a new language**


Edit `code_review_graph/parser.py` and add your extension to `EXTENSION_TO_LANGUAGE` along with node type mappings in `_CLASS_TYPES`, `_FUNCTION_TYPES`, `_IMPORT_TYPES`, and `_CALL_TYPES`. Include a test fixture and open a PR.



## Licence

MIT. See [LICENSE](LICENSE).

---

## File: code-review-graph-vscode/README.md

# Code Review Graph for VS Code

Visualize code dependencies, blast radius, and review context from your code graph -- directly in VS Code.

## Features

- **Code Graph Explorer** -- Browse files, classes, functions, and their relationships in a tree view
- **Blast Radius** -- See which files and symbols are impacted when you change code
- **Review Changes** -- Automatically detect git changes and show their blast radius
- **Find Callers / Callees** -- Trace all callers or callees of any function
- **Find Tests** -- Locate tests for any symbol
- **Query Graph** -- Run semantic queries (callers, callees, imports, inheritance, tests) with 8 patterns
- **Find Large Functions** -- Identify functions or classes exceeding a line-count threshold
- **Interactive Graph** -- Force-directed D3.js visualization of your code dependencies
- **Live Search** -- Fuzzy search across your entire code graph with instant results
- **Compute Embeddings** -- Generate vector embeddings for semantic search
- **Watch Mode** -- Continuous graph updates as you work
- **Auto-Update** -- Graph rebuilds in the background when you save files

## Quick Start

### 1. Install the Extension

Install **Code Review Graph** from the VS Code Marketplace.

### 2. Install the Backend

The extension requires the `code-review-graph` Python CLI to parse your codebase.

```bash
# Recommended
uv pip install code-review-graph

# Alternatives
pipx install code-review-graph
pip install code-review-graph
```

Requires Python 3.10+.

### 3. Build Your Graph

Open the Command Palette (`Ctrl+Shift+P`) and run **Code Graph: Build Graph**.

The graph database is stored locally at `.code-review-graph/graph.db` and updates automatically on file save.

## Commands

| Command | Description |
|---|---|
| `Code Graph: Build Graph` | Parse the codebase and create the graph database |
| `Code Graph: Update Graph` | Incrementally update the graph |
| `Code Graph: Show Blast Radius` | Show the blast radius for a symbol |
| `Code Graph: Review Changes` | Analyze git changes and show impacted files |
| `Code Graph: Find Callers` | Find all callers of a function |
| `Code Graph: Find Callees` | Find all functions called by a target |
| `Code Graph: Find Tests` | Find tests for a symbol |
| `Code Graph: Find Large Functions` | Find functions/classes exceeding a size threshold |
| `Code Graph: Query Graph` | Run semantic queries (8 patterns: callers_of, callees_of, etc.) |
| `Code Graph: Search` | Search the code graph |
| `Code Graph: Show Graph` | Open the interactive graph visualization |
| `Code Graph: Compute Embeddings` | Generate vector embeddings for semantic search |
| `Code Graph: Watch Mode` | Run graph in watch mode for continuous updates |

## Settings

| Setting | Default | Description |
|---|---|---|
| `codeReviewGraph.cliPath` | `""` | Path to the CLI binary. Leave empty to use the bundled version or one found on `PATH`. |
| `codeReviewGraph.autoUpdate` | `true` | Auto-update the graph on file save. |
| `codeReviewGraph.blastRadiusDepth` | `2` | Max traversal depth for blast radius (1--10). |
| `codeReviewGraph.graphTheme` | `"auto"` | Graph color theme: `auto`, `light`, or `dark`. |
| `codeReviewGraph.graph.maxNodes` | `500` | Max nodes in the graph visualization (10--5000). |
| `codeReviewGraph.graph.defaultEdges` | All except CONTAINS | Edge types shown by default. |
| `codeReviewGraph.treeView.showFiles` | `true` | Show file nodes in the tree view. |
| `codeReviewGraph.treeView.showClasses` | `true` | Show class nodes in the tree view. |
| `codeReviewGraph.treeView.showFunctions` | `true` | Show function nodes in the tree view. |
| `codeReviewGraph.treeView.showTypes` | `true` | Show type nodes in the tree view. |
| `codeReviewGraph.treeView.showTests` | `true` | Show test nodes in the tree view. |

## Requirements

- VS Code 1.85+
- Python 3.10+ (for the backend CLI)
- A workspace with source code to analyze

## Links

- [Main Repository](https://github.com/tirth8205/code-review-graph)
- [Report an Issue](https://github.com/tirth8205/code-review-graph/issues)

## License

MIT

---

## File: docs/architecture.md

# 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

```
/* Detailed source-code truncated for AI context efficiency. */
```

## 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()` walks outward from the changed nodes, relaxing a weighted
   best-path score per node (per-edge-kind weight × depth decay) rather than a plain BFS
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).

---

## File: docs/COMMANDS.md

# 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 | None = None              # Diff base; None auto-resolves to the last-synced commit
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, references_to, 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"
max_results: int = 100           # Minimal mode additionally caps visible results at 5
```

#### `get_review_context_tool`
```
changed_files: list[str] | None
max_depth: int = 2
include_source: bool = True
max_lines_per_file: int = 200    # Capped at 500
repo_root: str | None
base: str = "HEAD~1"
detail_level: str = "standard"   # "standard" or "minimal"
max_results: int = 50            # Graph nodes per list (max 100) and edges (max 150)
max_files: int = 25              # Files listed and given snippets (max 200)
```
Snippets share an 800-line budget across the whole response. Each list reports
its untruncated `*_total`, and `context.truncated` marks any cut.
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 provider-specific env vars)
provider: str | None # local, openai, google, minimax, voyage
detail_level: str = "standard"
```

#### `embed_graph_tool`
```
repo_root: str | None
model: str | None    # Embedding model name
provider: str | None # local, openai, google, minimax, voyage
```
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
max_steps: int = 50          # Capped at 200; flow.total_steps reports the full count
max_source_lines: int = 400  # Shared across all steps; capped at 2000
```

#### `get_affected_flows_tool`
```
changed_files: list[str] | None  # Auto-detected from VCS
base: str = "HEAD~1"
repo_root: str | None
detail_level: str = "standard"   # "standard" full step details, "minimal" metadata only
max_flows: int = 50              # 0 means "no caller limit"
```
Standard mode carries a full `steps` list per flow, so it additionally caps
visible flows at 25 and spends a shared 400-step budget across them; minimal
mode caps at 500. `total` always reports the untruncated flow count. See #849.

### Community Tools

#### `list_communities_tool`
```
sort_by: str = "size"    # size, cohesion, name
min_size: int = 0
repo_root: str | None
detail_level: str = "standard"
max_results: int = 50    # Communities returned (max 200)
max_members: int = 10    # Member names per community in standard mode (max 25)
```
Each community's `size` still reports its true member count;
`members_truncated` marks a cut member list.

#### `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
max_members: int = 25        # Member entries returned (max 25)
```

#### `get_architecture_overview_tool`
```
repo_root: str | None
detail_level: str = "minimal"    # "minimal" compact default, "standard" full detail
max_results: int = 100           # Cross-community rows and warnings (max 200)
max_members: int = 10            # Member names per community in standard mode (max 25)
```
`cross_community_edges_total` reports the untruncated row count.
Minimal responses may include compact estimated `context_savings` metadata.

### Graph Health and Architecture Tools

#### `get_hub_nodes_tool`
```
top_n: int = 10                  # Capped at 100
repo_root: str | None
detail_level: str = "standard"   # "minimal" returns name, kind, total_degree
```

#### `get_bridge_nodes_tool`
```
top_n: int = 10                  # Capped at 100
repo_root: str | None
detail_level: str = "standard"   # "minimal" returns name, kind, betweenness
```

#### `get_knowledge_gaps_tool`
```
repo_root: str | None
max_per_category: int = 15       # Entries per gap category (max 50)
detail_level: str = "standard"   # "minimal" drops file paths
```
`summary` maps each category to its untruncated count.

#### `get_surprising_connections_tool`
```
top_n: int = 15                  # Capped at 100
repo_root: str | None
detail_level: str = "standard"   # "minimal" returns source, target, kind, score
```

#### `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"
max_results: int = 25        # Changed functions, test gaps, changed files (max 100)
max_flows: int = 20          # Affected flows embedded (max 200)
```
Primary tool for code review. Maps changed files to affected functions, flows, communities, and test coverage gaps. Returns risk scores and prioritized review items.
Embedded flows carry per-flow metadata only — use `get_affected_flows_tool` for
step detail. `changed_functions_total`, `test_gaps_total`, and
`affected_flows_total` report the untruncated counts.
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
max_results: int = 50        # Edits/symbols/suggestions returned (max 150)
detail_level: str = "standard"  # "minimal" keeps identifying fields only
```
Truncating a rename preview truncates only the response: the stored preview
keeps every edit, so `apply_refactor_tool` still applies the full set.

#### `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
max_diff_files: int = 25     # Per-file diffs in a dry run (max 150)
```

### 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
max_chars: int = 20000       # Page content returned (max 80000)
```
`total_chars` reports the real page length; `truncated` marks a cut.

### Multi-Repo Tools

#### `list_repos_tool`
```
(no parameters)
```

#### `cross_repo_search_tool`
```
query: str
kind: str | None
limit: int = 20              # Results per repo
max_results: int = 50        # Merged results across all repos (max 100)
```

## Result Bounds

Every tool that returns a list is bounded, so no single MCP response can blow
a context window (see #849). The contract is uniform:

- Defaults are small. Pass the tool's cap parameter to widen up to its hard
  ceiling, or a smaller value to narrow.
- Truncation is never silent: the response reports the untruncated count
  (`total`, or a `*_total` field per list), sets `truncated: true`, and the
  summary line says how many of how many are shown.
- Cap parameters reject values below 1 and reject booleans, the same way
  `query_graph_tool`'s `max_results` does.
- Hard ceilings are enforced in code and pinned by `tests/test_token_budget.py`,
  which records the measured per-tool budget table as reviewable data.

## 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.
```
```

### `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

```
/* Detailed source-code truncated for AI context efficiency. */
```

## 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  [--alias NAME]  # Add a repository to watch.toml
crg-daemon remove      # 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.

---

## File: docs/CUSTOM_LANGUAGES.md

# Custom Languages (Bring Your Own Language)

code-review-graph ships parsers for 35+ 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. |
| `name_field` | string or list of strings | no | Ordered candidates for locating a definition's name when it is not a `name` field or a plain `identifier` child (see below). |
| `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).
- `name_field` must be a string or a list of non-empty strings (max 8
  candidates); anything else skips the entry with a warning.

### Naming definitions with `name_field`

By default the parser finds a definition's name from an `identifier`-like child
or a field literally called `name`. Many grammars keep the name elsewhere — in
a differently named field, or nested a level or two below it. When that happens
the definition is extracted **unnamed and silently dropped**. `name_field` tells
the parser where to look.

Each candidate is tried in order and resolved in two passes:

1. **Field first** — a child accessed by that tree-sitter field name. Fields are
   tried across *all* candidates before any type search, so a precise field
   always wins over a broader match.
2. **Typed descendant** — if no candidate matched a field, the first descendant
   whose *node type* equals a candidate (bounded depth). This covers names that
   sit under a fieldless wrapper.

The resolved node is then descended to its first text-bearing leaf and cleaned
(surrounding `{}`/quotes/whitespace stripped; multi-line or oversized text is
rejected). Because resolution is anchored on your configured candidates, it
never grabs an unrelated inner identifier.

```toml
[languages.bibtex]
extensions = [".bib"]
grammar = "bibtex"
class_node_types = ["entry"]
name_field = ["key"]            # @article{smith2020,...} -> "smith2020"

[languages.latex]
extensions = [".tex"]
grammar = "latex"
class_node_types = ["section", "chapter", "subsection"]
function_node_types = ["new_command_definition"]
name_field = ["name", "text", "declaration"]
#   \section{Introduction}   -> "Introduction" (via `text`)
#   \newcommand{\foo}{bar}    -> "\foo"        (via `declaration`)

[languages.markdown]
extensions = [".md"]
grammar = "markdown"
class_node_types = ["section"]
name_field = ["inline"]         # "# My Heading" -> "My Heading" (typed descendant)
```

Use a **list** when a grammar's node types keep their names in different places
(LaTeX `section` uses `text`, `\newcommand` uses `declaration`): the first
candidate that resolves wins. Omitting `name_field` preserves the previous
behavior exactly.

## 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.

---

## File: docs/FAQ.md

# 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, ~2.5 seconds on a ~3,000-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 (~65x median,
36x–376x 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 70 to ~1,100 files and show 36x–376x 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 a couple of 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 ` 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 ` 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.

---

## File: docs/FEATURES.md

# Features

## v2.3.6 (Current)
- **Every MCP tool response is bounded**: #849 found `get_affected_flows` returning ~247k tokens inside a workflow documented as "5 tool calls, 800 tokens total"; a sweep of the other 29 tools found the same class of bug in ten more places — `list_communities` returned 206k tokens with *default* arguments, `get_community` 134k, `get_architecture_overview` 625k in standard mode. All are now capped with a uniform contract: `total` (or a per-list `*_total`) always reports the untruncated count, `truncated` marks the cut, and the summary says how many of how many are shown. Cap parameters reject values below 1 and reject booleans. `detail_level="minimal"` was added to the analysis and refactor tools. `tests/test_token_budget.py` pins the per-tool budget table and the hard ceilings so a removed cap fails CI. See [COMMANDS.md](COMMANDS.md#result-bounds).
- **Framework-aware PHP parsing**: traits, enums, object creation, and base clauses are indexed; Composer PSR-4 resolution is longest-prefix, multi-directory, cached, and repository-bounded; Blade references ignore comments/escaped directives; Laravel Route and Eloquent edges require explicit framework/import/receiver evidence.
- **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#, VB.NET, 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, Terraform/OpenTofu structure (`.tf`; generic `.hcl` files are recognized as file nodes), Ansible playbooks/roles/tasks, Vue/Svelte SFCs, Astro files parsed through the TypeScript parser, Jupyter/Databricks notebooks, and Perl XS files. Generic YAML is not treated as source code.
- **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`

---

## File: docs/GITHUB_ACTION.md

# 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/` 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@v7
      - 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.

Self-hosted runners must be version `2.327.1` or newer. The composite action
uses Node 24-based GitHub actions, including `actions/setup-python@v6`,
`actions/cache@v6`, and the recommended `actions/checkout@v7` example.

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). |

## Outputs

| Output | Description |
|--------|-------------|
| `comment-file` | Runner-local path to the rendered markdown report. Use with `comment: false` when a separate trusted workflow will publish it. |

### Risk levels

`detect-changes` produces a 0.0–1.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
(``). 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/`, 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**: direct commenting needs `contents: read` for checkout and
  `pull-requests: write` to post the comment. In the split fork-safe setup,
  the analysis workflow needs only `contents: read`; the trusted commenter
  needs only `actions: read` and `pull-requests: write`. Grant exactly those
  permissions in each workflow.
- **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 they cannot post the comment directly. Use an
  unprivileged `pull_request` workflow with `comment: false`, upload the
  `comment-file` as an artifact, and publish it from a separate trusted
  `workflow_run` workflow. See
  [`.github/workflows/pr-review.yml`](../.github/workflows/pr-review.yml) and
  [`.github/workflows/pr-review-comment.yml`](../.github/workflows/pr-review-comment.yml).
  GitHub loads the `workflow_run` workflow from the default branch, so the
  trusted commenting half becomes active only after that workflow is merged.
  The privileged workflow must verify the source event and analyzed commit,
  extract only under `runner.temp`, cap and validate the artifact, and add its
  own sticky marker before posting. Avoid `pull_request_target` with a checkout
  of PR code because it can execute untrusted code with a privileged token
  ([details](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/)).

## Dogfooding

This repository runs the action on its own PRs via
[`.github/workflows/pr-review.yml`](../.github/workflows/pr-review.yml),
which runs the local `action.yml` without write permissions and uploads the
rendered report. The trusted
[`pr-review-comment.yml`](../.github/workflows/pr-review-comment.yml) workflow
validates that artifact and posts the sticky comment without checking out or
executing PR-controlled code.

## 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
```

---

## File: docs/INDEX.md

# 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](../code_review_graph/docs/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

---

## File: docs/LEGAL.md

# 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`, `voyage`) 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.