code-review-graph
Memory server
Created by Tirth
Published in tirth8205/code-review-graph. Publisher page
32,711 GitHub stars
What it is
Local-first code intelligence graph for MCP and CLI. Builds a persistent map of your codebase so AI coding tools read only what matters, with benchmarked context reductions on reviews and large-repo workflows.
AutoGineer overview, from the brain’s own files
code-review-graph is a local-first memory system designed to solve the token-heavy problem of AI code review. By parsing a repository into a structural map of functions, classes, imports, and tests using Tree-sitter, it stores the codebase as a graph in a local SQLite database. When an AI agent needs to review a change, the system calculates the blast radius—identifying only the relevant callers, dependents, and tests—and serves this compact context over the Model Context Protocol (MCP). This approach significantly reduces the token count required for agents to understand code impact, often by orders of magnitude. The system supports incremental updates via file system hooks and watch mode, ensuring the graph remains accurate without full re-parsing. It is designed for local execution, keeping data on the user's machine, and integrates directly with various AI coding tools and CLI environments.
How it works
Memory model: Hybrid
AutoGineer overview, from the brain’s own files
The system uses Tree-sitter to parse source code into an Abstract Syntax Tree (AST), which is then stored as nodes and edges in a local SQLite database. When a user requests a review or code analysis, the system queries this graph to determine the blast radius of a change. It then provides the AI agent with a minimal, relevant subset of the codebase rather than the entire repository. The graph is kept up-to-date incrementally; hooks and watch mode detect file changes, diff them, and re-parse only the affected files and their immediate dependents.
| Folder | Role | Files |
|---|---|---|
| .github | agent-config | 15 |
| docs | docs | 13 |
| hooks | hooks | 2 |
| scripts | scripts | 7 |
| skills | agent-instructions | 7 |
| tests | tests | 354 |
| .beads | folder | 9 |
| code-review-graph-vscode | folder | 32 |
| code_review_graph | folder | 103 |
| diagrams | folder | 12 |
| evaluate | folder | 24 |
The agent reads
- get_review_context_tool evidence: AGENTS.md
- query_graph_tool evidence: AGENTS.md
- skills/ evidence: README.md
- get_docs_section_tool evidence: README.md
- get_suggested_questions_tool evidence: README.md
The agent writes
- .gitignore evidence: CLAUDE.md
- .mcp.json evidence: README.md
- code_review_graph/cli.py evidence: README.md
- code_review_graph/parser.py evidence: README.md
Requirements
Apps: Docker
Runtimes: Python >=3.10
Databases: Neo4j, SQLite
API keys (by name): CRG_OPENAI_API_KEY, GITHUB_TOKEN, GOOGLE_API_KEY, MINIMAX_API_KEY, VOYAGE_API_KEY
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Google Antigravity, Kiro, OpenCode, Amp, Roo Code, Cline, JetBrains Junie, Devin (ex-Windsurf)
Set it up
This brain runs code automatically: 3 hooks (claude-code). Review hooks/hooks.json before opening the folder in Claude Code. No step below turns a hook on for you.
hooks/hooks.json:cat >/dev/null || true; command -v code-review-graph >/dev/null 2>&1 || exit 0; git rev-parse --git-dir >/dev/null 2>&1 && code-review-graph status --repo "$(git rev-parse --show-toplevel 2>/dev/null)" || echo 'Not a git repo, skipping'hooks/hooks.json:cat >/dev/null || true; code-review-graph build >/dev/null 2>&1 &hooks/hooks.json:command -v code-review-graph >/dev/null 2>&1 && code-review-graph hook-update; cat >/dev/null || true
- Not scanned by AutoGineerIts dependencies are installed by pip or uv when you run it and were not scanned by AutoGineer (9 declared). https://pypi.org/project/code-review-graph/
Created by Tirth. Published in tirth8205/code-review-graph. Licence: MIT License.
Usage
AutoGineer overview, from the brain’s own files
- Install the tool via pip and run the install command to configure your AI coding tools.
- Build the initial knowledge graph for your project.
- Use MCP-enabled AI agents to query the graph for impact analysis, code reviews, or architectural questions.
- Rely on automatic incremental updates triggered by file changes to keep the graph in sync.
- “Build the code review graph for this project”
- The system parses the repository and initializes the SQLite graph database for use by AI agents.
- “What is the impact of changing this function?”
- The agent uses the graph to identify and present the callers, dependents, and tests affected by the change.
Documentation by Tirth
<h1 align="center">code-review-graph</h1>
<p align="center"> <a href="https://trendshift.io/repositories/23329?utm_source=repository-badge&utm_medium=badge&utm_campaign=badge-repository-23329" target="_blank" rel="noopener noreferrer"> <img src="https://trendshift.io/api/badge/repositories/23329" alt="tirth8205%2Fcode-review-graph | Trendshift" width="250" height="55" /> </a> </p>
<p align="center"> <strong>A local code knowledge graph that gives AI coding tools precise review context over MCP.</strong> </p> <p align="center"> <a href="README.md">English</a> | <a href="README.zh-CN.md">简体中文</a> | <a href="README.ja-JP.md">日本語</a> | <a href="README.ko-KR.md">한국어</a> | <a href="README.hi-IN.md">हिन्दी</a> </p>
<p align="center"> <a href="https://pypi.org/project/code-review-graph/"><img src="https://img.shields.io/pypi/v/code-review-graph?style=flat-square&color=blue" alt="PyPI"></a> <a href="https://pepy.tech/project/code-review-graph"><img src="https://img.shields.io/pepy/dt/code-review-graph?style=flat-square" alt="Downloads"></a> <a href="https://github.com/tirth8205/code-review-graph/stargazers"><img src="https://img.shields.io/github/stars/tirth8205/code-review-graph?style=flat-square" alt="Stars"></a> <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square" alt="MIT Licence"></a> <a href="https://github.com/tirth8205/code-review-graph/actions/workflows/ci.yml"><img src="https://github.com/tirth8205/code-review-graph/actions/workflows/ci.yml/badge.svg?branch=staging" alt="CI"></a> <a href="https://www.python.org/"><img src="https://img.shields.io/badge/python-3.10%2B-blue.svg?style=flat-square" alt="Python 3.10+"></a> <a href="https://modelcontextprotocol.io/"><img src="https://img.shields.io/badge/MCP-compatible-green.svg?style=flat-square" alt="MCP"></a> <a href="https://code-review-graph.com"><img src="https://img.shields.io/badge/website-code--review--graph.com-blue?style=flat-square" alt="Website"></a> <a href="https://discord.gg/3p58KXqGFN"><img src="https://img.shields.io/badge/discord-join-5865F2?style=flat-square&logo=discord&logoColor=white" alt="Discord"></a> </p>
<p align="center"> <a href="docs/USAGE.md">Usage</a> · <a href="docs/COMMANDS.md">Commands</a> · <a href="docs/FAQ.md">FAQ</a> · <a href="docs/TROUBLESHOOTING.md">Troubleshooting</a> · <a href="docs/GITHUB_ACTION.md">GitHub Action</a> · <a href="docs/REPRODUCING.md">Reproducing the benchmarks</a> · <a href="docs/ROADMAP.md">Roadmap</a> </p>
<br>
AI coding tools often re-read large parts of a codebase to review a change. code-review-graph builds a structural map of the code with Tree-sitter, keeps it updated incrementally, and serves compact context over MCP, so the assistant reads only the files a change touches.
<p align="center"> <img src="diagrams/diagram1_before_vs_after.png" alt="The Token Problem: reading flask's whole corpus costs 143,594 tokens, a graph answer costs 2,196 (65x fewer)" width="85%" /> </p>
What it does
Maps your code. Functions, classes, imports, calls and tests become one graph.
Shows what a change can break. The callers, dependents and tests it can reach, with a risk score.
Stays up to date. Hooks and watch mode parse again only the files that changed.
Works with your AI tools. One
installsets up each tool it finds.Stays on your machine. One SQLite file, no cloud service; cloud embeddings are opt-in.
Quick Start
Requires Python 3.10+.
pip install code-review-graph # or: pipx install code-review-graph
code-review-graph install # detect installed AI coding tools and configure each one
code-review-graph build # parse the codebase<details> <summary><strong>What <code>install</code> changes, and how to preview it first</strong></summary> <br>
install detects which AI coding tools you have, writes an MCP server entry for each, installs hooks and skills where the platform supports them, and adds graph instructions to the platform's rules file. The MCP entry uses poetry run or uv run inside a Poetry or uv project environment, uvx code-review-graph serve when uvx is on PATH, and otherwise the current Python interpreter. Nothing is written until install has listed the files it will change and you have answered its question about instruction files; install --dry-run shows each change as a diff and writes nothing. Restart the editor or tool afterwards.
The files install writes, such as .claude/ and .mcp.json, stay visible to git in case your team commits them. install --gitignore adds the ones that run created to a marked block in .gitignore; uninstall removes the block.
For Codex, the installer also places a global code-review-graph Skill under
$CODEX_HOME/skills/code-review-graph/ (default:
~/.codex/skills/code-review-graph/). It prefers CRG MCP tools and falls back
to the bundled read-only CLI when MCP is unavailable; a missing, empty, or
stale graph never triggers an implicit build or update. WSL and Windows Codex
runtimes have separate CODEX_HOME directories and tool inventories, so
install and refresh the runtime that will execute the task.
</details>
<details> <summary><strong>Every supported platform, and how to set up just one</strong></summary> <br>
<p align="center"> <img src="diagrams/diagram8_supported_platforms.png" alt="One install, every platform: detects Codex, Claude Code, CodeBuddy Code, Cursor, Windsurf, Zed, Continue, OpenCode, MiMo Code, Antigravity, Gemini CLI, Qwen Code, Qoder, Kiro, GitHub Copilot, GitHub Copilot CLI, Hermes Agent and DeepSeek Harness" width="85%" /> </p>
To configure one platform, pass --platform with one of codex, claude-code, cursor, windsurf, zed, continue, opencode, mimo, antigravity, gemini-cli, qwen, kiro, qoder, copilot, copilot-cli, codebuddy, hermes, or dsh:
code-review-graph install --platform cursor
code-review-graph install --platform codebuddyConfig file locations are listed in docs/USAGE.md. Requires Python 3.10+.
</details>
<details> <summary><strong>Remove it again with <code>uninstall</code></strong></summary> <br>
uninstall removes CRG-owned files and entries from a Git or SVN working tree and leaves other MCP servers, hooks, skills and JSONC comments alone. Run it from anywhere inside the tree. Shared config files are replaced atomically, so a failed write leaves the original intact.
code-review-graph uninstall --dry-run # preview only
code-review-graph uninstall # preview, confirm, 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, keep graph databases
code-review-graph uninstall --keep-user-configs --repo . # this project only</details>
Restart the editor or tool after install.
Then open the project and ask the assistant:
Build the code review graph for this project<details> <summary><strong>How long a build takes, and what a partial build means</strong></summary> <br>
Build time scales with repository size; a cold build of a ~3,000-file repository took about 40 seconds (measured). After that, hooks and watch mode keep the graph updated. If some files fail to parse, the result has status partial and names them in its summary; the CLI also prints a Warning: line on stderr, and those files keep their previous graph rows.
</details>
Run with Docker or Podman
<details> <summary><strong>Build the image, then run it on your repository</strong></summary> <br>
Build the image from the repository root:
docker build -t code-review-graph:local .From the repository you want to analyse, run:
docker run --rm \
--user "$(id -u):$(id -g)" \
--env HOME=/tmp \
--volume "$PWD:/workspace" \
code-review-graph:local buildThe mounted repository must be writable. The command above uses your
user and group IDs so the generated .code-review-graph/ directory
remains owned by your user. Run these commands as a non-root host user.
For Podman, build the same Dockerfile and preserve your user mapping:
podman build -t code-review-graph:local .
podman run --rm \
--userns=keep-id \
--env HOME=/tmp \
--volume "$PWD:/workspace" \
code-review-graph:local buildThe graph persists in .code-review-graph/ in the mounted repository and stores
absolute container paths such as /workspace/app.py. Run update and
detect-changes through Docker or Podman with the same /workspace mount.
A host update on this graph can fail with "built with a different repository
root". To switch to host commands, run code-review-graph build on the host
to rebuild the graph with host paths.
Replace build with status to inspect the saved graph.
The image includes Git and defaults to a non-root user.
No graph is generated during image construction.
These examples use macOS/Linux shell syntax. On macOS, start your
Docker-compatible engine or Podman machine before running them. On a host with
SELinux on, such as Fedora, add :z to the mount ("$PWD:/workspace:z"), or
the container is refused access to the repository.
</details>
How It Works
The repository is parsed into ASTs with Tree-sitter and stored as a graph of nodes (functions, classes, imports) and edges (calls, inheritance, test coverage). At review time the graph is queried for the smallest set of files the assistant needs to read.
<details> <summary><strong>Two diagrams: how the assistant uses the graph, and the build pipeline</strong></summary> <br>
<p align="center"> <img src="diagrams/diagram7_mcp_integration_flow.png" alt="How the assistant uses the graph: the user asks for a review, the assistant calls MCP tools, the graph returns blast radius and risk scores, the assistant reads only the affected files" width="80%" /> </p>
<p align="center"> <img src="diagrams/diagram2_architecture_pipeline.png" alt="Architecture pipeline: Repository to Tree-sitter parser to SQLite graph to blast radius to minimal review set" width="100%" /> </p>
</details>
Blast-radius analysis
<details> <summary><strong>Every caller, dependent and test a change can affect</strong></summary> <br>
When a file changes, the graph traces every caller, dependent and test that could be affected. The assistant reads those files instead of scanning the whole project.
<p align="center"> <img src="diagrams/diagram3_blast_radius.png" alt="Blast radius: a change to login() propagates to callers, dependents, and tests" width="70%" /> </p>
</details>
Incremental updates
<details> <summary><strong>Only changed files are parsed again (about 2.5 s for a two-file edit)</strong></summary> <br>
Editor hooks, git hooks (pre-commit, post-checkout, post-merge, post-rewrite) and watch mode trigger incremental updates. The update diffs changed files, finds their dependents through the graph's import and call edges, and re-parses only the files whose SHA-256 hash 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.
<p align="center"> <img src="diagrams/diagram4_incremental_update.png" alt="Incremental update flow: a hook or watch update triggers a git diff, dependents are found through graph edges, and only files whose SHA-256 hash changed are re-parsed" width="90%" /> </p>
</details>
Whole codebase or targeted answer?
<details> <summary><strong>A slice of the graph shaped to the question, not the whole codebase</strong></summary> <br>
Instead of feeding a whole corpus to the model, the graph returns a slice shaped to the question. In the 2026-08-02 capture of this repository at 84bde354, 208,821 source tokens became ~3,190 tokens per question. The repository has grown a lot since that snapshot, so both numbers are larger today.
<p align="center"> <img src="diagrams/diagram6_monorepo_funnel.png" alt="code-review-graph at the 84bde354 snapshot: 208,821 source tokens funnel down to ~3,190 token graph responses, about 65x fewer tokens per question" width="80%" /> </p>
</details>
Language coverage and notebooks
<details> <summary><strong>Python, JavaScript, TypeScript, Go, Rust, Java, C/C++, C# and many more, plus notebooks: the full list</strong></summary> <br>
<p align="center"> <img src="diagrams/diagram9_language_coverage.png" alt="Language coverage by category: Web, Backend, Systems, Mobile, Scripting, Shells, Domain, Config and Other, plus Jupyter and Databricks notebooks" width="90%" /> </p>
The parser extracts functions, classes, imports, call sites, inheritance and tests, using Tree-sitter where a grammar exists and targeted fallbacks elsewhere. Supported: Python, JavaScript/TypeScript/TSX (including .mjs, .cjs, .mts and .cts), Go, Rust, Java, C/C++, C#, VB.NET, Ruby, Kotlin (.kt, .kts), Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL/PL/SQL (Oracle) and dbt models, Terraform/OpenTofu (.tf, and Terraform blocks in .hcl files; an .hcl file with no Terraform blocks keeps only a file node), Ansible YAML (playbooks, roles, tasks), Spring Boot application config (application.properties, application.yml, application.yaml and their application-<profile> variants; key names and value types only, never values), value-free generic YAML structure (translation files in folders such as locales/ become file nodes only), XSLT stylesheets (.xsl, .xslt), value-free JSON structure (.json; translation, test, docs, example and data files keep only a file node, and lock files are skipped), Vue/Svelte SFCs, HTML pages (.html, .htm; inline scripts, plus div, section and article blocks with an id as DocSection nodes), Astro files (parsed with the TypeScript grammar), Jupyter and Databricks notebooks (.ipynb, plus Databricks .py exports), Perl XS files (.xs), Robot Framework files (.robot, .resource), and Liquid templates (.liquid). Other .properties files are not treated as source code.
PHP projects also get repository-bounded Composer PSR-4 resolution, Blade template references,
and Laravel Route and Eloquent edges when the source shows explicit framework imports, model
inheritance and receiver evidence. Static Laravel routes in routes/*.php emit Endpoint nodes
and controller HANDLES edges, including literal group prefixes.
Java projects get Spring dependency-injection call resolution, request endpoints and WebFlux routes, scheduled triggers, application-event publisher-to-listener edges, Spring AOP advice-to-target edges, and Temporal workflow and activity edges. Each resolver runs after the parse and needs the injected field, published event or workflow stub to be visible in the repository.
</details>
Add your own language
<details> <summary><strong>Map a file extension to a bundled Tree-sitter grammar in <code>languages.toml</code></strong></summary> <br>
If your repository uses a language the parser does not cover, add a languages.toml to .code-review-graph/ that maps file extensions to any grammar bundled in tree_sitter_language_pack, plus the node types for functions, classes, imports and calls:
[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 does the extraction. Built-in languages cannot be overridden. See docs/CUSTOM_LANGUAGES.md for the schema, validation rules and a worked example.
</details>
Risk-scored PR reviews in CI (GitHub Action)
<details> <summary><strong>One pull request comment with risk scores, affected flows and test gaps; see the sample workflow</strong></summary> <br>
The same analysis runs as a composite GitHub Action. The graph is built and queried on your CI runner; no source code is sent to an external service. On each pull request the action posts one sticky comment with risk-scored functions, affected execution flows and test gaps, updated in place on every push. The optional fail-on-risk input turns it into a merge gate. The optional details input adds more graph context, folded away and capped in size: why each top symbol scored what it did, the communities, hubs and cross-boundary links the change touches, its impact radius, its untested hotspots, and where the graph came from.
# .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/[email protected]
with:
github-token: ${{ secrets.GITHUB_TOKEN }}See docs/GITHUB_ACTION.md for inputs, risk levels and caching, or the workflow this repository runs on itself in .github/workflows/pr-review.yml.
</details>
Benchmarks
The median per-question token reduction across the 6 repositories is about 63x (whole-corpus baseline vs graph query). The 358x maximum is one repository (fastapi, the largest corpus), not the typical result.
<details> <summary><strong>The benchmark board, and how the numbers were made</strong></summary> <br>
<p align="center"> <img src="diagrams/diagram5_benchmark_board.png" alt="Benchmarks across 6 repositories: ~63x median per-question token reduction (358x max), 0.69 average impact F1 against graph-derived ground truth" width="85%" /> </p>
All numbers come from the evaluation runner against 6 open-source repositories (13 commits). Every config pins an upstream SHA, Leiden runs with a fixed seed, and embeddings are deterministic on CPU, so two runs on different machines produce the same numbers. The reproduction recipe is in docs/REPRODUCING.md. A weekly report-only run on the two smallest configs lives in .github/workflows/eval.yml.
</details>
<details> <summary><strong>Token efficiency: ~63x median per-question reduction (range 35x to 358x; whole-corpus vs graph query)</strong></summary> <br>
For a typical agent question ("how does authentication work", "what is the main entry point", and so on), the graph returns ~2,200 to 3,900 tokens of search hits plus neighbour edges instead of every source file. The table averages 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 | 357.6x |
| flask | a29f88ce | 143,594 | 2,196 | 65.4x |
| code-review-graph | 84bde354 | 208,821 | 3,190 | 65.5x |
| gin | 5c00df8a | 166,868 | 2,766 | 60.3x |
| httpx | b55d4635 | 142,356 | 2,661 | 53.5x |
| express | b4ab7d65 | 136,052 | 3,936 | 34.6x |
Captured 2026-08-02 from clean clones at the pinned SHAs (crg 2.3.7, local
all-MiniLM-L6-v2embeddings). These numbers are lower than the 2026-05-25 capture they replace: node embedding text became richer, soavg graph_tokensrose in every repo. fastapi is measured at its current pin22381558rather than the retired0227991a.The Reduction column is
naive_corpus_tokens / avg graph_tokens, so it divides out from the two columns beside it. The benchmark's ownaverage_reduction_ratioaverages the five per-question ratios instead, which always reads higher; those per-question figures are indocs/REPRODUCING.md.The
code-review-graphrow is a snapshot, not a current measurement. The repository has grown since84bde354, so its corpus and graph are both much larger today.
The whole-corpus baseline is an upper bound no real agent pays; an agent greps for identifiers and reads the best-matching files. The agent_baseline eval benchmark measures that case (a pure-Python grep over the corpus, top-3 files by match count, token-counted against the graph query cost). Against that realistic baseline the median across 18 questions on 6 repositories is 6.65x (mean 14.8x, range 4.2x to 72.6x), captured 2026-09-22, before search and this baseline began ranking test files lower, so code-review-graph eval --benchmark agent_baseline --embed now gives different numbers until the capture is redone. The full table is in docs/REPRODUCING.md, under "Canonical capture, 2026-09-22".
The ratio tracks repository size, because the graph answer stays flat (2,432 to 4,021 tokens across every repo measured) while the grep baseline grows with the corpus: 4.5x on express, 52x on fastapi. Both figures measure retrieval cost, the tokens needed to locate an answer. An agent that then opens the files the graph named pays the graph on top of its usual reads rather than instead of them; semantic_search_nodes_tool(include_source=True) returns the code with the locations so it does not have to, and measuring that path end-to-end is open work.
The formal token_efficiency benchmark measures a different scenario, the full get_review_context() JSON against only the changed-file content of a commit, and reports ratios below 1 for small commits because the response carries impact-radius edges and source snippets. The two benchmarks answer different questions; see docs/REPRODUCING.md.
Review and impact tools attach a compact context_savings estimate to their responses. The CLI shows the same figures in the Token Savings panel (see Usage below) and --verify compares them with OpenAI's cl100k_base tokenizer. Calibration across 222 sample files puts the estimate within about 1% of real tokens in aggregate (data).
</details>
<details> <summary><strong>Impact accuracy: 0.69 average F1 against graph-derived ground truth (recall 1.0 is a circular upper bound)</strong></summary> <br>
Blast-radius analysis recovers every file in the ground truth on all 13 evaluation commits. Read that as an upper bound, not as "100% recall": the ground truth (changed files plus files with call or import edges into them) comes from the same graph the predictor traverses. The lower precision is deliberate; flagging an extra file costs less than missing 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 |
This table is from the 2026-08-02 capture, taken before the path fix described below. That bug also kept real neighbour files out of the graph-derived ground truth, so these numbers will change when the benchmark is re-run.
The benchmark also runs a co-change mode: the predictor is seeded with one changed file and graded against the other files the author touched in the same commit, which is evidence from git history rather than from the graph. Both modes appear in the result CSVs (ground_truth_mode column). In the 2026-08-02 capture co-change mode returned predicted_files = 0 on every graded commit, code seeds included (for example code_review_graph/cli.py and gin's context.go). The cause was a path bug in the benchmark: git diff gives repo-relative paths, the graph stores absolute ones, so the seed never matched a node. That bug is now fixed, but the benchmark has not been re-run yet, so no co-change number is quoted.
</details>
<details> <summary><strong>Build stats</strong></summary> <br>
From the same 2026-08-02 clean-room build at the pinned SHAs above. Embedding counts are lower than node counts because File nodes are not embedded. The code-review-graph row is that snapshot, not the repository as it stands now.
| Repo | Nodes | Edges | Embeddings |
|---|---|---|---|
| fastapi | 6,287 | 32,036 | 5,159 |
| express | 1,990 | 19,492 | 1,849 |
| gin | 1,589 | 17,237 | 1,491 |
| code-review-graph | 1,446 | 9,094 | 1,354 |
| flask | 1,415 | 8,259 | 1,329 |
| httpx | 1,263 | 8,236 | 1,193 |
</details>
Limitations
<details> <summary><strong>Five known limits: circular recall, small edits, search ranking, flow detection, false positives</strong></summary> <br>
Impact "recall 1.0" is circular. The historical ground truth comes from the same graph edges the predictor walks, so it is an upper bound by construction. Co-change numbers are not quoted until the benchmark is re-run with the path fix.
Small single-file changes. Graph context can exceed a plain file read for trivial edits. The overhead is the structural metadata that makes multi-file analysis possible.
Search ranking. Keyword search usually finds the right result near the top, but ranking needs work. Express queries can return no hits because of module-pattern naming.
Flow detection. Entry-point detection is strongest for Python and PHP/Laravel. JavaScript and Go flow detection needs work.
Precision vs recall. Impact analysis is conservative. It flags files that might be affected, which means false positives in large dependency graphs.
</details>
Features
Updates, impact analysis, search, visualisation, CI reviews, refactoring and multi-repo support, all kept on your machine.
<details> <summary><strong>The full feature table</strong></summary> <br>
| Feature | Details |
|---|---|
| Incremental updates | Re-parses only files whose hash changed. On a ~3,000-file repo a two-file edit takes ~2.5 s on the hook path (measured). |
| Language and notebook support | See Language coverage above. |
| Framework-aware PHP parsing | Repository-bounded Composer PSR-4 imports, Blade template references, evidence-gated Laravel Route-to-controller and Eloquent relationship edges |
| Framework-aware Java parsing | Spring dependency-injection call resolution, request endpoints and WebFlux routes, scheduled triggers, application-event publisher-to-listener edges, Spring AOP advice edges, Temporal workflow and activity edges, and Spring Boot config keys indexed without their values |
| Blast-radius analysis | Which functions, classes and files are likely affected by a change |
| Auto-update hooks | Editor hooks, git hooks (before each commit, after checkout, merge and rebase) and watch mode update the graph as you work |
| Semantic search | Optional vector embeddings via sentence-transformers, Google Gemini, MiniMax, Voyage AI, or any OpenAI-compatible endpoint |
| Interactive visualisation | D3.js force-directed graph with search, community legend toggles and degree-scaled nodes, plus a neighbourhood view seeded from a symbol, file, flow or change |
| Hub and bridge detection | Most-connected nodes and chokepoints (betweenness centrality) |
| Surprise scoring | Unexpected coupling: cross-community, cross-language, peripheral-to-hub edges |
| Knowledge gap analysis | Isolated nodes, untested hotspots, thin communities, import cycles |
| Suggested questions | Review questions generated from bridges, hubs and surprises |
| Edge confidence | Four tiers (EXTRACTED, INFERRED, AMBIGUOUS, UNRESOLVED) with float scores on edges, so a call the graph could not bind says so |
| Graph traversal | BFS/DFS from any node with configurable depth and token budget; path_to shows how one symbol reaches another and names the weakest hop |
| Export formats | GraphML, Neo4j Cypher, Obsidian vault, JSON, and SVG (SVG needs matplotlib from the eval extra) |
| Token benchmarking | code_review_graph/token_benchmark.py measures whole-corpus tokens against graph query tokens per question |
| Estimated context savings | context_savings metadata (estimated, saved_tokens, saved_percent) on review, impact, detect-changes and architecture responses |
| Community auto-split | Communities above 25% of the graph are split recursively with Leiden |
| Execution flows | Call chains from entry points, sorted by weighted criticality |
| Community detection | Leiden clustering with resolution scaled to graph size |
| Architecture overview | Community-based architecture map with coupling warnings |
| Risk-scored reviews | detect_changes maps diffs to affected functions, YAML and JSON paths, flows and test gaps |
| Custom languages | New languages via .code-review-graph/languages.toml, no fork 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 | Markdown wiki from community structure |
| Multi-repo registry | Register several repos and search across them |
| Multi-repo daemon | crg-daemon watches several repos as child processes, with health checks and restart |
| MCP prompts | 5 workflow templates: review, architecture, debug, onboard, pre-merge |
| Full-text search | FTS5 hybrid search combining keyword and vector similarity |
| Local storage | One SQLite file in .code-review-graph/; no external database or cloud service |
</details>
Usage
Your assistant uses the graph by itself once it is built. Open a part below for skills, commands, MCP tools and settings.
<details> <summary><strong>Skills</strong></summary> <br>
install writes these four skills for the platforms that support them (Claude Code, Gemini CLI, CodeBuddy Code, Hermes Agent and Qoder). Ask for one by name.
| Skill | Description |
|---|---|
explore-codebase | Navigate and understand codebase structure using the knowledge graph |
review-changes | Perform a structured code review using change detection and impact |
debug-issue | Systematically debug issues using graph-powered code navigation |
refactor-safely | Plan and execute safe refactoring using dependency analysis |
Qoder also gets build-graph, review-delta and review-pr from the repository's skills/ directory.
</details>
<details> <summary><strong>CLI reference</strong></summary> <br>
code-review-graph install # Detect and configure all platforms
code-review-graph install --platform <name> # One platform
code-review-graph install --steering # Claude Code: graph context beside each search and read
code-review-graph uninstall --dry-run # Preview removal of installed artifacts
code-review-graph build # Parse the whole codebase
code-review-graph build --follow-symlinks # Also index symlinked folders
code-review-graph update # Incremental update (changed files only)
code-review-graph status # Graph statistics
code-review-graph watch # Update on file changes
code-review-graph forget <path> # Drop already-parsed files from the graph
code-review-graph dead-code # Functions and classes with no callers or tests
code-review-graph query path_to <from> --to <to> # How one symbol reaches another
code-review-graph impact --detail-level compact # Blast radius as ranked file paths
code-review-graph visualize # Interactive HTML graph
code-review-graph visualize --seed-symbol <name> # Neighbourhood of one symbol
code-review-graph visualize --format json # Export graph data as JSON
code-review-graph visualize --format graphml # Export as GraphML
code-review-graph visualize --format svg # Export as SVG (needs matplotlib)
code-review-graph visualize --format obsidian # Export as Obsidian vault
code-review-graph visualize --format cypher # Export as Neo4j Cypher
code-review-graph wiki # Markdown wiki from communities
code-review-graph detect-changes --brief # Risk panel + token savings (read-only)
code-review-graph detect-changes --brief --base main # Against the merge base of main and HEAD
code-review-graph update --brief # Refresh graph + same panel
code-review-graph detect-changes --brief --verify # Cross-check against tiktoken
code-review-graph detect-changes --details all # Add graph context to the JSON
code-review-graph context-history # Trend of the opt-in savings log (CRG_CONTEXT_HISTORY=1)
code-review-graph register <path> # Register repo in the multi-repo registry
code-review-graph unregister <path|alias> # Remove repo from the registry
code-review-graph repos # List registered repositories
code-review-graph daemon start # Start the multi-repo watch daemon
code-review-graph daemon stop # Stop the daemon
code-review-graph daemon status # Daemon status and repos
code-review-graph eval --all # Run every evaluation benchmark
code-review-graph serve # Start the MCP server (stdio)
code-review-graph serve --http # MCP over Streamable HTTP on localhost:5555
code-review-graph serve --http --multi-repo # One HTTP server for every registered repo
code-review-graph serve --multi-worktree # One child server per linked Git worktreeThis is a selection. code-review-graph --help lists every command, and docs/COMMANDS.md documents their flags.
When detect-changes --base names a branch, the diff runs against the merge base of that branch and HEAD. Commit hashes and other revisions are used as given.
visualize --format svg needs matplotlib, which ships in the eval extra (pip install "code-review-graph[eval]"). The other export formats need no extra install.
JSON exports are written inside the local graph data directory, which Git ignores by default. They can contain absolute paths and code-structure metadata, so inspect an export before publishing it.
</details>
<details> <summary><strong>Token Savings panel: <code>detect-changes --brief</code> vs <code>update --brief</code></strong></summary> <br>
Both commands print the same panel showing how many tokens the graph saved compared with handing the changed files to an agent raw. They differ in one thing: whether the graph is refreshed first.
┌─────────────────────── 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. Queries the existing graph for the current changes and prints the panel. | Most of the time; hooks or crg-daemon keep the graph fresh. |
update --brief | Re-parses the changed files into the graph first, then prints the same panel. | After a rebase, a large change set, or whenever the graph may be stale. |
Add --verify to either command to compare the figures with OpenAI's cl100k_base tokenizer (needs pip install tiktoken). The estimate is within about 1% of real tokens in aggregate; see docs/REPRODUCING.md.
The same context_savings metadata is attached to the JSON responses of the get_impact_radius, get_review_context, detect_changes and get_architecture_overview MCP tools.
To see these estimates over time, set CRG_CONTEXT_HISTORY=1 (off by default). Each estimate then adds one line to a local log next to graph.db, holding token counts, tool names, a timestamp, a detail level and file and depth counts, never file names, paths or source text, and code-review-graph context-history prints it per day or per tool. See docs/USAGE.md.
</details>
<details> <summary><strong>Multi-repo daemon</strong></summary> <br>
If install gives your editor no hooks (for example Windsurf or Zed), or you want the graph kept fresh without editor integration, the daemon watches your repositories and updates their graphs. It ships with code-review-graph; no separate install.
# 1. Register the repos 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. Check on it
crg-daemon status # daemon and per-repo watcher status
crg-daemon logs --repo proj-a -f # tail logs for one repo
crg-daemon stop # stop the daemon and all watchersAlso available as code-review-graph daemon start|stop|status|....
crg-daemon add writes to ~/.code-review-graph/watch.toml, which you can also edit directly:
[[repos]]
path = "/home/user/project-a"
alias = "proj-a"
[[repos]]
path = "/home/user/project-b"
alias = "project-b"The daemon watches this file and starts or stops watcher processes as repos are added or removed. A health check every 30 seconds restarts dead watchers.
See docs/COMMANDS.md for the full config reference.
</details>
<details> <summary><strong>30 MCP tools</strong></summary> <br>
The assistant uses these once the graph is built. The 25 tools that only read carry readOnlyHint: true, so a client can run them without asking; the five that write (build_or_update_graph_tool, run_postprocess_tool, embed_graph_tool, apply_refactor_tool, generate_wiki_tool) do not.
| 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 | Compact context (~100 tokens); call this first |
get_impact_radius_tool | Blast radius of changed files; detail_level="compact" gives ranked file paths |
get_review_context_tool | Review context with structural summary |
query_graph_tool | Callers, callees, tests, imports, inheritance, and path_to between two symbols |
traverse_graph_tool | BFS/DFS traversal from any node with a 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 | Functions, classes or files above a line-count threshold |
list_flows_tool | Execution flows sorted by criticality |
get_flow_tool | One execution flow |
get_affected_flows_tool | Flows affected by changed files |
list_communities_tool | Detected code communities |
get_community_tool | One community |
get_architecture_overview_tool | Architecture overview from community structure |
detect_changes_tool | Risk-scored change impact analysis |
get_hub_nodes_tool | Most-connected nodes |
get_bridge_nodes_tool | Chokepoints by betweenness centrality |
get_knowledge_gaps_tool | Structural weaknesses, untested hotspots and import cycles |
get_surprising_connections_tool | Unexpected cross-community coupling |
get_suggested_questions_tool | Review questions generated from the analysis |
refactor_tool | Rename preview, dead code detection, suggestions |
apply_refactor_tool | Apply a previously previewed refactoring |
generate_wiki_tool | Markdown wiki from communities |
get_wiki_page_tool | One wiki page |
list_repos_tool | Registered repositories |
cross_repo_search_tool | Search registered repositories; repos limits the search to a subset |
MCP prompts (5 workflow templates):
review_changes, architecture_map, debug_issue, onboard_developer, pre_merge_check
</details>
<details> <summary><strong>Configuration</strong></summary> <br>
To exclude paths from indexing, create a .code-review-graphignore file in the repository root:
generated/**
*.generated.ts
vendor/**
node_modules/**In git repositories a build reads tracked files plus untracked files git does not ignore (git ls-files and git ls-files --others --exclude-standard), so gitignored files are skipped. Use .code-review-graphignore to exclude other files or when git is not available. The default ignore list is in docs/USAGE.md.
Optional dependency groups:
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 with Jedi (then build --enrich)
pip install "code-review-graph[eval]" # Evaluation benchmarks and SVG export (matplotlib)
pip install "code-review-graph[wiki]" # ollama client (not used by the current wiki generator)
pip install "code-review-graph[all]" # All optional dependenciesEnvironment Variables
| Variable | Description | Default |
|---|---|---|
CRG_GIT_TIMEOUT | Timeout in seconds for Git operations (build, update, watch) | 30 |
CRG_DISCOVERY_TIMEOUT | Timeout in seconds for each Git command that discovers what changed, when a review tool or command was not given an explicit file list. Running out reports an error, never "no changes" | 5, or CRG_GIT_TIMEOUT when you set that explicitly |
CRG_DATA_DIR | Directory for graph databases and generated artefacts | - |
CRG_HOOK_WORKTREES | Set to 1 to let the generated git hooks run in linked git worktrees | - |
CRG_EMBEDDING_MODEL | Default model for local vector embeddings | all-MiniLM-L6-v2 |
CRG_ACCEPT_CLOUD_EMBEDDINGS | Set to 1 to suppress the cloud embedding egress warning | - |
CRG_ALLOW_REMOTE_CODE | Allow HuggingFace models that require trust_remote_code=True | 0 |
CRG_VECTOR_CACHE | Set to 1 to keep the decoded embedding vectors in memory between semantic searches instead of reading them all back from SQLite on every query. Costs RAM (vectors × dimensions × 4 bytes per cached index) and one open read connection per cached database for the life of the process: on Windows, graph.db cannot be deleted or replaced while it is open | - |
CRG_MAX_IMPACT_NODES | Maximum nodes 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_DELETION_FILES | Maximum files whose old version is read in one change report, to name the functions and classes a diff deleted | 100 |
CRG_MAX_CHANGED_YAML_PATHS | Maximum changed YAML paths analysed in one change report, and separately the same number of JSON paths | 200 |
CRG_MAX_TRANSITIVE_FRONTIER | Maximum callees followed per hop when tests_for and detect-changes look for tests. The first by name are kept, so one graph always gives one answer | 50 |
CRG_CALLER_TEST_ROUTE_DEPTH | Hops to walk up incoming CALLS when looking for a tested caller of an untested changed symbol. Reported as a note, never as coverage | 2 |
CRG_CALLER_TEST_ROUTE_MAX_CALLERS | Maximum callers examined while looking for that tested caller | 500 |
CRG_TOOL_TIMEOUT | Timeout in seconds for read-only MCP tools (0 disables). Does not bound the tools that write: build, postprocess, embed, wiki and apply-refactor | 0 |
CRG_CHURN_WINDOW_DAYS | Window for detect-changes --churn commit counts | 90 |
CRG_CONTEXT_HISTORY | Set to 1 to keep a local log of each context_savings estimate in .code-review-graph/context-history.jsonl; read it with code-review-graph context-history. Holds token counts, tool names, a timestamp, a detail level and file and depth counts, never file names, paths or source text, and is never sent anywhere | - |
CRG_LEIDEN_SEED | Seed for Leiden community detection | 42 |
CRG_RECURSE_SUBMODULES | Include git submodules when set to 1, true or yes | - |
CRG_FOLLOW_SYMLINKS | Follow symlinked folders, inside or outside the repository, when set to 1, true, yes or on; same as --follow-symlinks. See Symlinked folders | - |
CRG_MAX_FOLLOWED_FILES | Most files taken through followed symlinked folders | 10000 |
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 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 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 for OpenAI-compatible embeddings | - |
CRG_OPENAI_DIMENSION | Pin the embedding dimension (v3 models support reduction) | - |
CRG_OPENAI_BATCH_SIZE | Batch size for OpenAI-compatible requests | 100 |
NO_COLOR | Disable ANSI colours in the terminal | - |
CRG_SERIAL_PARSE | Set to 1 to disable parallel parsing (for debugging) | - |
CRG_ENABLE_ENRICHMENT | Set to 1 to resolve Python method calls with Jedi on every build and update (needs the enrichment extra; about a second more per Python file). Same as build --enrich | off |
CRG_DISABLE_ENRICHMENT | Set to 1 to never run Jedi, even when asked for | - |
OpenAI-compatible embeddings need no extra install. Set the variables and pass provider="openai" to embed_graph:
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 limitsThe cloud-egress warning is skipped when the base URL points at 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:
export VOYAGE_API_KEY=pa-...
export CRG_ACCEPT_CLOUD_EMBEDDINGS=1
code-review-graph embed --provider voyage --model voyage-code-3Model selection. Avoid
-preview,-betaor-expmodel IDs for an index you plan to keep; preview models can change weights (a different dimension forces a full re-embed) or be withdrawn. Prefer GA releases such astext-embedding-3-small/text-embedding-3-large(OpenAI) orgemini-embedding-001(native Gemini provider, which needsGOOGLE_API_KEY).The embedding text is identifiers, signatures, structural context, and a bounded first-paragraph docstring or doc-comment summary. Function bodies are not sent. Graphs created before documentation extraction was added need one full
code-review-graph buildbefore re-embedding. Routine builds never refresh embeddings; to refresh after a build, pass both--embedding-providerand--embedding-model. Cloud providers receive this source-derived text and may charge for it.
Tool Filtering
CRG exposes 30 MCP tools by default. To limit the server to a subset, use --tools or the CRG_TOOLS environment variable:
# CLI flag
code-review-graph serve --tools query_graph_tool,semantic_search_nodes_tool,detect_changes_tool
# Environment variable
CRG_TOOLS=query_graph_tool,semantic_search_nodes_tool code-review-graph serveThe flag takes precedence over the variable. When neither is set, all tools are available. In an MCP client config:
{
"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"]
}
}
}</details>
FAQ and comparisons
Answers in docs/FAQ.md:
<details> <summary><strong>How it compares with LSP, RAG and grep, when not to use it, and more</strong></summary> <br>
vs LSP / language servers: one persistent cross-language graph instead of per-language daemons; LSP stays more precise per symbol.
vs RAG / embeddings: structural edges parsed from the AST, not similarity chunks; embeddings are optional and only assist search.
vs grep / agentic search: grep wins on one-hop lookups; the graph wins on multi-hop questions (impact radius, callers-of-callers, tests-for, affected flows).
When not to use it: small repos, trivial single-file diffs, one-off questions.
Does it phone home?: no telemetry; cloud embeddings are opt-in.
How do I verify it is working?:
status,detect-changes --brief,/mcp.
</details>
Troubleshooting
More cases, including Windows/WSL, are in docs/TROUBLESHOOTING.md.
pip / pipx cannot download hatchling (or Errno 9 / Bad file descriptor to PyPI)
<details> <summary><strong>How to fix it</strong></summary> <br>
Installing from a source tree (for example pipx install .) needs build dependencies from PyPI. If you see Could not find a version that satisfies the requirement hatchling after connection warnings, the Python in that terminal may not be able to open an HTTPS connection to pypi.org. This is seen most often in an editor's integrated terminal, and sometimes with a VPN, firewall or proxy.
Run the same command from a terminal outside the editor, such as Terminal.app.
Install from a checkout with uv, which uses different download machinery:
cd /path/to/code-review-graph uv tool install . --forceFor development in a clone, use
uv syncanduv run code-review-graph ....
To diagnose: python3 scripts/diagnose_pypi_connectivity.py. If it prints FAILED, the problem is the network environment, not the package name.
</details>
Windows: Invalid JSON: EOF while parsing or MCP error -32000: Connection closed
<details> <summary><strong>How to fix it</strong></summary> <br>
Do not use a cmd /c wrapper in the Claude Code config. Point ~/.claude.json at the .exe directly and set UTF-8 through the config:
"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" }
}</details>
Contributing
Pull requests target staging (the default branch). Changes are promoted
staging → testing → main, and releases are tagged from main. No
promotion is automatic: a person reviews and merges each one. See
CONTRIBUTING.md for the full flow.
<details> <summary><strong>Set up a development copy and run the tests</strong></summary> <br>
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</details>
<details> <summary><strong>Add a built-in language</strong></summary> <br>
To add a built-in language, edit code_review_graph/parser.py: add the extension to EXTENSION_TO_LANGUAGE and node type mappings to _CLASS_TYPES, _FUNCTION_TYPES, _IMPORT_TYPES and _CALL_TYPES. Include a test fixture and open a PR. For a language you only need in one repository, use languages.toml instead.
</details>
Licence
MIT. See LICENSE.
<p align="center"> <br> <a href="https://code-review-graph.com">code-review-graph.com</a><br><br> <code>pip install code-review-graph && code-review-graph install</code> </p>
docs/COMMANDS.md
All Available Commands
Skills and Slash Commands
The skills build-graph, review-delta and review-pr ship in the repository's skills/ folder. install --platform qoder (or the default --platform all) copies them, with the four workflow skills below, to .qoder/skills/. Other clients do not get these three.
build-graph
Build or update the knowledge graph.
First run: full build
Later runs: incremental update (changed files only)
review-delta
Review changes since the last commit.
Changed files come from
git diffBlast radius is changed nodes plus 2-hop neighbours
Output is a structured review with guidance
review-pr
Review a PR or branch diff.
Uses
main(ormaster) as the baseCovers every commit in the PR
Output is a structured review with a risk assessment
install also writes four workflow skills, explore-codebase, review-changes,
debug-issue and refactor-safely, to .claude/skills/ for Claude Code,
.gemini/skills/ for Gemini CLI, .codebuddy/skills/ for CodeBuddy and
~/.hermes/skills/code-review-graph/ (or $HERMES_HOME/skills/code-review-graph/)
for Hermes Agent. Qoder gets all seven skills in .qoder/skills/, and Codex gets
one global skill. --no-skills skips them all.
MCP Tools
Every repo_root parameter is optional and auto-detected from the current
directory when omitted. The one exception is serve --http --multi-repo, where
every call must pass the absolute path of a registered repository (see Server
below).
Every tool carries readOnlyHint: true except the five that write:
build_or_update_graph_tool, run_postprocess_tool, embed_graph_tool,
apply_refactor_tool and generate_wiki_tool. A client can use the hint to
skip its approval prompt for the read-only ones.
Where a tool takes base, a local or remote branch ref (for example
origin/main) resolves to its merge base with HEAD, so the diff covers only
this branch's own commits. Commit ids and revision expressions such as HEAD~3
are used as given. If Git cannot find the merge base (shallow clone), the ref
is used as given.
Core Tools
build_or_update_graph_tool
full_rebuild: bool = False # True re-parses every file
repo_root: str | None
base: str | None = None # Diff base; None means the commit the graph was last built at
postprocess: str = "full" # "full", "minimal" (signatures + FTS), or "none"
recurse_submodules: bool | None # None falls back to CRG_RECURSE_SUBMODULES
embedding_provider: str | None # Refresh an existing embedding index; needs embedding_model
embedding_model: str | None # Exact model for embedding_providerIf some files fail to parse, status is "partial" and the summary names
them. Their previous graph rows are kept.
One write runs per repository at a time. When another write to the graph is
running, this tool, run_postprocess_tool and embed_graph_tool
return at once with status: "contended", skipped: true and the holder_pid;
retry once it ends. An embedding refresh on a graph with no embeddings yet embeds
nothing and sets embeddings_refresh_skipped: true; run embed_graph_tool once
first.
run_postprocess_tool
flows: bool = True
communities: bool = True
fts: bool = True
repo_root: str | None
embedding_provider: str | None # Refresh an existing embedding index; needs embedding_model
embedding_model: str | Noneget_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
max_results: int # Nodes to traverse to; default CRG_MAX_IMPACT_NODES, or 500
repo_root: str | None
base: str = "HEAD~1"
detail_level: str = "standard" # "standard", "minimal", or "compact"
max_files: int = 25 # Compact-mode display limit (hard ceiling 200)
resolution: str = "all" # "all" or "direct" (only calls bound to a node)An impacted node that calls or references the changed code in one hop carries
call_site (line, plus file only when the call is written outside the node's
own file_path) and call_site_count when there is more than one.
unresolved_call_sites counts call sites that name a changed symbol but were
never bound to it, so an empty radius is not read as proof of absence.
Changed files the graph has no nodes for are listed in unmatched_files, and
when none of the changed files is in the graph, risk is "unknown".
Responses may include estimated context_savings metadata.
Use detail_level="compact" when the next step is opening affected files rather
than inspecting individual call sites. It returns ranked repository-relative
changed_files and impacted_files, with changed_file_count,
changed_files_omitted, impacted_file_count, impacted_files_omitted,
total_impacted, truncated, and unresolved_call_sites. unmatched_files
(with unmatched_files_total) lists the changed files the graph has no nodes for.
Paths outside the repository keep their absolute identity.
Files follow the graph's best-first node ranking. Counts describe the traversed
radius; truncated remains true when traversal or a file list was bounded.
The mode omits node/edge records and repeated qualified names. max_files limits
each displayed file list, independently of the max_results traversal budget.
Standard output and its existing limits are unchanged; minimal output remains a
summary without file paths.
The CLI exposes the same controls:
code-review-graph impact --files src/auth.py --detail-level compact --max-files 20
code-review-graph impact --files src/auth.py --detail-level minimalquery_graph_tool
pattern: str # callers_of, references_to, callees_of, imports_of, importers_of,
# children_of, tests_for, inheritors_of, triggers_of, triggered_by,
# publishers_of, listeners_of, advises, advised_by, handlers_of,
# endpoints_for, consumers_of, file_summary, path_to
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 also caps visible results at 5
resolution: str = "all" # "all", "direct", or "unresolved"
to: str | None # path_to only: the node or file to reachcallers_of, callees_of and references_to return one row per call site, not
one per node. Each row carries call_site (line, plus file only when the
call is written outside the row's own file_path), and rows whose target was
matched by bare name alone carry target_resolution: "unresolved". The response
adds distinct_nodes and a resolution_split of the whole answer. When some of
the rows are only name matches and resolution is "all", a short confidence
sentence says how many. Call sites
are ordered so every distinct node appears before any node's second call site,
so truncation never costs a caller. Within that order, calls bound to a node come
before bare-name guesses.
A full name that one real symbol has resolves to it, even when tests or test
helpers share the name; the summary says how many it skipped. When two real
symbols share the name, status is "ambiguous" and the response lists
qualified names to pick from (five short rows in minimal mode).
path_to answers how target reaches to, with one row per hop through calls,
references, inheritance, imports and containment. It picks the route whose weakest
hop is strongest, and the shortest of those. Each hop's certainty says how it was
bound: direct to that node, unique_name through a bare name only one symbol
has, or shared_name through a name several share. certainty and weakest_hop
on the response give the weakest one, which is the hop to check first. How a hop
was bound ranks first and the edge's stored confidence only breaks ties, since
graphs built before #984 was fixed store 1.0 even on an edge that names no node.
In standard mode a hop shows confidence when it is below 1.0. When there is no
route, results is empty, and reverse_route_exists is set if one runs the other
way. If the search stops at its visit limit first, search_incomplete is set
instead, as a route may still exist. Callbacks and dynamic dispatch are not in the
graph, so no route does not prove there is none. An unclear end comes back as
not_found or ambiguous, with endpoint saying which. Minimal mode names each
hop by its qualified name from the repository root, and adds file only when the
call is written outside the source's own file.
tests_for rows that are guesses carry inferred_by, in minimal mode too:
naming_convention for a test found by its name, and ambiguous_call for a PHP
test whose call could mean this method or a same-named one in another class the
test imports, as with app(Service::class)->run(). The summary says how many
rows are guesses. A guess is never counted as coverage.
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 = 100 # Graph nodes per list (max 100) and edges (max 150)
max_files: int = 25 # Files listed and given snippets (max 200)changed_files is ordered by risk score (highest first, per file in
context.file_risk), and both max_files and the shared 800-line snippet
budget are spent in that order. The budget buys whole changed regions -- the
diff hunks, each widened to its enclosing definition when that definition is
short enough to read whole -- granted round-robin across the ranked files, so
a file with one small change costs one small grant and a file with six hunks
gets six turns. No single file may hold more than 40% of the budget.
context.source_regions reports shown, total, and an incomplete map of
the files whose regions did not all fit. Each list reports its untruncated
*_total, and context.truncated / context.source_truncated mark any cut.
Changed files the graph has no nodes for are listed in unmatched_files, and
when none of the changed files is in the graph, risk is "unknown".
Responses may include estimated context_savings metadata.
traverse_graph_tool
query: str
depth: int = 3 # Clamped to 1-6
mode: str = "bfs" # "bfs" or "dfs"
token_budget: int = 2000 # At most 10000 approximate tokens; the walk stops there
repo_root: str | Nonetruncated means the budget ran out before the walk did.
semantic_search_nodes_tool
query: str # Search string
kind: str | None # File, Class, Function, Type, Test, YamlPath, DocSection,
# Variable, Output, JsonPath
limit: int = 20 # At most 100; at most 5 with detail_level "minimal"
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"
include_source: bool = False # Attach each hit's source lines
expand_neighbors: bool = False # With include_source: also the top hits' callers and callees
max_source_lines: int = 60 # Lines per snippet (1-60)Search ranks three times what it shows. total and result_count count the
matches it ranked, results_omitted those not shown, and total_is_lower_bound
says there may be more.
include_source=True lets you answer from the reply instead of opening the
files. All snippets share one budget of 240 lines, or 700 with
expand_neighbors=True, which adds up to four callers and callees for each of
the top three hits. A cut snippet carries source_truncated. Source is not
attached when detail_level="minimal".
kind is applied before the hits are cut. Test code ranks below production
code unless the query asks about tests (kind="Test", or a word such as
"test", "spec", "fixture" or "mock"). JSON ranks below code unless you ask for
it (kind="JsonPath", a $ path or a .json name). Liquid Output nodes are
found only with kind="Output".
embed_graph_tool
repo_root: str | None
model: str | None # Embedding model name
provider: str | None # local, openai, google, minimax, voyageLocal embeddings need pip install "code-review-graph[embeddings]", and Google
needs pip install "code-review-graph[google-embeddings]". The OpenAI-compatible,
MiniMax and Voyage providers use the standard library HTTP client. Every cloud
provider reads its key from environment variables (see the README).
list_graph_stats_tool
repo_root: str | Nonefind_large_functions_tool
min_lines: int = 50 # Minimum line count
kind: str | None # File, Class, Function, Test, YamlPath, DocSection, or JsonPath
file_path_pattern: str | None # File path substring
limit: int = 50 # At most 100; at most 25 with detail_level "minimal"
repo_root: str | None
detail_level: str = "standard" # "minimal" gives fewer, smaller rowstotal and result_count count every match before the cut.
get_docs_section_tool
section_name: str # usage, review-delta, review-pr, commands, legal, watch, embeddings, languages, troubleshooting
repo_root: str | NoneFlow Tools
list_flows_tool
sort_by: str = "criticality" # criticality, depth, node_count, file_count, name
limit: int = 50 # Flows returned (max 200)
kind: str | None # 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); ignored when flow_id is given
include_source: bool = False # Source snippet per 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 2000get_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 = 50Standard mode carries a full steps list per flow, so it caps visible flows
at 25 and spends a shared 400-step budget across them; minimal mode caps at
500. total 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)size reports the true member count; members_truncated marks a cut member list.
get_community_tool
community_name: str | None # Name to search (partial match); ignored when community_id is given
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" (default) or "standard"
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 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_degreeget_bridge_nodes_tool
top_n: int = 10 # Capped at 100
repo_root: str | None
detail_level: str = "standard" # "minimal" returns name, kind, betweennessget_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 pathssummary maps each category to its untruncated count. The categories are
isolated_nodes, thin_communities, untested_hotspots,
single_file_communities and import_cycles.
An import_cycles row is one group of files that import each other: size is
how many files, name and cycle spell out one loop with its import lines, and
load_time says whether that loop can fail as the files load. false means every
loop has an import that runs late: inside a function, a dynamic import() or a
Ruby autoload. Imports that never run are left out: if TYPE_CHECKING: in
Python and import type in TypeScript, and import { type X } unless
verbatimModuleSyntax keeps it. Only imports that
name a file are followed, so Go packages and C# namespaces are not. Graphs built
before this release need code-review-graph build to tell these imports apart.
get_surprising_connections_tool
top_n: int = 15 # Capped at 100
repo_root: str | None
detail_level: str = "standard" # "minimal" returns source, target, kind, scoreget_suggested_questions_tool
repo_root: str | NoneChange Analysis and Refactoring Tools
detect_changes_tool
base: str = "HEAD~1"
changed_files: list[str] | None
include_source: bool = False # Snippets share a 600-line budget
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)Main tool for code review. Maps changed files to affected functions, YAML and JSON
paths, flows, communities and test coverage gaps, returning changed_yaml_paths and
changed_json_paths separately from function risk/test-gap scoring, plus risk scores
and review priorities.
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. Responses may include estimated context_savings metadata.
When the diff only deletes lines, the code around the gap did not change, so it
is not listed as changed. The old version of the file is read instead: a
function or class that lost some lines is listed as changed, and one that is
gone is listed in removed_symbols (at most 25; removed_symbols_total has the
full count) with up to three of the callers, subclasses, importers or tests that
still use it (used_by_total has the full count). A removed symbol that is still
used breaks its users, so it gets a risk_score on the same scale as a changed
one (the flows it was part of, callers from other communities, test coverage,
security names, caller count) and counts towards the overall risk_score; one
that nothing uses scores 0. The summary names the ones still in use, and minimal
mode reports removed_symbol_count. Up to 100 files
are read per call (CRG_MAX_DELETION_FILES); the summary says when a file could
not be read. A deleted file is not covered.
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) 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 onlyTruncating a rename preview truncates only the response. The stored preview
keeps every edit, so apply_refactor_tool applies the full set.
old_name is matched exactly: a name, Class.method or a qualified name such as
pkg/util.py::helper. When a bare name belongs to several symbols, the first by
file and line is renamed and the preview's qualified_name says which; pass a
qualified name to pick another. A name that matches no symbol gives not_found.
The preview also edits the import and export lines that name the symbol. Each
edit carries the column where the old name starts and its site (definition,
call, reference, import, export, attribute or __all__), and applying
replaces the name only there. An edit that no longer matches the file is skipped
and counted in edits_skipped.
apply_refactor_tool
refactor_id: str # ID from a prior refactor_tool call
repo_root: str | None
dry_run: bool = False # Return a 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 unchangedget_wiki_page_tool
community_name: str
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 searched repos (max 100)
repos: list[str] | None # Aliases or folder names; default: every registered reporepos limits the search to named registry entries. Matching is exact and
case-sensitive; paths are not accepted. An alias match wins over a folder-name
match. Results merge by each repo's local rank, with registry order as the
tie-breaker.
Names that match no entry are returned in unknown. A name that matches
several entries selects all of them and is listed in ambiguous. Both lists
are capped at 20 entries; unknown_total / ambiguous_total and
unknown_truncated / ambiguous_truncated report the real counts, and the
summary reports the counts rather than echoing the names.
Result Bounds
Every tool that returns a list is bounded, so one MCP response cannot fill a context window (see #849). The contract is the same everywhere:
Defaults are small. Pass the tool's cap parameter to widen up to its hard ceiling, or a smaller value to narrow.
Truncation is reported: the response carries the untruncated count (
total, or a*_totalfield per list), setstruncated: true, and the summary says how many of how many are shown.Cap parameters reject values below 1 and reject booleans.
Hard ceilings are enforced in code and pinned by
tests/test_token_budget.py.
MCP Prompts (5 workflow templates)
review_changes
Pre-commit review using detect_changes, affected_flows and test gaps.
base: str = "HEAD~1"architecture_map
Architecture documentation using communities, flows and Mermaid diagrams.
debug_issue
Guided debugging using search, flow tracing and recent changes.
description: str = ""onboard_developer
New developer orientation using stats, architecture and critical flows.
pre_merge_check
PR readiness check with risk scoring, test gaps and dead code detection.
base: str = "HEAD~1"CLI Commands
Run code-review-graph <command> --help for the full option list.
# Setup
code-review-graph --version # Show the version (or -v)
code-review-graph install # Configure detected AI coding platforms (alias: init)
code-review-graph install --dry-run # Show each file's diff; write nothing
code-review-graph install --gitignore # Also add the files this run creates to .gitignore (off by default)
code-review-graph install --platform codex # Configure Codex MCP, hooks, and global Skill
code-review-graph install --steering # Opt in: graph context beside Claude Code searches and reads
code-review-graph install --no-steering # Remove only that hook
code-review-graph uninstall # Remove CRG configs, hooks, skills, and data
code-review-graph uninstall --dry-run # List what it would remove; change nothing
code-review-graph uninstall --keep-data # Remove integrations, keep graph databases
code-review-graph uninstall --platform codex # Unbind one platform (keeps graph data and others)
code-review-graph uninstall --all-repos # Also clean every repository in the registry
code-review-graph uninstall --keep-user-configs # Clean repositories only; leave files in your home folder
code-review-graph uninstall -y # Apply without asking
# Build and update
code-review-graph build # Full build
code-review-graph build --data-dir /mnt/graphs/myrepo # Keep graph.db outside the repo (or set CRG_DATA_DIR); also on update,
# postprocess, embed, watch, status, context-history, forget, visualize, wiki, dead-code
code-review-graph build --skip-flows # Parse + signatures + FTS only
code-review-graph build --skip-postprocess # Raw parse only
code-review-graph build -q # No output (-q/--quiet also on update and status)
code-review-graph build --enrich # Also resolve Python calls with Jedi (needs [enrichment]);
# also on update, or CRG_ENABLE_ENRICHMENT=1. Off by default
code-review-graph update # Incremental update from the last-built commit
code-review-graph update --base origin/main # Custom base ref
code-review-graph update --brief # Update graph, then show the risk panel
code-review-graph update --brief --verify # ...and cross-check against tiktoken
code-review-graph update --wait # Wait for a running build to finish, then update
code-review-graph build --follow-symlinks # Also follow symlinked folders, inside or outside the repo
# (off by default; also on update and watch, or CRG_FOLLOW_SYMLINKS=1)
code-review-graph postprocess # Re-run flows, communities, FTS
code-review-graph postprocess --no-flows # Skip a step: --no-flows, --no-communities, --no-fts
code-review-graph forget PATH [PATH ...] # Drop parsed files from the graph (no full rebuild)
code-review-graph forget src/legacy --dry-run # Preview which files would be forgotten
code-review-graph forget PATH --wait # Wait for a running write to finish, then forget
code-review-graph embed --provider local # Compute vector embeddings for semantic search
code-review-graph embed --wait # Wait for a running write to finish, then embed
code-review-graph update --embedding-provider local --embedding-model all-MiniLM-L6-v2
# Refresh an existing embedding index (default: off)
# Monitor and inspect
code-review-graph status # Graph statistics (no graph: exit 1, no DB created)
code-review-graph status --json # One JSON object
code-review-graph context-history # Per-day trend of the opt-in context savings log
code-review-graph context-history --by tool --days 7 # Per tool, last 7 UTC days (--json for JSON)
code-review-graph watch # Auto-update on file changes (needs an existing graph)
code-review-graph visualize # Interactive HTML graph (needs an existing graph)
code-review-graph visualize --format graphml # Formats: html, json, graphml, cypher, obsidian, svg
# Written to the data folder (.code-review-graph/ by default) as
# graph.<ext>, or obsidian/. svg needs matplotlib:
# pip install "code-review-graph[eval]" (without it: exit 1)
code-review-graph visualize --mode community # Modes: auto (default), full, community, file
code-review-graph visualize --serve # Serve graph.html on localhost:8765
# Neighbourhood view: draw a symbol's surroundings instead of the repository.
# The whole-repo page collapses to one bubble per community past 3000 nodes or
# 9000 edges; a seeded page ships only the nodes within --depth hops.
code-review-graph visualize --seed-symbol login # depth 2 by default
code-review-graph visualize --seed-symbol login --depth 3 # more context
code-review-graph visualize --seed-file src/auth.py # a file and its symbols
code-review-graph visualize --seed-changed # the files in this review
code-review-graph visualize --seed-changed --seed-changed-base origin/main
code-review-graph visualize --seed-flow "login request" # an execution flow
code-review-graph visualize --path-from login --path-to audit_log
code-review-graph visualize --seed-symbol login --render-depth 0 # expand on click
code-review-graph visualize --seed-symbol login --max-nodes 300 # hard cap
code-review-graph visualize --seed-symbol login --sidecar # payload in graph.data.js
# Analysis
code-review-graph detect-changes # Risk-scored change analysis (read-only)
code-review-graph detect-changes --base HEAD~3 # Custom base revision
code-review-graph detect-changes --base origin/main # Branch refs use their merge base with HEAD
code-review-graph detect-changes --brief # Compact panel with token-savings estimate
code-review-graph detect-changes --brief --verify # ...and cross-check against tiktoken
code-review-graph detect-changes --churn # Add opt-in change-frequency risk (CRG_CHURN_WINDOW_DAYS, default 90)
# The MCP tools (detect_changes, get_minimal_context) always include it.
# Cached per commit; CRG_CHURN_TIMEOUT (5s) and CRG_CHURN_MAX_COMMITS
# (2000) bound the git log. A repository that cannot answer in time is
# recorded as unavailable for the life of the process, so the timeout is
# paid once, and the response says so: `churn_status` is "unavailable"
# and the summary carries a "Degraded" line.
code-review-graph detect-changes --details all # Add bounded graph context under "details" (JSON only):
# risk, communities, hubs, coupling, impact, gaps, graph.
# Off by default; the GitHub Action's `details` input uses it.
code-review-graph dead-code # Functions/classes with no callers or test references
code-review-graph dead-code --kind Function --file-pattern src/ --json
code-review-graph dead-code --limit 20 # 0, the default, means no limit
# Read-only graph queries (CLI mirrors of the MCP tools)
code-review-graph query callers_of <target> # Patterns: callers_of, callees_of, imports_of, importers_of,
# children_of, tests_for, inheritors_of, file_summary
code-review-graph query path_to <a> --to <b> # How a reaches b, hop by hop
code-review-graph impact [--files F ...] [--depth N] [--max-results N] [--base REF] [--detail-level compact] [--max-files N]
code-review-graph search <query> [--kind Function] [--limit N]
code-review-graph flows [--sort criticality] [--limit N] [--kind KIND]
code-review-graph flow --id ID | --name NAME [--source]
code-review-graph communities [--sort size] [--min-size N]
code-review-graph community --id ID | --name NAME [--members]
code-review-graph architecture [--detail-level minimal|standard]
code-review-graph large-functions [--min-lines N] [--kind Function] [--path SUBSTR] [--limit N]
code-review-graph refactor rename --old-name A --new-name B
code-review-graph refactor dead_code [--kind Function] [--path SUBSTR]
code-review-graph refactor suggest
# Wiki
code-review-graph wiki # Markdown wiki in .code-review-graph/wiki/ (needs an existing graph)
code-review-graph wiki --force # Rewrite every page, even unchanged ones
# Multi-repo
code-review-graph register <path> [--alias name] # Register a repository
code-review-graph unregister <path_or_alias> # Remove from registry
code-review-graph repos # List registered repositories
# Daemon (multi-repo watcher), installed with the package
code-review-graph daemon start [--foreground] # Start the watch daemon
code-review-graph daemon stop # Stop the daemon
code-review-graph daemon restart [--foreground] # Restart the daemon
code-review-graph daemon status # Daemon status and repos
code-review-graph daemon logs [--repo ALIAS] [--follow] [--lines N] # Daemon or per-repo logs (default 50 lines)
code-review-graph daemon add <path> [--alias NAME] # Add a repo to the daemon config
code-review-graph daemon remove <path_or_alias> # Remove a repo from the daemon config
# Evaluation
code-review-graph eval --all # Run every benchmark on every repo config (bare `eval` runs nothing)
code-review-graph eval --benchmark token_efficiency --repo flask # Pick benchmarks and repos (comma-separated); --repo takes config names, not paths
code-review-graph eval --report # Write evaluate/reports/summary.md from evaluate/results and print README tables
# (--output-dir sets the results folder that runs write and --report reads)
code-review-graph eval --benchmark search_quality --embed [--embed-provider local|openai|google|minimax|voyage] [--embed-model M] # --embed builds vectors; agent_baseline,
# search_quality and multi_hop_retrieval need it
# Hook entry points (the hooks install writes run these; they read the hook event JSON on stdin)
code-review-graph hook-update # Update the graph of the repo that holds the edited file
code-review-graph hook-status # Graph status for the event's cwd
code-review-graph enrich # Graph context for a search or read (the --steering hook)
# Server
code-review-graph serve # Start MCP server (stdio)
code-review-graph serve --auto-watch # Also watch for file changes while the server runs
code-review-graph serve --http # Streamable HTTP on 127.0.0.1:5555 (--host, --port)
code-review-graph serve --http --multi-repo # One HTTP server for every registered repository
code-review-graph serve --tools query_graph_tool,detect_changes_tool # Tool allowlist (or CRG_TOOLS)
code-review-graph serve --multi-worktree --repo /path/to/repo # One stdio session, one child per linked worktree
code-review-graph mcp # Alias for serve; accepts --repo, --auto-watch and --multi-worktree--multi-repo lets one serve --http process serve several repositories, so
the embedding model is loaded once. It serves the repositories in the registry
(code-review-graph register <path>), read again on every call, so register
and unregister take effect without a restart. Every tool call must pass
repo_root, the absolute path of a registered repository; list_repos_tool
lists them. A call with no repo_root, a relative path or a path that is not
registered is refused with a message saying so. It never falls back to --repo
or the server's working directory. The usual project-root check still applies.
--multi-repo needs --http and cannot be combined with --repo,
--auto-watch, --multi-worktree or CRG_DATA_DIR. To keep several graphs
current, run the daemon (code-review-graph daemon). Without --multi-repo,
serve --http keeps the old fallback.
--multi-worktree is an opt-in stdio mode for parallel reviews. The server
starts one regular serve --repo <root> child lazily for the primary checkout
and each linked worktree requested through repo_root. Only paths reported by
git worktree list for the same Git common directory are accepted. Each child
uses its worktree's normal .code-review-graph/graph.db or its per-repository
Registry data_dir, so graphs from different commits cannot be mixed.
The mode rejects a process-wide CRG_DATA_DIR, because that would force all
worktrees into one database. Configure external storage with a separate,
non-overlapping Registry data_dir entry per worktree instead; the router
rejects two worktrees that resolve to the same graph database. --multi-worktree
is stdio only and cannot be combined with --http.
Notes:
updateanddetect-changesneed a Git repository or an SVN working copy. In SVN,--basetakes a revision or range such asr100:HEAD; with no revision, both commands use the working-copy edits fromsvn status. In Git,updatewith no--basediffs from the commit the graph was last built at; when that commit is missing (fresh graph, rewritten history, shallow clone) it falls back to a full rebuild.updateprints aWarning:line on stderr naming files that failed to parse. Those files keep their previous graph rows.detect-changes --briefis read-only against the existing graph and takes about a second.update --briefre-parses changed files first, then prints the same panel. Useupdate --briefafter a rebase or a large change set, or when the graph may be stale.status,detect-changes,visualize,wiki,watch,forget,dead-codeand the read-only graph queries (querytorefactorabove) exit 1 when no graph exists and do not create one.forgetanddead-codestill move a legacy top-level.code-review-graph.dbinto.code-review-graph/graph.dbbefore running.One write runs per repository at a time.
build,update,postprocess,forgetandembedtake a lock onwrite.locknext tograph.db; if another one holds it, they say so, do nothing and exit 75. Pass--waitto wait for it instead.watchtakes the lock for each write and keeps its changes to retry if the lock is held.installlists the files it will change and asks about the instruction files (CLAUDE.md,AGENTS.mdand the rest) before it writes anything;-yanswers yes. Ctrl-C at the question stops with nothing written.visualizewithout a seed exports the whole repository, and falls back to community (or file) bubbles once the rendered graph passes 3000 nodes or 9000 edges. With--seed-symbol,--seed-file,--seed-changed,--seed-flowor--path-from/--path-toit exports a neighbourhood instead: the nodes within--depthhops of the seed and the edges among them, with everything else left out of the payload rather than drawn dimmed. A seeded page always uses the full renderer and never aggregates.A hop is any edge other than
CONTAINS, such asCALLS,IMPORTS_FROM,INHERITS,REFERENCESorTESTED_BY.CONTAINSis structural and is not a hop, so a depth-2 neighbourhood of one function does not swallow its whole module; the containingFilenodes are still shipped so clustering and collapse work.--render-depth(default 1) is how many hops are drawn on open. Clicking a node on the edge of what is drawn reveals its next ring,+1 hopreveals a whole ring, and both read from the payload already in the page.--path-from A --path-to Bhighlights the shortest path throughCALLS,IMPORTS_FROMandINHERITS, following edge direction where one exists and falling back to an undirected route otherwise. The page says which it used.The default output is still a single self-contained
graph.htmlyou can email.--sidecarmoves the payload intograph.data.jsnext to it, which is only worth it for a large neighbourhood.--max-nodes(default 1500) is a hard cap on the nodes in the payload, not a hint. The outermost hop goes first; once the outer hops are gone the seed set itself is trimmed, best-connected first by whole-graph degree, ties broken by name so two runs of the same command agree. A--seed-changedrun over a large review is exactly the case where the seed set is the large thing, so the command prints how many seed nodes it dropped and the page says so too. The nodes on a--path-from/--path-toanswer are placed first and are never dropped; a--max-nodesbelow the path length is refused rather than half-answered.Flag combinations that cannot mean anything are refused, not ignored:
--seed-changed-basewithout--seed-changed;--depth,--render-depthor--max-nodeswithout a seed; any seed or tuning flag with a non-html--format;--sidecarwith a non-html--format;--mode communityor--mode filewith a seed (they aggregate, which is the opposite of a neighbourhood); and a--render-depthabove--depth.install(with--platform all, the default, orclaude,codexorqoder) appends a Gitpre-commithook that updates the graph and prints a risk summary before each commit. The hook skips linked worktrees unlessCRG_HOOK_WORKTREES=1is set, so a worktree does not build a second graph for another branch.installalso appendspost-checkout,post-mergeandpost-rewriteGit hooks that runupdateafter a branch switch, a merge and a rebase, with the same worktree rule. An existing hook written in a language other than shell is left alone,--no-hooksskips all four, anduninstallremoves them.For an empty or incomplete graph, run
code-review-graph build. See docs/TROUBLESHOOTING.md, "Empty or incomplete graph".context-historyreads a log that is off by default. SetCRG_CONTEXT_HISTORY=1where the MCP server or the CLI runs to turn it on. See USAGE.md.
Standalone Daemon CLI (crg-daemon)
crg-daemon is installed with code-review-graph and mirrors the
code-review-graph daemon subcommands:
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 # Daemon status, repos, and process liveness
crg-daemon logs [--repo ALIAS] [-f] [-n N] # Tail daemon or per-repo log files
crg-daemon add <path> [--alias NAME] # Add a repository to watch.toml
crg-daemon remove <path_or_alias> # Remove a repository from watch.tomlConfiguration
The daemon reads ~/.code-review-graph/watch.toml (or $CRG_HOME/watch.toml):
[daemon]
session_name = "crg-watch" # logical daemon name
log_dir = "/home/user/.code-review-graph/logs" # absolute path; ~ is not expanded (default: ~/.code-review-graph/logs, or $CRG_HOME/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 with
subprocess.Popen. It polls the config file and starts or stops children as
repos are added or removed. A health check every 30 seconds restarts dead
watchers. No tmux or screen is needed.
512 files · 480 not served here
- .beads/.gitignore (not served here)
- .beads/README.md (not served here)
- .beads/config.yaml (not served here)
- .beads/hooks/post-checkout (not served here)
- .beads/hooks/post-merge (not served here)
- .beads/hooks/pre-commit (not served here)
- .beads/hooks/pre-push (not served here)
- .beads/hooks/prepare-commit-msg (not served here)
- .beads/metadata.json (not served here)
- .gitattributes (not served here)
- .github/ISSUE_TEMPLATE/bug_report.yml (not served here)
- .github/ISSUE_TEMPLATE/config.yml (not served here)
- .github/ISSUE_TEMPLATE/feature_request.yml (not served here)
- .github/ISSUE_TEMPLATE/platform_request.yml (not served here)
- .github/PULL_REQUEST_TEMPLATE.md (not served here)
- .github/copilot-instructions.md
- .github/dependabot.yml (not served here)
- .github/requirements-minimum.txt (not served here)
- .github/workflows/ci.yml (not served here)
- .github/workflows/eval.yml (not served here)
- .github/workflows/pr-review-comment.yml (not served here)
- .github/workflows/pr-review.yml (not served here)
- .github/workflows/promote.yml (not served here)
- .github/workflows/promotion-gate.yml (not served here)
- .github/workflows/publish.yml (not served here)
- .gitignore (not served here)
- .mcp.json
- AGENTS.md
- ATTRIBUTION.md
- AUTOGINEER-RUNS.md
- CHANGELOG.md
- CLAUDE.md
- CODE_OF_CONDUCT.md
- CONTRIBUTING.md
- Dockerfile (not served here)
- GEMINI.md
- LICENSE
- README.hi-IN.md
- README.ja-JP.md
- README.ko-KR.md
- README.md
- README.zh-CN.md
- SECURITY.md
- action.yml (not served here)
- code-review-graph-vscode/.gitignore (not served here)
- code-review-graph-vscode/.vscodeignore (not served here)
- code-review-graph-vscode/CHANGELOG.md (not served here)
- code-review-graph-vscode/LICENSE
- code-review-graph-vscode/README.md (not served here)
- code-review-graph-vscode/esbuild.mjs (not served here)
- code-review-graph-vscode/media/icons/graph.svg (not served here)
- code-review-graph-vscode/media/icons/icon.png (not served here)
- code-review-graph-vscode/media/walkthrough/build.md (not served here)
- code-review-graph-vscode/media/walkthrough/explore.md (not served here)
- code-review-graph-vscode/media/walkthrough/install.md (not served here)
- code-review-graph-vscode/package-lock.json (not served here)
- code-review-graph-vscode/package.json (not served here)
- code-review-graph-vscode/src/backend/cli.ts (not served here)
- code-review-graph-vscode/src/backend/sqlite.ts (not served here)
- code-review-graph-vscode/src/backend/watcher.ts (not served here)
- code-review-graph-vscode/src/extension.ts (not served here)
- code-review-graph-vscode/src/features/blastRadius.ts (not served here)
- code-review-graph-vscode/src/features/cursorResolver.ts (not served here)
- code-review-graph-vscode/src/features/navigation.ts (not served here)
- code-review-graph-vscode/src/features/reviewAssistant.ts (not served here)
- code-review-graph-vscode/src/features/scmDecorations.ts (not served here)
- code-review-graph-vscode/src/features/search.ts (not served here)
- code-review-graph-vscode/src/onboarding/installer.ts (not served here)
- code-review-graph-vscode/src/onboarding/welcome.ts (not served here)
- code-review-graph-vscode/src/views/graphWebview.ts (not served here)
- code-review-graph-vscode/src/views/statusBar.ts (not served here)
- code-review-graph-vscode/src/views/treeItems.ts (not served here)
- code-review-graph-vscode/src/views/treeView.ts (not served here)
- code-review-graph-vscode/src/webview/graph.ts (not served here)
- code-review-graph-vscode/test/sqlite.test.ts (not served here)
- code-review-graph-vscode/tsconfig.json (not served here)
- code_review_graph/__init__.py (not served here)
- code_review_graph/__main__.py (not served here)
- code_review_graph/_legacy_instructions.py (not served here)
- code_review_graph/analysis.py (not served here)
- code_review_graph/aop_resolver.py (not served here)
- code_review_graph/assets/code-review-graph/SKILL.md (not served here)
- code_review_graph/assets/code-review-graph/agents/openai.yaml (not served here)
- code_review_graph/assets/code-review-graph/scripts/crg_readonly.py (not served here)
- code_review_graph/assets/d3.v7.min.js (not served here)
- code_review_graph/build_state.py (not served here)
- code_review_graph/change_details.py (not served here)
- code_review_graph/changes.py (not served here)
- code_review_graph/cli.py (not served here)
- code_review_graph/codex_skill.py (not served here)
- code_review_graph/communities.py (not served here)
- code_review_graph/config_keys.py (not served here)
- code_review_graph/constants.py (not served here)
- code_review_graph/context_history.py (not served here)
- code_review_graph/context_savings.py (not served here)
- code_review_graph/csharp_resolver.py (not served here)
- code_review_graph/custom_languages.py (not served here)
- code_review_graph/daemon.py (not served here)
- code_review_graph/daemon_cli.py (not served here)
- code_review_graph/docs/LLM-OPTIMIZED-REFERENCE.md (not served here)
- code_review_graph/dsh.py (not served here)
- code_review_graph/embeddings.py (not served here)
- code_review_graph/enrich.py (not served here)
- code_review_graph/errors.py (not served here)
- code_review_graph/eval/__init__.py (not served here)
- code_review_graph/eval/benchmarks/__init__.py (not served here)
- code_review_graph/eval/benchmarks/agent_baseline.py (not served here)
- code_review_graph/eval/benchmarks/build_performance.py (not served here)
- code_review_graph/eval/benchmarks/flow_completeness.py (not served here)
- code_review_graph/eval/benchmarks/impact_accuracy.py (not served here)
- code_review_graph/eval/benchmarks/incremental_fidelity.py (not served here)
- code_review_graph/eval/benchmarks/multi_hop_retrieval.py (not served here)
- code_review_graph/eval/benchmarks/search_quality.py (not served here)
- code_review_graph/eval/benchmarks/token_efficiency.py (not served here)
- code_review_graph/eval/configs/code-review-graph.yaml (not served here)
- code_review_graph/eval/configs/express.yaml (not served here)
- code_review_graph/eval/configs/fastapi.yaml (not served here)
- code_review_graph/eval/configs/flask.yaml (not served here)
- code_review_graph/eval/configs/gin.yaml (not served here)
- code_review_graph/eval/configs/httpx.yaml (not served here)
- code_review_graph/eval/reporter.py (not served here)
- code_review_graph/eval/runner.py (not served here)
- code_review_graph/eval/scorer.py (not served here)
- code_review_graph/eval/token_benchmark.py (not served here)
- code_review_graph/event_resolver.py (not served here)
- code_review_graph/exports.py (not served here)
- code_review_graph/flows.py (not served here)
- code_review_graph/forget.py (not served here)
- code_review_graph/graph.py (not served here)
- code_review_graph/graph_diff.py (not served here)
- code_review_graph/hcl_resolver.py (not served here)
- code_review_graph/hints.py (not served here)
- code_review_graph/hook_update.py (not served here)
- code_review_graph/http_origin_guard.py (not served here)
- code_review_graph/incremental.py (not served here)
- code_review_graph/inheritance.py (not served here)
- code_review_graph/install_gitignore.py (not served here)
- code_review_graph/install_plan.py (not served here)
- code_review_graph/jedi_resolver.py (not served here)
- code_review_graph/jsonc.py (not served here)
- code_review_graph/locking.py (not served here)
- code_review_graph/main.py (not served here)
- code_review_graph/memory.py (not served here)
- code_review_graph/migrations.py (not served here)
- code_review_graph/multi_worktree.py (not served here)
- code_review_graph/neighbourhood.py (not served here)
- code_review_graph/npm_alias_resolver.py (not served here)
- code_review_graph/parser.py (not served here)
- code_review_graph/postprocessing.py (not served here)
- code_review_graph/prompts.py (not served here)
- code_review_graph/python_resolver.py (not served here)
- code_review_graph/refactor.py (not served here)
- code_review_graph/registry.py (not served here)
- code_review_graph/rescript_resolver.py (not served here)
- code_review_graph/scoped_resolver.py (not served here)
- code_review_graph/search.py (not served here)
- code_review_graph/skills.py (not served here)
- code_review_graph/spring_resolver.py (not served here)
- code_review_graph/stdio.py (not served here)
- code_review_graph/temporal_resolver.py (not served here)
- code_review_graph/token_benchmark.py (not served here)
- code_review_graph/tools/__init__.py (not served here)
- code_review_graph/tools/_common.py (not served here)
- code_review_graph/tools/analysis_tools.py (not served here)
- code_review_graph/tools/build.py (not served here)
- code_review_graph/tools/community_tools.py (not served here)
- code_review_graph/tools/context.py (not served here)
- code_review_graph/tools/docs.py (not served here)
- code_review_graph/tools/flows_tools.py (not served here)
- code_review_graph/tools/query.py (not served here)
- code_review_graph/tools/refactor_tools.py (not served here)
- code_review_graph/tools/registry_tools.py (not served here)
- code_review_graph/tools/review.py (not served here)
- code_review_graph/tsconfig_resolver.py (not served here)
- code_review_graph/uncertainty.py (not served here)
- code_review_graph/uninstall.py (not served here)
- code_review_graph/visualization.py (not served here)
- code_review_graph/wiki.py (not served here)
- code_review_graph/workspace_resolver.py (not served here)
- diagrams/context-savings-demo.gif (not served here)
- diagrams/context-savings-demo.tape (not served here)
- diagrams/diagram1_before_vs_after.png (not served here)
- diagrams/diagram2_architecture_pipeline.png (not served here)
- diagrams/diagram3_blast_radius.png (not served here)
- diagrams/diagram4_incremental_update.png (not served here)
- diagrams/diagram5_benchmark_board.png (not served here)
- diagrams/diagram6_monorepo_funnel.png (not served here)
- diagrams/diagram7_mcp_integration_flow.png (not served here)
- diagrams/diagram8_supported_platforms.png (not served here)
- diagrams/diagram9_language_coverage.png (not served here)
- diagrams/generate_diagrams.py (not served here)
- docs/COMMANDS.md
- docs/CUSTOM_LANGUAGES.md
- docs/FAQ.md
- docs/FEATURES.md
- docs/GITHUB_ACTION.md
- docs/INDEX.md
- docs/LEGAL.md
- docs/REPRODUCING.md
- docs/ROADMAP.md
- docs/TROUBLESHOOTING.md
- docs/USAGE.md
- docs/architecture.md
- docs/schema.md
- evaluate/results/code-review-graph_agent_baseline_2026-09-22.csv (not served here)
- evaluate/results/code-review-graph_impact_accuracy_2026-08-02.csv (not served here)
- evaluate/results/code-review-graph_multi_hop_retrieval_2026-05-25.csv (not served here)
- evaluate/results/code-review-graph_token_efficiency_2026-05-25.csv (not served here)
- evaluate/results/express_agent_baseline_2026-09-22.csv (not served here)
- evaluate/results/express_impact_accuracy_2026-08-02.csv (not served here)
- evaluate/results/express_multi_hop_retrieval_2026-05-25.csv (not served here)
- evaluate/results/express_token_efficiency_2026-05-25.csv (not served here)
- evaluate/results/fastapi_agent_baseline_2026-09-22.csv (not served here)
- evaluate/results/fastapi_impact_accuracy_2026-08-02.csv (not served here)
- evaluate/results/fastapi_multi_hop_retrieval_2026-05-25.csv (not served here)
- evaluate/results/fastapi_token_efficiency_2026-05-25.csv (not served here)
- evaluate/results/flask_agent_baseline_2026-09-22.csv (not served here)
- evaluate/results/flask_impact_accuracy_2026-08-02.csv (not served here)
- evaluate/results/flask_multi_hop_retrieval_2026-05-25.csv (not served here)
- evaluate/results/flask_token_efficiency_2026-05-25.csv (not served here)
- evaluate/results/gin_agent_baseline_2026-09-22.csv (not served here)
- evaluate/results/gin_impact_accuracy_2026-08-02.csv (not served here)
- evaluate/results/gin_multi_hop_retrieval_2026-05-25.csv (not served here)
- evaluate/results/gin_token_efficiency_2026-05-25.csv (not served here)
- evaluate/results/httpx_agent_baseline_2026-09-22.csv (not served here)
- evaluate/results/httpx_impact_accuracy_2026-08-02.csv (not served here)
- evaluate/results/httpx_multi_hop_retrieval_2026-05-25.csv (not served here)
- evaluate/results/httpx_token_efficiency_2026-05-25.csv (not served here)
- hooks/hooks.json
- hooks/session-start.sh (not served here)
- pyproject.toml (not served here)
- scripts/audit_python_imports.py (not served here)
- scripts/diagnose_pypi_connectivity.py (not served here)
- scripts/generate_documentary_skills.py (not served here)
- scripts/print_minimum_dependencies.py (not served here)
- scripts/promotion_gate.py (not served here)
- scripts/render_pr_comment.py (not served here)
- scripts/smoke_google_embeddings.py (not served here)
- skills/build-graph/SKILL.md (not served here)
- skills/debug-issue/SKILL.md (not served here)
- skills/explore-codebase/SKILL.md (not served here)
- skills/refactor-safely/SKILL.md (not served here)
- skills/review-changes/SKILL.md (not served here)
- skills/review-delta/SKILL.md (not served here)
- skills/review-pr/SKILL.md (not served here)
- tests/__init__.py (not served here)
- tests/conftest.py (not served here)
- tests/corpus_baselines.json (not served here)
- tests/determinism_build_runner.py (not served here)
- tests/determinism_dump.py (not served here)
- tests/fixtures/KafkaPatterns.java (not served here)
- tests/fixtures/MarkdownMsg.tsx (not served here)
- tests/fixtures/Sample.cs (not served here)
- tests/fixtures/SampleJava.java (not served here)
- tests/fixtures/SpringDI.java (not served here)
- tests/fixtures/TemporalWorkflow.java (not served here)
- tests/fixtures/__tests__/UserService.ts (not served here)
- tests/fixtures/alias_importer.ts (not served here)
- tests/fixtures/caller_example.py (not served here)
- tests/fixtures/cpp_qt_headers/MyWidget.cpp (not served here)
- tests/fixtures/cpp_qt_headers/MyWidget.h (not served here)
- tests/fixtures/cpp_qt_headers/MyWidgetPlain.cpp (not served here)
- tests/fixtures/cpp_qt_headers/MyWidgetPlain.h (not served here)
- tests/fixtures/detect_changes_sample.json (not served here)
- tests/fixtures/graphml_xsd/SOURCE.txt (not served here)
- tests/fixtures/graphml_xsd/graphml-attributes.xsd (not served here)
- tests/fixtures/graphml_xsd/graphml-parseinfo.xsd (not served here)
- tests/fixtures/graphml_xsd/graphml-structure.xsd (not served here)
- tests/fixtures/graphml_xsd/graphml.xsd (not served here)
- tests/fixtures/graphml_xsd/xlink.xsd (not served here)
- tests/fixtures/multi_call_example.py (not served here)
- tests/fixtures/playbooks/sample_ansible_playbook.yml (not served here)
- tests/fixtures/roles/myrole/meta/main.yml (not served here)
- tests/fixtures/sample.R (not served here)
- tests/fixtures/sample.c (not served here)
- tests/fixtures/sample.cpp (not served here)
- tests/fixtures/sample.dart (not served here)
- tests/fixtures/sample.ex (not served here)
- tests/fixtures/sample.gd (not served here)
- tests/fixtures/sample.hh (not served here)
- tests/fixtures/sample.jl (not served here)
- tests/fixtures/sample.kt (not served here)
- tests/fixtures/sample.liquid (not served here)
- tests/fixtures/sample.lua (not served here)
- tests/fixtures/sample.luau (not served here)
- tests/fixtures/sample.m (not served here)
- tests/fixtures/sample.nix (not served here)
- tests/fixtures/sample.php (not served here)
- tests/fixtures/sample.pl (not served here)
- tests/fixtures/sample.rb (not served here)
- tests/fixtures/sample.res (not served here)
- tests/fixtures/sample.resi (not served here)
- tests/fixtures/sample.scala (not served here)
- tests/fixtures/sample.sh (not served here)
- tests/fixtures/sample.sol (not served here)
- tests/fixtures/sample.sql (not served here)
- tests/fixtures/sample.sv (not served here)
- tests/fixtures/sample.swift (not served here)
- tests/fixtures/sample.tf (not served here)
- tests/fixtures/sample.xs (not served here)
- tests/fixtures/sample_bun.test.ts (not served here)
- tests/fixtures/sample_callback_refs.py (not served here)
- tests/fixtures/sample_config.json (not served here)
- tests/fixtures/sample_databricks_export.py (not served here)
- tests/fixtures/sample_databricks_notebook.ipynb (not served here)
- tests/fixtures/sample_dead_guard.c (not served here)
- tests/fixtures/sample_dead_guard.go (not served here)
- tests/fixtures/sample_dead_guard.rs (not served here)
- tests/fixtures/sample_dead_guard.ts (not served here)
- tests/fixtures/sample_dead_guard_impl.rs (not served here)
- tests/fixtures/sample_go.go (not served here)
- tests/fixtures/sample_html.html (not served here)
- tests/fixtures/sample_lib.sh (not served here)
- tests/fixtures/sample_map_dispatch.py (not served here)
- tests/fixtures/sample_map_dispatch.ts (not served here)
- tests/fixtures/sample_mocha.test.ts (not served here)
- tests/fixtures/sample_module.nix (not served here)
- tests/fixtures/sample_notebook.ipynb (not served here)
- tests/fixtures/sample_plsql.sql (not served here)
- tests/fixtures/sample_python.py (not served here)
- tests/fixtures/sample_rust.rs (not served here)
- tests/fixtures/sample_typescript.ts (not served here)
- tests/fixtures/sample_vitest.test.ts (not served here)
- tests/fixtures/sample_vue.vue (not served here)
- tests/fixtures/sample_xslt.xsl (not served here)
- tests/fixtures/sample_xslt_common.xsl (not served here)
- tests/fixtures/sample_zig.zig (not served here)
- tests/fixtures/sample_zig_util.zig (not served here)
- tests/fixtures/src/lib/utils.ts (not served here)
- tests/fixtures/tasks/sample_ansible_tasks.yml (not served here)
- tests/fixtures/test_sample.R (not served here)
- tests/fixtures/test_sample.py (not served here)
- tests/fixtures/tsconfig.json (not served here)
- tests/import_audit.py (not served here)
- tests/mcp_client_compat.py (not served here)
- tests/real_repo_corpus.py (not served here)
- tests/test_action_e2e.py (not served here)
- tests/test_action_render.py (not served here)
- tests/test_affected_flows_limits.py (not served here)
- tests/test_agent_transparency.py (not served here)
- tests/test_ansible_parser.py (not served here)
- tests/test_aop_resolution_edge_cases.py (not served here)
- tests/test_aop_resolver.py (not served here)
- tests/test_build_tool_errors.py (not served here)
- tests/test_call_site_provenance.py (not served here)
- tests/test_callback_context_preservation.py (not served here)
- tests/test_change_details.py (not served here)
- tests/test_changes.py (not served here)
- tests/test_changes_path_normalization.py (not served here)
- tests/test_churn_runtime.py (not served here)
- tests/test_cjs_and_kts_extensions.py (not served here)
- tests/test_cli.py (not served here)
- tests/test_cli_install.py (not served here)
- tests/test_cli_reconciliation.py (not served here)
- tests/test_cli_stdio_encoding.py (not served here)
- tests/test_cli_surface.py (not served here)
- tests/test_cli_tool_commands.py (not served here)
- tests/test_codeql_url_substring.py (not served here)
- tests/test_codex_skill_wrapper.py (not served here)
- tests/test_commonjs_imports.py (not served here)
- tests/test_communities.py (not served here)
- tests/test_compact_impact.py (not served here)
- tests/test_composer_vendor_ignore.py (not served here)
- tests/test_concurrency_and_daemon.py (not served here)
- tests/test_conftest_gates.py (not served here)
- tests/test_context_history.py (not served here)
- tests/test_context_savings.py (not served here)
- tests/test_cpp_header_prefilter.py (not served here)
- tests/test_cpp_overload_identity.py (not served here)
- tests/test_cpp_qt_headers.py (not served here)
- tests/test_csharp_dead_code_overrides.py (not served here)
- tests/test_csharp_dotted_query_targets.py (not served here)
- tests/test_csharp_namespace_binding_edges.py (not served here)
- tests/test_csharp_namespaces.py (not served here)
- tests/test_custom_languages.py (not served here)
- tests/test_d3_asset_line_endings.py (not served here)
- tests/test_daemon.py (not served here)
- tests/test_daemon_cli_followups.py (not served here)
- tests/test_daemon_stop_confirmation.py (not served here)
- tests/test_daemon_stop_escalation.py (not served here)
- tests/test_dbt_parser.py (not served here)
- tests/test_dead_code_edge_evidence.py (not served here)
- tests/test_dead_code_query_scaling.py (not served here)
- tests/test_dependency_floors.py (not served here)
- tests/test_derived_data_fallbacks.py (not served here)
- tests/test_determinism.py (not served here)
- tests/test_diagnose_pypi_connectivity.py (not served here)
- tests/test_docstring_embeddings.py (not served here)
- tests/test_document_skills.py (not served here)
- tests/test_documentation.py (not served here)
- tests/test_dsh.py (not served here)
- tests/test_e2e_mcp_client.py (not served here)
- tests/test_e2e_real_repo.py (not served here)
- tests/test_edit_hook_follows_the_edited_file.py (not served here)
- tests/test_elixir_multi_clause.py (not served here)
- tests/test_embedding_initialization.py (not served here)
- tests/test_embedding_refresh.py (not served here)
- tests/test_embedding_refresh_skip_notice.py (not served here)
- tests/test_embeddings.py (not served here)
- tests/test_enrich.py (not served here)
- tests/test_env_config.py (not served here)
- tests/test_eval.py (not served here)
- tests/test_export_formats.py (not served here)
- tests/test_failure_reporting.py (not served here)
- tests/test_flow_source_stays_in_repo.py (not served here)
- tests/test_flows.py (not served here)
- tests/test_follow_symlinks.py (not served here)
- tests/test_forget.py (not served here)
- tests/test_forget_legacy_db.py (not served here)
- tests/test_forget_parity.py (not served here)
- tests/test_freshness_metadata.py (not served here)
- tests/test_fts_delta.py (not served here)
- tests/test_fts_sync.py (not served here)
- tests/test_genai_packaging.py (not served here)
- tests/test_generic_yaml_edge_cases.py (not served here)
- tests/test_git_hook_block_helpers.py (not served here)
- tests/test_git_hook_worktree.py (not served here)
- tests/test_git_sync_hooks.py (not served here)
- tests/test_go_embeddings.py (not served here)
- tests/test_go_imports.py (not served here)
- tests/test_go_receiver_resolution.py (not served here)
- tests/test_graph.py (not served here)
- tests/test_graph_update_contracts.py (not served here)
- tests/test_hcl_parser.py (not served here)
- tests/test_hcl_provider_aliases.py (not served here)
- tests/test_header_dead_branch_scan.py (not served here)
- tests/test_hermes_install.py (not served here)
- tests/test_hermes_yaml_preservation.py (not served here)
- tests/test_hints.py (not served here)
- tests/test_hook_update.py (not served here)
- tests/test_html_parser.py (not served here)
- tests/test_http_multi_repo.py (not served here)
- tests/test_http_origin_guard.py (not served here)
- tests/test_identity_pending_ignored.py (not served here)
- tests/test_identity_retry.py (not served here)
- tests/test_import_cycles.py (not served here)
- tests/test_import_scope.py (not served here)
- tests/test_import_target_integrity.py (not served here)
- tests/test_incremental.py (not served here)
- tests/test_incremental_dependent_derived_state.py (not served here)
- tests/test_incremental_fidelity.py (not served here)
- tests/test_incremental_flow_lifecycle.py (not served here)
- tests/test_incremental_reexport_regression.py (not served here)
- tests/test_incremental_unindexed_change_memory.py (not served here)
- tests/test_inherited_calls.py (not served here)
- tests/test_install_gitignore_block.py (not served here)
- tests/test_install_outputs_are_ignored.py (not served here)
- tests/test_install_preview.py (not served here)
- tests/test_installer_ownership.py (not served here)
- tests/test_integration_git.py (not served here)
- tests/test_integration_v2.py (not served here)
- tests/test_java_call_references.py (not served here)
- tests/test_java_name_extraction.py (not served here)
- tests/test_js_module_resolution.py (not served here)
- tests/test_js_wrapped_functions.py (not served here)
- tests/test_json_parser.py (not served here)
- tests/test_jsonc_removals.py (not served here)
- tests/test_julia_reconciliation.py (not served here)
- tests/test_jvm_implementors.py (not served here)
- tests/test_keyword_argument_callbacks.py (not served here)
- tests/test_kotlin_import_comments.py (not served here)
- tests/test_kotlin_imports.py (not served here)
- tests/test_language_reconciliation.py (not served here)
- tests/test_legacy_graph_paths.py (not served here)
- tests/test_liquid.py (not served here)
- tests/test_main.py (not served here)
- tests/test_main_lazy_tool_imports.py (not served here)
- tests/test_mcp_auto_watch.py (not served here)
- tests/test_mcp_auto_watch_startup_window.py (not served here)
- tests/test_mcp_stdio_shutdown.py (not served here)
- tests/test_mcp_surface.py (not served here)
- tests/test_mcp_watch_store_shutdown.py (not served here)
- tests/test_memory.py (not served here)
- tests/test_migrations.py (not served here)
- tests/test_missing_registered_data_dir.py (not served here)
- tests/test_multi_worktree.py (not served here)
- tests/test_multilang.py (not served here)
- tests/test_neighbourhood.py (not served here)
- tests/test_neighbourhood_bounded_reads.py (not served here)
- tests/test_neighbourhood_browser.py (not served here)
- tests/test_nested_method_identities.py (not served here)
- tests/test_nested_scope_upgrade.py (not served here)
- tests/test_notebook.py (not served here)
- tests/test_npm_alias_resolver.py (not served here)
- tests/test_opencode_v1_plugin_contract.py (not served here)
- tests/test_opencode_v2_plugin_contract.py (not served here)
- tests/test_optional_dependencies.py (not served here)
- tests/test_packaging.py (not served here)
- tests/test_parser.py (not served here)
- tests/test_parser_grammar_contract.py (not served here)
- tests/test_parser_load_probe.py (not served here)
- tests/test_php_guessed_test_links.py (not served here)
- tests/test_php_includes.py (not served here)
- tests/test_php_laravel.py (not served here)
- tests/test_php_scoped_calls.py (not served here)
- tests/test_platform_lifecycle.py (not served here)
- tests/test_postprocessing.py (not served here)
- tests/test_powershell_extraction.py (not served here)
- tests/test_pr803_poisoned_graph_recovery.py (not served here)
- tests/test_pr_review_workflows.py (not served here)
- tests/test_promotion_gate.py (not served here)
- tests/test_promotion_is_manual.py (not served here)
- tests/test_prompts.py (not served here)
- tests/test_public_tool_hints.py (not served here)
- tests/test_pure_deletion_review.py (not served here)
- tests/test_python_module_alias_shims.py (not served here)
- tests/test_python_property_reads.py (not served here)
- tests/test_python_reachability.py (not served here)
- tests/test_python_receiver_evidence.py (not served here)
- tests/test_python_receiver_shapes.py (not served here)
- tests/test_python_relative_imports.py (not served here)
- tests/test_python_star_imports.py (not served here)
Advantages
- Drastically reduces token usage by providing only the relevant code context for a specific change. evidence: README.md
- Maintains data privacy by storing the knowledge graph locally in a SQLite file without requiring cloud services. evidence: README.md
Limitations
- The graph may become stale if the background update process is interrupted or fails to detect changes. evidence: .github/copilot-instructions.md
- Requires Python 3.10+ and may need manual configuration for environments where MCP is not natively supported. evidence: README.md
Security
Risk level: Medium risk
This brain runs
MCP servers (2)
.mcp.json(code-review-graph):uvx code-review-graph serveREADME.md(code-review-graph):code-review-graph serve --tools query_graph_tool,semantic_search_nodes_tool,detect_changes_tool,get_review_context_tool
Agent instructions that run or fetch something (38)
AGENTS.md:Other commands that may prompt: `scp` and `ssh` (`-o BatchMode=yes`), `apt-get` (`-y`), `brew` (`HOMEBREW_NO_AUTO_UPDATE=1`).· uses the networkCLAUDE.md:`locking.py`: the cross-process lock that lets one graph write run at a time.CLAUDE.md:`skills.py`: `install` (platform MCP configs, hooks, skills, instruction blocks).CLAUDE.md:`install_plan.py`: every install read and write goes through it, so `install --dry-run` can show each change as a diff and a real install writes nothing before its question is answered.CLAUDE.md:`jsonc.py`: comment-preserving JSONC tokenising and splices, shared by install and uninstall so neither flattens a commented config.CLAUDE.md:`install_gitignore.py`: the opt-in `install --gitignore` block listing the files a run created.CLAUDE.md:`uninstall.py`: reverses `install`.CLAUDE.md:uv run pytest tests/ --tb=short -qCLAUDE.md:uv run ruff check code_review_graph/CLAUDE.md:uv run mypy code_review_graph/ --ignore-missing-imports --no-strict-optionalCLAUDE.md:uv run code-review-graph build # full buildCLAUDE.md:uv run code-review-graph update # incremental updateCLAUDE.md:uv run code-review-graph status # graph statisticsCLAUDE.md:uv run code-review-graph detect-changes # risk-scored change analysis (read-only)CLAUDE.md:uv run code-review-graph forget PATH # drop files from the graphCLAUDE.md:uv run code-review-graph serve # MCP server (stdio; --http for localhost)CLAUDE.md:uv run code-review-graph wiki # Markdown wikiCLAUDE.md:uv run code-review-graph register <path> # add a repo to the multi-repo registryCLAUDE.md:uv run code-review-graph eval --all # benchmarksCLAUDE.md:uv run code-review-graph --help # full command listCLAUDE.md:Install and platforms: `test_skills.py`, `test_cli_install.py`, `test_uninstall.py`, `test_git_hook_worktree.py`, `test_hermes_install.py`, `test_qoder_bundled_skills.py`, `test_installer_ownership.py` (what install may rewrite and what belongs to the user), `test_install_gitignore_blo …[+283 chars]CLAUDE.md:Builds a wheel and an sdist with `python -m build`, installs each into its own virtual environment, and drives the installed program with the checkout out of reach.CLAUDE.md:Run it before a release with `uv run --python 3.13 python -m pytest tests/test_packaging.py -m packaging`.CLAUDE.md:upgrade-path: manual run only.code_review_graph/assets/code-review-graph/SKILL.md:Resolve the active repository root from the workspace or `git rev-parse --show-toplevel`.code_review_graph/assets/code-review-graph/SKILL.md:Without MCP, run the bundled helper’s `status` command (it forwards `--json` to the CLI).code_review_graph/assets/code-review-graph/SKILL.md:python3 <skill-root>/scripts/crg_readonly.py status --repo "<repo-root>"code_review_graph/assets/code-review-graph/SKILL.md:python3 <skill-root>/scripts/crg_readonly.py architecture --repo "<repo-root>"code_review_graph/assets/code-review-graph/SKILL.md:python3 <skill-root>/scripts/crg_readonly.py search "<query>" --repo "<repo-root>"code_review_graph/assets/code-review-graph/SKILL.md:python3 <skill-root>/scripts/crg_readonly.py query callers_of "<symbol>" --repo "<repo-root>"code_review_graph/assets/code-review-graph/SKILL.md:python3 <skill-root>/scripts/crg_readonly.py query path_to "<symbol>" --to "<other>" --repo "<repo-root>"code_review_graph/assets/code-review-graph/SKILL.md:python3 <skill-root>/scripts/crg_readonly.py impact --repo "<repo-root>"code_review_graph/assets/code-review-graph/SKILL.md:python3 <skill-root>/scripts/crg_readonly.py detect-changes --brief --repo "<repo-root>"code_review_graph/assets/code-review-graph/SKILL.md:A Windows-mounted path such as `/mnt/e/...` is not by itself a failure: verify that the selected runtime can execute the CLI and access that path.skills/build-graph/SKILL.md:Hooks installed by `code-review-graph install` run an update after each edit, before each commit and after a branch switch, merge or rebase, so manual builds are rarely needed.skills/review-delta/SKILL.md:Changes come from `git diff` against `HEAD~1`;skills/review-pr/SKILL.md:The graph tools read the files on disk, so check out what you review first: `git switch <branch>`, or for a PR `git fetch origin pull/<n>/head:pr-<n>` then `git switch pr-<n>`, then call `build_or_update_graph_tool()`.· uses the networkskills/review-pr/SKILL.md:Diff it against `main` (or `master`) with `git diff --name-only main...HEAD`, and use that ref as `base` below.
Hooks (3)
hooks/hooks.json(SessionStart):cat >/dev/null || true; command -v code-review-graph >/dev/null 2>&1 || exit 0; git rev-parse --git-dir >/dev/null 2>&1 && code-review-graph status --repo "$(git rev-parse --show-toplevel 2>/dev/null)" || echo 'Not a git repo, skipping'hooks/hooks.json(PostToolUse):cat >/dev/null || true; code-review-graph build >/dev/null 2>&1 &hooks/hooks.json(PostToolUse):command -v code-review-graph >/dev/null 2>&1 && code-review-graph hook-update; cat >/dev/null || true
- Medium risk: runs-mcp-server-unpinned-runner (.mcp.json)
- Medium risk: runs-instruction-instruction-network (AGENTS.md:17)
- Medium risk: runs-instruction-instruction-network (skills/review-pr/SKILL.md:13)
Attribution and licence
- Created by
- Tirth
- Declared authors
- Tirth (declared in pyproject.toml)
- Publisher
- Tirth Kanani (Community)
- Source
- tirth8205/code-review-graph · commit e7f2fb2
- Licence
- MIT License (files served here)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Tirth.
Install from source
480 of its files are not served here, so there is no ZIP. tirth8205/code-review-graph at e7f2fb2