chore: restore original directory structure (project under code-review-graph-main/)
This commit is contained in:
@@ -0,0 +1,484 @@
|
||||
# All Available Commands
|
||||
|
||||
## Skills and Slash Commands
|
||||
|
||||
These commands are installed for clients that support project skills or slash-command style workflows.
|
||||
|
||||
### `/code-review-graph:build-graph`
|
||||
Build or update the knowledge graph.
|
||||
- First time: performs a full build
|
||||
- Subsequent: incremental update (only changed files)
|
||||
|
||||
### `/code-review-graph:review-delta`
|
||||
Review only changes since last commit.
|
||||
- Auto-detects changed files via git diff
|
||||
- Computes blast radius (2-hop default)
|
||||
- Generates structured review with guidance
|
||||
|
||||
### `/code-review-graph:review-pr`
|
||||
Review a PR or branch diff.
|
||||
- Uses main/master as base
|
||||
- Full impact analysis across all PR commits
|
||||
- Structured output with risk assessment
|
||||
|
||||
### `/code-review-graph:unified-review`
|
||||
Three-layer unified code review (CRG graph context + ai-code-review scoring + gstack-review workflow).
|
||||
- Read-only: every finding waits for a manual fix decision
|
||||
- Objective Layer-2 metrics via `score_review_tool`
|
||||
- Fingerprint merge + quality score via `dedupe_findings_tool`
|
||||
- Standalone HTML report via `generate_report_tool`
|
||||
|
||||
### `/code-review-graph:project-review`
|
||||
Whole-project or single-feature code review (not diff-based).
|
||||
- Scope parsed from user instruction: 全面/整个项目 → whole-project; otherwise feature + target
|
||||
- Whole-project: `score_review_tool(all_files=True)` scores every source file
|
||||
- Feature: `semantic_search_nodes` + `query_graph(children_of)` locate the code
|
||||
- Read-only: every finding waits for a manual fix decision
|
||||
|
||||
## MCP Tools
|
||||
|
||||
### Core Tools
|
||||
|
||||
#### `build_or_update_graph_tool`
|
||||
```
|
||||
full_rebuild: bool = False # True for full re-parse
|
||||
repo_root: str | None # Auto-detected
|
||||
base: str | None = None # Diff base; None auto-resolves to the last-synced commit
|
||||
postprocess: str = "full" # "full", "minimal", or "none"
|
||||
recurse_submodules: bool | None # Falls back to CRG_RECURSE_SUBMODULES
|
||||
```
|
||||
|
||||
#### `run_postprocess_tool`
|
||||
```
|
||||
flows: bool = True
|
||||
communities: bool = True
|
||||
fts: bool = True
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
#### `get_minimal_context_tool`
|
||||
```
|
||||
task: str = "" # What you are doing
|
||||
changed_files: list[str] | None # Auto-detected from VCS when omitted
|
||||
repo_root: str | None
|
||||
base: str = "HEAD~1"
|
||||
```
|
||||
|
||||
#### `get_impact_radius_tool`
|
||||
```
|
||||
changed_files: list[str] | None # Auto-detected from VCS
|
||||
max_depth: int = 2 # Hops in graph
|
||||
repo_root: str | None
|
||||
base: str = "HEAD~1"
|
||||
detail_level: str = "standard" # "standard" or "minimal"
|
||||
```
|
||||
Relevant responses may include compact estimated `context_savings` metadata.
|
||||
|
||||
#### `query_graph_tool`
|
||||
```
|
||||
pattern: str # callers_of, references_to, callees_of, imports_of, importers_of,
|
||||
# children_of, tests_for, inheritors_of, file_summary
|
||||
target: str # Node name, qualified name, or file path
|
||||
repo_root: str | None
|
||||
detail_level: str = "standard" # "standard" or "minimal"
|
||||
```
|
||||
|
||||
#### `get_review_context_tool`
|
||||
```
|
||||
changed_files: list[str] | None
|
||||
max_depth: int = 2
|
||||
include_source: bool = True
|
||||
max_lines_per_file: int = 200
|
||||
repo_root: str | None
|
||||
base: str = "HEAD~1"
|
||||
detail_level: str = "standard" # "standard" or "minimal"
|
||||
```
|
||||
Relevant responses may include compact estimated `context_savings` metadata.
|
||||
|
||||
#### `traverse_graph_tool`
|
||||
```
|
||||
query: str
|
||||
depth: int = 3 # 1-6
|
||||
mode: str = "bfs" # "bfs" or "dfs"
|
||||
token_budget: int = 2000
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
#### `semantic_search_nodes_tool`
|
||||
```
|
||||
query: str # Search string
|
||||
kind: str | None # File, Class, Function, Type, Test
|
||||
limit: int = 20
|
||||
repo_root: str | None
|
||||
model: str | None # Embedding model (falls back to provider-specific env vars)
|
||||
provider: str | None # local, openai, google, minimax, voyage
|
||||
detail_level: str = "standard"
|
||||
```
|
||||
|
||||
#### `embed_graph_tool`
|
||||
```
|
||||
repo_root: str | None
|
||||
model: str | None # Embedding model name
|
||||
provider: str | None # local, openai, google, minimax, voyage
|
||||
```
|
||||
Local embeddings require: `pip install "code-review-graph[embeddings]"`. Cloud providers use stdlib HTTP clients and require their provider environment variables.
|
||||
|
||||
#### `list_graph_stats_tool`
|
||||
```
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
#### `find_large_functions_tool`
|
||||
```
|
||||
min_lines: int = 50 # Minimum line count threshold
|
||||
kind: str | None # File, Class, Function, or Test
|
||||
file_path_pattern: str | None # Filter by file path substring
|
||||
limit: int = 50 # Max results to return
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
#### `get_docs_section_tool`
|
||||
```
|
||||
section_name: str # usage, review-delta, review-pr, commands, legal, watch, embeddings, languages, troubleshooting
|
||||
```
|
||||
|
||||
### Flow Tools
|
||||
|
||||
#### `list_flows_tool`
|
||||
```
|
||||
sort_by: str = "criticality" # criticality, depth, node_count, file_count, name
|
||||
limit: int = 50
|
||||
kind: str | None # Filter by entry point kind (e.g. "Test", "Function")
|
||||
repo_root: str | None
|
||||
detail_level: str = "standard"
|
||||
```
|
||||
|
||||
#### `get_flow_tool`
|
||||
```
|
||||
flow_id: int | None # Database ID from list_flows_tool
|
||||
flow_name: str | None # Name to search (partial match)
|
||||
include_source: bool = False # Include source snippets for each step
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
#### `get_affected_flows_tool`
|
||||
```
|
||||
changed_files: list[str] | None # Auto-detected from VCS
|
||||
base: str = "HEAD~1"
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
### Community Tools
|
||||
|
||||
#### `list_communities_tool`
|
||||
```
|
||||
sort_by: str = "size" # size, cohesion, name
|
||||
min_size: int = 0
|
||||
repo_root: str | None
|
||||
detail_level: str = "standard"
|
||||
```
|
||||
|
||||
#### `get_community_tool`
|
||||
```
|
||||
community_name: str | None # Name to search (partial match)
|
||||
community_id: int | None # Database ID
|
||||
include_members: bool = False
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
#### `get_architecture_overview_tool`
|
||||
```
|
||||
repo_root: str | None
|
||||
detail_level: str = "minimal" # "minimal" compact default, "standard" full detail
|
||||
```
|
||||
Minimal responses may include compact estimated `context_savings` metadata.
|
||||
|
||||
### Graph Health and Architecture Tools
|
||||
|
||||
#### `get_hub_nodes_tool`
|
||||
```
|
||||
top_n: int = 10
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
#### `get_bridge_nodes_tool`
|
||||
```
|
||||
top_n: int = 10
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
#### `get_knowledge_gaps_tool`
|
||||
```
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
#### `get_surprising_connections_tool`
|
||||
```
|
||||
top_n: int = 15
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
#### `get_suggested_questions_tool`
|
||||
```
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
### Change Analysis and Refactoring Tools
|
||||
|
||||
#### `detect_changes_tool`
|
||||
```
|
||||
base: str = "HEAD~1"
|
||||
changed_files: list[str] | None
|
||||
include_source: bool = False
|
||||
max_depth: int = 2
|
||||
repo_root: str | None
|
||||
detail_level: str = "standard"
|
||||
```
|
||||
Primary tool for code review. Maps changed files to affected functions, flows, communities, and test coverage gaps. Returns risk scores and prioritized review items.
|
||||
Relevant responses may include compact estimated `context_savings` metadata.
|
||||
|
||||
### Unified Review Tools
|
||||
|
||||
#### `score_review_tool`
|
||||
```
|
||||
changed_files: list[str] | None # Auto-detected from git diff if omitted
|
||||
base: str = "HEAD~1"
|
||||
include_churn: bool = True
|
||||
repo_root: str | None
|
||||
detail_level: str = "standard" # "minimal" for grades + values only
|
||||
```
|
||||
Computes objective Layer-2 metrics for changed files: `sql_risk`,
|
||||
`exception_coverage`, `redundancy_rate`, `high_risk_density`,
|
||||
`vulnerability_risk`. Each metric carries a `good`/`warn`/`fail` grade,
|
||||
thresholds and evidence. LLM-judged metrics are listed in `llm_judged`.
|
||||
|
||||
#### `dedupe_findings_tool`
|
||||
```
|
||||
findings: list # [{path, category, severity, confidence, source?}]
|
||||
suppress_prior: list | None # Previously user-skipped findings to suppress
|
||||
repo_root: str | None
|
||||
```
|
||||
Merges findings by `path:line:category` fingerprint (highest confidence
|
||||
wins), boosts multi-source confidence (+1, cap 10), routes low-confidence
|
||||
findings to the appendix, and computes `PR quality score = max(0, 10 -
|
||||
(critical*2 + informational*0.5))`.
|
||||
|
||||
#### `generate_report_tool`
|
||||
```
|
||||
review_data: dict # metrics + findings + verdict + tier + scope
|
||||
output_path: str | None # Base name without extension
|
||||
repo_root: str | None # Default: <repo_root>/code-review-report
|
||||
format: str = "both" # "html" | "markdown" | "both"
|
||||
```
|
||||
Renders the standalone HTML review report (self-contained, no external
|
||||
dependencies) and/or the Chinese Markdown report from the bundled
|
||||
`report-template.html`. Default `format="both"` writes
|
||||
`code-review-report.html` + `code-review-report.md`.
|
||||
|
||||
#### `refactor_tool`
|
||||
```
|
||||
mode: str = "rename" # "rename", "dead_code", or "suggest"
|
||||
old_name: str | None # (rename) Current symbol name
|
||||
new_name: str | None # (rename) New name
|
||||
kind: str | None # (dead_code) Function or Class
|
||||
file_pattern: str | None # (dead_code) Filter by file path substring
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
#### `apply_refactor_tool`
|
||||
```
|
||||
refactor_id: str # ID from prior refactor_tool call
|
||||
repo_root: str | None
|
||||
dry_run: bool = False # Return diff without writing files
|
||||
```
|
||||
|
||||
### Wiki Tools
|
||||
|
||||
#### `generate_wiki_tool`
|
||||
```
|
||||
repo_root: str | None
|
||||
force: bool = False # Regenerate all pages even if unchanged
|
||||
```
|
||||
|
||||
#### `get_wiki_page_tool`
|
||||
```
|
||||
community_name: str # Community name to look up
|
||||
repo_root: str | None
|
||||
```
|
||||
|
||||
### Multi-Repo Tools
|
||||
|
||||
#### `list_repos_tool`
|
||||
```
|
||||
(no parameters)
|
||||
```
|
||||
|
||||
#### `cross_repo_search_tool`
|
||||
```
|
||||
query: str
|
||||
kind: str | None
|
||||
limit: int = 20
|
||||
```
|
||||
|
||||
## MCP Prompts (7 workflow templates)
|
||||
|
||||
### `review_changes`
|
||||
Pre-commit review workflow using detect_changes, affected_flows, and test gaps.
|
||||
```
|
||||
base: str = "HEAD~1"
|
||||
```
|
||||
|
||||
### `architecture_map`
|
||||
Architecture documentation using communities, flows, and Mermaid diagrams.
|
||||
|
||||
### `debug_issue`
|
||||
Guided debugging using search, flow tracing, and recent changes.
|
||||
```
|
||||
description: str = ""
|
||||
```
|
||||
|
||||
### `onboard_developer`
|
||||
New developer orientation using stats, architecture, and critical flows.
|
||||
|
||||
### `pre_merge_check`
|
||||
PR readiness check with risk scoring, test gaps, and dead code detection.
|
||||
```
|
||||
base: str = "HEAD~1"
|
||||
```
|
||||
|
||||
### `unified_review`
|
||||
Three-layer unified review: CRG graph context + objective scoring
|
||||
(`score_review`), finding merge (`dedupe_findings`), and the standalone
|
||||
HTML report (`generate_report`). READ-ONLY: every finding waits for a
|
||||
manual fix decision.
|
||||
```
|
||||
base: str = "HEAD~1"
|
||||
tier: str = "standard" # fast | standard | strict
|
||||
```
|
||||
|
||||
### `project_review`
|
||||
Whole-project or single-feature code review (not diff-based). Reviews
|
||||
the entire codebase or a single feature/module using graph-wide analysis
|
||||
and objective scoring. READ-ONLY: every finding waits for a manual fix
|
||||
decision. Scope is parsed from the user instruction (全面/整个项目 →
|
||||
whole-project; otherwise feature + target keyword).
|
||||
```
|
||||
scope: str = "whole-project" # whole-project | feature
|
||||
target: str = "" # feature/module keyword when scope="feature"
|
||||
```
|
||||
|
||||
## CLI Commands
|
||||
|
||||
```bash
|
||||
# Setup
|
||||
code-review-graph install # Configure detected AI coding platforms (alias: init)
|
||||
code-review-graph install --dry-run # Preview without writing files
|
||||
code-review-graph install --platform codex # Configure one platform
|
||||
code-review-graph uninstall # Remove all CRG configs, hooks, skills, and data
|
||||
code-review-graph uninstall --platform codex # Unbind one platform (keeps graph data + others)
|
||||
|
||||
# Build and update
|
||||
code-review-graph build # Full build
|
||||
code-review-graph build --skip-flows # Parse + signatures + FTS only
|
||||
code-review-graph build --skip-postprocess # Raw parse only
|
||||
code-review-graph update # Incremental update
|
||||
code-review-graph update --base origin/main # Custom base ref
|
||||
code-review-graph update --brief # Update graph + show risk panel
|
||||
code-review-graph update --brief --verify # ...and cross-check vs tiktoken
|
||||
code-review-graph postprocess # Re-run flows, communities, FTS
|
||||
code-review-graph 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 embed --provider local # Compute vector embeddings for semantic search
|
||||
code-review-graph update --embedding-provider local --embedding-model all-MiniLM-L6-v2
|
||||
# Explicitly refresh an existing index (default: off)
|
||||
|
||||
# Monitor and inspect
|
||||
code-review-graph status # Graph statistics
|
||||
code-review-graph watch # Auto-update on file changes
|
||||
code-review-graph visualize # Generate interactive HTML graph
|
||||
code-review-graph visualize --format graphml # Export GraphML
|
||||
code-review-graph visualize --serve # Serve graph.html on localhost:8765
|
||||
|
||||
# Analysis
|
||||
code-review-graph detect-changes # Risk-scored change analysis
|
||||
code-review-graph detect-changes --base HEAD~3 # Custom base ref
|
||||
code-review-graph detect-changes --brief # Compact panel with token-savings estimate
|
||||
code-review-graph detect-changes --brief --verify # ...and cross-check vs tiktoken
|
||||
code-review-graph detect-changes --churn # Add opt-in change-frequency risk
|
||||
|
||||
# detect-changes vs update --brief — which one?
|
||||
# • detect-changes --brief: read-only. Asks "what's the impact of my current
|
||||
# changes against the existing graph?" Fast (~1s). Use this when the graph
|
||||
# is already up to date (the default, if you have hooks installed).
|
||||
# • update --brief: re-parses your changed files into the graph FIRST, then
|
||||
# runs the same analysis at the end. Use this after a rebase, a big
|
||||
# change set, or whenever you suspect the graph is stale.
|
||||
# Both end with an identical "Token Savings" panel.
|
||||
|
||||
# Wiki
|
||||
code-review-graph wiki # Generate markdown wiki from communities
|
||||
|
||||
# Multi-repo
|
||||
code-review-graph register <path> [--alias name] # Register a repository
|
||||
code-review-graph unregister <path_or_alias> # Remove from registry
|
||||
code-review-graph repos # List registered repositories
|
||||
|
||||
# Daemon (multi-repo watcher) — included with install, no extra dependencies
|
||||
code-review-graph daemon start [--foreground] # Start the watch daemon
|
||||
code-review-graph daemon stop # Stop the daemon
|
||||
code-review-graph daemon restart [--foreground] # Restart the daemon
|
||||
code-review-graph daemon status # Show daemon status and repos
|
||||
code-review-graph daemon logs [--repo ALIAS] [--follow] # View daemon or per-repo logs
|
||||
code-review-graph daemon add <path> [--alias NAME] # Add a repo to daemon config
|
||||
code-review-graph daemon remove <path_or_alias> # Remove a repo from daemon config
|
||||
|
||||
# Evaluation
|
||||
code-review-graph eval # Run evaluation benchmarks
|
||||
|
||||
# Server
|
||||
code-review-graph serve # Start MCP server (stdio)
|
||||
code-review-graph serve --http # Streamable HTTP on localhost:5555
|
||||
code-review-graph serve --tools query_graph_tool,detect_changes_tool # Tool allowlist
|
||||
code-review-graph mcp # Alias for serve
|
||||
```
|
||||
|
||||
## Standalone Daemon CLI (`crg-daemon`)
|
||||
|
||||
The `crg-daemon` command is included with every `code-review-graph` installation — no
|
||||
separate install required. It is also available as a standalone entry point. It mirrors the
|
||||
`code-review-graph daemon` subcommands:
|
||||
|
||||
```bash
|
||||
crg-daemon start [--foreground] # Start the multi-repo watch daemon
|
||||
crg-daemon stop # Stop the daemon and all watcher processes
|
||||
crg-daemon restart [--foreground] # Restart (stop + start)
|
||||
crg-daemon status # Show daemon status, repos, and process liveness
|
||||
crg-daemon logs [--repo ALIAS] [-f] [-n N] # Tail daemon or per-repo log files
|
||||
crg-daemon add <path> [--alias NAME] # Add a repository to watch.toml
|
||||
crg-daemon remove <path_or_alias> # Remove a repository from watch.toml
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
The daemon reads its configuration from `~/.code-review-graph/watch.toml`:
|
||||
|
||||
```toml
|
||||
session_name = "crg-watch" # logical daemon name
|
||||
log_dir = "~/.code-review-graph/logs"
|
||||
poll_interval = 2 # seconds between config file polls
|
||||
|
||||
[[repos]]
|
||||
path = "/home/user/project-a"
|
||||
alias = "project-a"
|
||||
|
||||
[[repos]]
|
||||
path = "/home/user/project-b"
|
||||
alias = "project-b"
|
||||
```
|
||||
|
||||
The daemon spawns one `code-review-graph watch` child process per repo,
|
||||
managed via `subprocess.Popen`. It monitors the config file for changes and
|
||||
automatically reconciles child processes (starting/stopping as repos are
|
||||
added or removed). Health checks run every 30 seconds and automatically
|
||||
restart dead watchers. No external dependencies (tmux, screen, etc.) are
|
||||
required.
|
||||
@@ -0,0 +1,224 @@
|
||||
# Custom Languages (Bring Your Own Language)
|
||||
|
||||
code-review-graph ships parsers for 35+ languages, but the
|
||||
[tree-sitter-language-pack](https://github.com/Goldziher/tree-sitter-language-pack)
|
||||
it depends on bundles many more grammars than the built-in list. If your repo
|
||||
uses a language the graph does not cover yet — Erlang, Haskell, OCaml,
|
||||
Fortran, Ada, Clojure, ... — you can teach the parser about it with a small
|
||||
config file. No fork, no code changes.
|
||||
|
||||
## Quick start
|
||||
|
||||
Create `<repo_root>/.code-review-graph/languages.toml`:
|
||||
|
||||
```toml
|
||||
[languages.erlang]
|
||||
extensions = [".erl"]
|
||||
grammar = "erlang"
|
||||
function_node_types = ["function_clause"]
|
||||
class_node_types = ["record_decl"]
|
||||
import_node_types = ["import_attribute"]
|
||||
call_node_types = ["call"]
|
||||
comment = "Erlang via the bundled tree-sitter-erlang grammar"
|
||||
```
|
||||
|
||||
Then rebuild:
|
||||
|
||||
```bash
|
||||
uv run code-review-graph build
|
||||
```
|
||||
|
||||
Files matching the configured extensions are now parsed with the named
|
||||
grammar, and the resulting Function/Class nodes and CALLS/IMPORTS_FROM edges
|
||||
flow through every downstream feature (impact radius, search, communities,
|
||||
wiki, MCP tools) exactly like built-in languages. Nodes carry the custom
|
||||
language name (here `erlang`) in their `language` field.
|
||||
|
||||
## Schema reference
|
||||
|
||||
Each custom language is one `[languages.<name>]` table.
|
||||
|
||||
| Key | Type | Required | Meaning |
|
||||
|-----|------|----------|---------|
|
||||
| `<name>` | table key | yes | Language identifier stored on every parsed node. Lowercase letters, digits, `_`, `-`; max 32 chars; must start with a letter. |
|
||||
| `extensions` | list of strings | yes | File extensions to claim, each starting with a dot (e.g. `".erl"`). Matched case-insensitively. |
|
||||
| `grammar` | string | yes | A grammar name shipped by `tree_sitter_language_pack` (probe availability — see below). |
|
||||
| `function_node_types` | list of strings | no* | Tree-sitter node types that define functions/methods. Matching nodes become `Function` nodes (or `Test` nodes when the name/file looks like a test). |
|
||||
| `class_node_types` | list of strings | no* | Node types that define classes/records/types. Matching nodes become `Class` nodes. |
|
||||
| `import_node_types` | list of strings | no* | Node types for import/include statements. Each yields an `IMPORTS_FROM` edge. |
|
||||
| `call_node_types` | list of strings | no* | Node types for call expressions. Each yields a `CALLS` edge from the enclosing function. |
|
||||
| `name_field` | string or list of strings | no | Ordered candidates for locating a definition's name when it is not a `name` field or a plain `identifier` child (see below). |
|
||||
| `comment` | string | no | Free-form note for humans; ignored by the parser. |
|
||||
|
||||
\* At least one of the four node-type lists must be non-empty, otherwise the
|
||||
entry is skipped (there would be nothing to extract).
|
||||
|
||||
### Validation rules (safety first)
|
||||
|
||||
The loader never crashes a build. Anything invalid is skipped with a
|
||||
`WARNING` log line:
|
||||
|
||||
- **Built-ins always win.** A custom language cannot claim a built-in
|
||||
extension (`.py`, `.ts`, `.ex`, ...) and cannot reuse a built-in language
|
||||
name (`python`, `elixir`, ...).
|
||||
- `grammar` must load from `tree_sitter_language_pack`; unknown grammars are
|
||||
skipped.
|
||||
- Every extension must start with a dot.
|
||||
- Two custom languages cannot claim the same extension (first one wins).
|
||||
- At most **20** custom languages are loaded per repo.
|
||||
- Malformed TOML disables custom languages for that build (with a warning).
|
||||
- `name_field` must be a string or a list of non-empty strings (max 8
|
||||
candidates); anything else skips the entry with a warning.
|
||||
|
||||
### Naming definitions with `name_field`
|
||||
|
||||
By default the parser finds a definition's name from an `identifier`-like child
|
||||
or a field literally called `name`. Many grammars keep the name elsewhere — in
|
||||
a differently named field, or nested a level or two below it. When that happens
|
||||
the definition is extracted **unnamed and silently dropped**. `name_field` tells
|
||||
the parser where to look.
|
||||
|
||||
Each candidate is tried in order and resolved in two passes:
|
||||
|
||||
1. **Field first** — a child accessed by that tree-sitter field name. Fields are
|
||||
tried across *all* candidates before any type search, so a precise field
|
||||
always wins over a broader match.
|
||||
2. **Typed descendant** — if no candidate matched a field, the first descendant
|
||||
whose *node type* equals a candidate (bounded depth). This covers names that
|
||||
sit under a fieldless wrapper.
|
||||
|
||||
The resolved node is then descended to its first text-bearing leaf and cleaned
|
||||
(surrounding `{}`/quotes/whitespace stripped; multi-line or oversized text is
|
||||
rejected). Because resolution is anchored on your configured candidates, it
|
||||
never grabs an unrelated inner identifier.
|
||||
|
||||
```toml
|
||||
[languages.bibtex]
|
||||
extensions = [".bib"]
|
||||
grammar = "bibtex"
|
||||
class_node_types = ["entry"]
|
||||
name_field = ["key"] # @article{smith2020,...} -> "smith2020"
|
||||
|
||||
[languages.latex]
|
||||
extensions = [".tex"]
|
||||
grammar = "latex"
|
||||
class_node_types = ["section", "chapter", "subsection"]
|
||||
function_node_types = ["new_command_definition"]
|
||||
name_field = ["name", "text", "declaration"]
|
||||
# \section{Introduction} -> "Introduction" (via `text`)
|
||||
# \newcommand{\foo}{bar} -> "\foo" (via `declaration`)
|
||||
|
||||
[languages.markdown]
|
||||
extensions = [".md"]
|
||||
grammar = "markdown"
|
||||
class_node_types = ["section"]
|
||||
name_field = ["inline"] # "# My Heading" -> "My Heading" (typed descendant)
|
||||
```
|
||||
|
||||
Use a **list** when a grammar's node types keep their names in different places
|
||||
(LaTeX `section` uses `text`, `\newcommand` uses `declaration`): the first
|
||||
candidate that resolves wins. Omitting `name_field` preserves the previous
|
||||
behavior exactly.
|
||||
|
||||
## Finding the right node type names
|
||||
|
||||
Node type names are grammar-specific, so you need to look at the tree the
|
||||
grammar actually produces. Two easy options:
|
||||
|
||||
**Option 1 — tree-sitter playground.** Paste a snippet into
|
||||
<https://tree-sitter.github.io/tree-sitter/7-playground.html> and read the
|
||||
node names off the parse tree (select the matching grammar first).
|
||||
|
||||
**Option 2 — probe locally with Python.** The exact grammar version your
|
||||
build uses is the one in `tree_sitter_language_pack`, so probing locally is
|
||||
the most reliable source of truth:
|
||||
|
||||
```bash
|
||||
uv run python - <<'EOF'
|
||||
import tree_sitter_language_pack as tslp
|
||||
|
||||
source = b"""
|
||||
-module(math_utils).
|
||||
add(A, B) -> helper(A) + B.
|
||||
helper(X) -> X * 2.
|
||||
"""
|
||||
|
||||
def dump(node, depth=0):
|
||||
print(" " * depth + node.type, node.text.decode()[:40].replace("\n", " "))
|
||||
for child in node.children:
|
||||
dump(child, depth + 1)
|
||||
|
||||
dump(tslp.get_parser("erlang").parse(source).root_node)
|
||||
EOF
|
||||
```
|
||||
|
||||
Pick the node types that wrap whole definitions (`function_clause`, not the
|
||||
inner `atom`) and whole call expressions (`call`, not the callee identifier).
|
||||
|
||||
## Worked example: Erlang end to end
|
||||
|
||||
`src/math_utils.erl`:
|
||||
|
||||
```erlang
|
||||
-module(math_utils).
|
||||
-export([add/2, scale/2]).
|
||||
-import(lists, [map/2]).
|
||||
|
||||
-record(point, {x, y}).
|
||||
|
||||
add(A, B) ->
|
||||
helper(A) + B.
|
||||
|
||||
helper(X) -> X * 2.
|
||||
|
||||
scale(Points, F) ->
|
||||
lists:map(fun(P) -> add(P, F) end, Points).
|
||||
```
|
||||
|
||||
With the `[languages.erlang]` config from the quick start, a build produces:
|
||||
|
||||
- `Function` nodes `add`, `helper`, `scale` (from `function_clause`),
|
||||
each with `language = "erlang"`.
|
||||
- A `Class` node `point` (from `record_decl`).
|
||||
- `CALLS` edges `add → helper` and `scale → add`, resolved to their
|
||||
same-file qualified names, plus `scale → lists:map` for the remote call.
|
||||
- An `IMPORTS_FROM` edge targeting `lists` (from `import_attribute`).
|
||||
- `CONTAINS` edges from the file to every definition.
|
||||
|
||||
## How extraction works (and its limits)
|
||||
|
||||
Custom languages run through the same generic tree-sitter walker as built-in
|
||||
languages — there is no per-language code path to maintain. That keeps the
|
||||
feature simple, but the generic heuristics have limits:
|
||||
|
||||
- **Name extraction uses the default name-field heuristics.** The walker
|
||||
looks for a child node of a common identifier type (`identifier`, `name`,
|
||||
`type_identifier`, ...) and falls back to the grammar's `name` field
|
||||
(`node.child_by_field_name("name")`). Grammars that store definition names
|
||||
in another shape (e.g. nested two levels deep with a non-standard field)
|
||||
will produce unnamed — and therefore skipped — definitions.
|
||||
- **Callee extraction probes common field names** (`function`, `callee`,
|
||||
`expr`, `name`) and descends through curried applications. Exotic call
|
||||
shapes may be missed.
|
||||
- **Import targets** come from the grammar's `module`/`name`/`path`/`source`
|
||||
field when present, otherwise the raw statement text is recorded.
|
||||
- **No cross-file module resolution.** Import edges keep the module name as
|
||||
written (e.g. `lists`); they are not resolved to file paths the way
|
||||
built-in languages with dedicated resolvers are.
|
||||
- **No language-specific extras**: things like decorator-based test
|
||||
detection, framework annotations (Spring, Temporal), or SFC handling only
|
||||
exist for built-in languages.
|
||||
|
||||
If a language needs deeper support than the generic walker can give, please
|
||||
open an issue — config-driven support is the on-ramp, not the ceiling.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Run a build with `-v`/logging enabled and look for `languages.toml`
|
||||
warnings — every skipped entry says exactly why it was skipped.
|
||||
- Probe grammar availability:
|
||||
`uv run python -c "import tree_sitter_language_pack as t; t.get_language('erlang')"`
|
||||
(raises `LookupError` if the grammar is not bundled).
|
||||
- The config is read when a parser is constructed (every `build`/`update`),
|
||||
so config changes take effect on the next build — re-run
|
||||
`uv run code-review-graph build` after editing.
|
||||
@@ -0,0 +1,252 @@
|
||||
# FAQ — how code-review-graph compares
|
||||
|
||||
Honest answers to the questions we get most often. Where another tool is genuinely
|
||||
better for a job, this page says so.
|
||||
|
||||
- [How is this different from LSP and language servers?](#how-is-this-different-from-lsp-and-language-servers)
|
||||
- [Isn't this just RAG?](#isnt-this-just-rag)
|
||||
- [Why not just grep?](#why-not-just-grep)
|
||||
- [How does it compare to Serena, codegraph, claude-context, and repomix?](#how-does-it-compare-to-serena-codegraph-claude-context-and-repomix)
|
||||
- [When should I not use it?](#when-should-i-not-use-it)
|
||||
- [Does it phone home?](#does-it-phone-home)
|
||||
- [How do I verify it is working?](#how-do-i-verify-it-is-working)
|
||||
- [How big a codebase justifies it?](#how-big-a-codebase-justifies-it)
|
||||
- [How does it handle monorepos, git worktrees, and multiple repos?](#how-does-it-handle-monorepos-git-worktrees-and-multiple-repos)
|
||||
|
||||
---
|
||||
|
||||
## How is this different from LSP and language servers?
|
||||
|
||||
Language servers and code-review-graph (CRG) both build a structural model of your
|
||||
code, but they optimize for different things.
|
||||
|
||||
**What LSP does better.** A language server is backed by a real compiler frontend (or
|
||||
something close to it), so it gives you type-aware, semantically precise results:
|
||||
exact go-to-definition through generics and overloads, find-references that
|
||||
understands scoping, live diagnostics, completions, and renames that are safe by
|
||||
construction. If you need a *provably complete* reference list for one symbol in one
|
||||
language, an LSP server is the gold standard and CRG does not try to replace it.
|
||||
|
||||
**What CRG does differently:**
|
||||
|
||||
- **One persistent graph instead of per-language daemons.** Language servers run one
|
||||
process per language and (with a few exceptions that cache an index on disk) rebuild
|
||||
or revalidate state per session. CRG parses once with Tree-sitter, stores nodes and
|
||||
edges in a single SQLite file (`.code-review-graph/graph.db`), and answers queries
|
||||
across roughly 35 languages plus notebooks from one process — including cross-language
|
||||
edges that no single LSP server models.
|
||||
- **It survives sessions and commits.** The graph is updated incrementally (changed
|
||||
files only, ~2.5 seconds on a ~3,000-file repo) rather than rebuilt per editor
|
||||
session.
|
||||
- **Review-oriented edges.** `tests_for`, execution flows, community membership,
|
||||
risk-scored change analysis — relationships LSP does not model because they are not
|
||||
needed for editing.
|
||||
|
||||
**The honest trade-off:** CRG's call resolution is AST-level and heuristic, not
|
||||
compiler-backed. Dynamic dispatch, metaprogramming, and duck typing can produce
|
||||
inferred or ambiguous edges — which is exactly why every edge carries a confidence
|
||||
tier (`EXTRACTED` / `INFERRED` / `AMBIGUOUS`). LSP is more precise per symbol; CRG is
|
||||
broader, persistent, and cheaper to query across the whole repo.
|
||||
|
||||
## Isn't this just RAG?
|
||||
|
||||
No. RAG splits your code into text chunks, embeds them, and retrieves chunks by
|
||||
similarity to the query. That answers "find code that *talks about* X." It cannot
|
||||
answer "who *calls* X" — similarity between two functions tells you nothing about
|
||||
whether one invokes the other.
|
||||
|
||||
CRG stores **structural edges parsed from the AST**: calls, imports, inheritance,
|
||||
test coverage. "Who calls `login()`" is a graph lookup, not a similarity guess.
|
||||
Embeddings exist in CRG but they are optional and play a supporting role — one input
|
||||
to hybrid search (FTS5 BM25 keyword + vector) used to find a *starting node*, after
|
||||
which traversal follows real edges. Currently only function signatures are embedded
|
||||
(~10 tokens per node), not bodies.
|
||||
|
||||
The benchmark that captures the difference is multi-hop retrieval: natural-language
|
||||
query → anchor node → one-hop traversal (`callers_of`, `tests_for`, ...). CRG scores
|
||||
0.909 across 11 hand-curated tasks on 6 real repos (see
|
||||
[REPRODUCING.md](REPRODUCING.md)). Pure similarity retrieval has no equivalent of the
|
||||
second hop.
|
||||
|
||||
**Where RAG-style search is better:** purely conceptual questions ("where is rate
|
||||
limiting discussed?") over prose, comments, and docs. CRG's own keyword search
|
||||
ranking is a documented weakness (MRR 0.35 — see the limitations section in the
|
||||
[README](../README.md#benchmarks)).
|
||||
|
||||
## Why not just grep?
|
||||
|
||||
Fair question — Anthropic has been explicit that Claude Code deliberately ships
|
||||
*without* a code index. Agentic search (glob, grep, targeted file reads) is always
|
||||
exactly as fresh as your working tree, has no chunking or staleness failure modes,
|
||||
and needs zero setup. For one-hop questions — "where is `parse_file` defined?" —
|
||||
that approach works well, and CRG will not beat it by much.
|
||||
|
||||
The gap appears on **multi-hop structural questions**, where each hop costs the agent
|
||||
another round of grep + read + reasoning, and token spend compounds:
|
||||
|
||||
- **Impact radius** — "what could break if I change this file?" requires callers,
|
||||
dependents, *and* their tests. One `get_impact_radius` call returns all three.
|
||||
- **Callers of callers** — transitive tracing via `traverse_graph` or repeated
|
||||
`query_graph(pattern="callers_of")`, instead of N rounds of grepping for each
|
||||
intermediate name (and grep matches *text*, so overloaded or re-exported names
|
||||
produce false hits the agent must read to rule out).
|
||||
- **Tests for** — `query_graph(pattern="tests_for")` maps code to covering tests via
|
||||
parsed edges plus naming conventions, and `detect_changes` adds transitive test
|
||||
coverage. Grep only finds tests that mention the name literally.
|
||||
- **Affected flows** — "which execution paths does this change touch?" has no grep
|
||||
equivalent at all.
|
||||
|
||||
The graph also persists: agentic search re-derives the same structure from scratch
|
||||
every session, while CRG keeps it in SQLite and updates incrementally.
|
||||
|
||||
One honest caveat on the numbers: the whole-corpus token-reduction numbers (~65x median,
|
||||
36x–376x range) compare graph responses against reading the **whole corpus**, not
|
||||
against a skilled agentic-grep session (see [REPRODUCING.md](REPRODUCING.md) for what
|
||||
each benchmark measures). For single-hop lookups in a small repo, grep is cheap and
|
||||
good. The multi-hop review workflow is where the graph earns its keep.
|
||||
|
||||
## How does it compare to Serena, codegraph, claude-context, and repomix?
|
||||
|
||||
These are good tools solving adjacent problems. Short factual comparison, based on
|
||||
each project's public documentation (check upstream docs for current behavior):
|
||||
|
||||
| Tool | Approach | Persistence | External deps | Review focus |
|
||||
|---|---|---|---|---|
|
||||
| **code-review-graph** | Tree-sitter AST → structural graph (calls, imports, inheritance, tests) over MCP + CLI | SQLite in `.code-review-graph/`, incremental updates | None for the core; embeddings optional | Yes — blast radius, risk-scored change analysis, test-gap detection |
|
||||
| **Serena** | LSP-backed symbol retrieval and editing tools over MCP | Language-server state plus per-project memories | A language server per language | General coding-agent toolkit, not review-specific |
|
||||
| **codegraph** | AST/call-graph indexing over MCP (several projects share this name; details vary by implementation) | Varies by implementation | Varies by implementation | Generally retrieval-focused |
|
||||
| **claude-context** | Chunk + embed semantic code search over MCP | Vector index in a vector database | Embedding provider + vector DB (cloud or self-hosted) | Search-focused, not review-specific |
|
||||
| **repomix** | Packs the whole repo into one AI-friendly file | None — regenerated per run | Node.js | One-shot context packing; no structural queries |
|
||||
|
||||
Rough guidance: if you want symbol-precise *editing* tools, Serena's LSP approach is
|
||||
a better fit. If you want semantic *search* and are happy running a vector store,
|
||||
claude-context covers that. If your repo is small enough to paste wholesale into a
|
||||
large context window, repomix is the simplest thing that works. CRG's niche is the
|
||||
persistent structural graph for **review**: impact analysis, risk scoring, and
|
||||
test-coverage tracing with no external services.
|
||||
|
||||
## When should I not use it?
|
||||
|
||||
Consistent with the limitations section in the [README](../README.md#benchmarks):
|
||||
|
||||
- **Repos under a few hundred files.** An agent can often just read everything
|
||||
relevant directly; the graph's structural metadata adds overhead that a small repo
|
||||
doesn't repay. See [How big a codebase justifies it?](#how-big-a-codebase-justifies-it)
|
||||
- **Trivial single-file changes.** The graph response carries impact-radius edges and
|
||||
source snippets, which can exceed the raw content of a one-file diff. This is
|
||||
measured and documented (the formal `token_efficiency` benchmark reports ratios
|
||||
below 1.0 for small commits — by design, see [REPRODUCING.md](REPRODUCING.md)).
|
||||
- **One-off questions on a repo you won't revisit.** The build is fast (~10 seconds
|
||||
for a 500-file project) but the payoff comes from *reuse* across queries and
|
||||
sessions. For a single question, agentic search is fine.
|
||||
- **Flow detection on JS/Go.** Entry-point detection is currently reliable mainly for
|
||||
Python framework patterns; JavaScript and Go flow detection needs work (33% recall,
|
||||
documented in the README limitations).
|
||||
|
||||
## Does it phone home?
|
||||
|
||||
No. There is zero telemetry. The graph is a SQLite file in `.code-review-graph/`
|
||||
inside your repo, and the core build / review / search / MCP workflows run entirely
|
||||
locally. The streamable-HTTP MCP transport binds to localhost by default.
|
||||
|
||||
The only network activity is opt-in:
|
||||
|
||||
- **Local embeddings** (`pip install "code-review-graph[embeddings]"`) download the
|
||||
sentence-transformers model from HuggingFace on first use. Your code does not leave
|
||||
the machine.
|
||||
- **Cloud embeddings** (OpenAI-compatible, Google Gemini, MiniMax) send the text being
|
||||
embedded — currently function signatures — to the provider you explicitly configure
|
||||
via environment variables. CRG prints an egress warning unless you acknowledge it
|
||||
with `CRG_ACCEPT_CLOUD_EMBEDDINGS=1`; the warning is skipped automatically when the
|
||||
endpoint is localhost.
|
||||
|
||||
See [LEGAL.md](LEGAL.md) for the full privacy notes.
|
||||
|
||||
## How do I verify it is working?
|
||||
|
||||
1. **Check the graph exists and has content:**
|
||||
|
||||
```bash
|
||||
code-review-graph status
|
||||
```
|
||||
|
||||
You should see node/edge counts and graph statistics. Zero nodes means the build
|
||||
didn't run or found nothing to parse.
|
||||
|
||||
2. **See the savings on a real change** — make any edit, then:
|
||||
|
||||
```bash
|
||||
code-review-graph detect-changes --brief
|
||||
```
|
||||
|
||||
This prints the risk summary and the boxed **Token Savings** panel against the
|
||||
existing graph (read-only). Add `--verify` to cross-check the estimate against
|
||||
OpenAI's `cl100k_base` tokenizer (requires `pip install tiktoken`). If you suspect
|
||||
the graph is stale, `code-review-graph update --brief` re-parses changed files
|
||||
first and prints the same panel.
|
||||
|
||||
3. **Check the MCP wiring** — in Claude Code, run `/mcp` and confirm the
|
||||
`code-review-graph` server is connected with its tools listed. Then ask the
|
||||
assistant something structural ("what calls `parse_file`?") and watch it use
|
||||
`query_graph` instead of grepping.
|
||||
|
||||
If any of these fail, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
|
||||
|
||||
## How big a codebase justifies it?
|
||||
|
||||
This comes up often (see #414). Honest guidance, tied to the documented small-repo
|
||||
overhead:
|
||||
|
||||
- **Below a few hundred files:** marginal. The graph builds in seconds and works
|
||||
fine, but an agent can already hold most of the repo in context, and for trivial
|
||||
diffs the structural response can cost more tokens than it saves (the documented
|
||||
overhead regime — see [When should I not use it?](#when-should-i-not-use-it)).
|
||||
- **A few hundred to a few thousand files:** this is where the benchmarks live. The
|
||||
six evaluation repos range from 70 to ~1,100 files and show 36x–376x reductions on
|
||||
whole-corpus agent questions, with the caveat noted above about what that baseline
|
||||
measures.
|
||||
- **Multi-thousand-file repos and monorepos:** the strongest case. No agent can read
|
||||
the corpus per question (FastAPI alone is ~950k tokens of source), re-deriving
|
||||
structure by search every session is the dominant cost, and incremental updates
|
||||
keep the graph fresh in a couple of seconds.
|
||||
|
||||
A second axis matters as much as file count: **how often you ask multi-file
|
||||
questions**. A 300-file repo you review daily benefits more than a 3,000-file repo
|
||||
you touch once.
|
||||
|
||||
## How does it handle monorepos, git worktrees, and multiple repos?
|
||||
|
||||
**Monorepos.** One graph per repository root by default — commands auto-detect the
|
||||
root by walking up to the nearest `.git`, and in git repos only tracked files are
|
||||
indexed (`git ls-files`), so gitignored build artifacts are skipped automatically.
|
||||
Use a `.code-review-graphignore` file to exclude tracked paths (e.g. `vendor/**`,
|
||||
generated code), or pass `--repo <path>` to point a command at a specific directory.
|
||||
|
||||
**Git worktrees.** Each worktree is detected as its own root, so each gets its own
|
||||
`.code-review-graph/` database matching its checkout. Don't try to share one database
|
||||
across worktrees at different commits — the graph reflects one working tree. If you
|
||||
want the database outside the working tree entirely (ephemeral workspaces, network
|
||||
shares), use `--data-dir <path>` on `build`/`update`/etc., or set the `CRG_DATA_DIR`
|
||||
environment variable.
|
||||
|
||||
**Multiple repos.** A lightweight registry (stored at
|
||||
`~/.code-review-graph/registry.json`) lets MCP clients search across projects:
|
||||
|
||||
```bash
|
||||
code-review-graph register ~/work/api --alias api # add a repo (optional alias)
|
||||
code-review-graph repos # list registered repos
|
||||
code-review-graph unregister api # remove by path or alias
|
||||
```
|
||||
|
||||
Once registered, the `list_repos_tool` and `cross_repo_search_tool` MCP tools work
|
||||
across all of them. To keep several graphs fresh automatically, the bundled daemon watches
|
||||
registered repos as child processes:
|
||||
|
||||
```bash
|
||||
crg-daemon add ~/work/api --alias api
|
||||
crg-daemon start
|
||||
crg-daemon status
|
||||
```
|
||||
|
||||
(Also available as `code-review-graph daemon start|stop|status`.) See
|
||||
[COMMANDS.md](COMMANDS.md) for the full daemon reference.
|
||||
@@ -0,0 +1,156 @@
|
||||
# Features
|
||||
|
||||
## v2.3.6 (Current)
|
||||
- **Framework-aware PHP parsing**: traits, enums, object creation, and base clauses are indexed; Composer PSR-4 resolution is longest-prefix, multi-directory, cached, and repository-bounded; Blade references ignore comments/escaped directives; Laravel Route and Eloquent edges require explicit framework/import/receiver evidence.
|
||||
- **Custom languages without forking**: drop a `.code-review-graph/languages.toml` into your repo to index any grammar shipped by tree-sitter-language-pack — extension map plus node-type lists, validated and capped, with built-in languages always winning. See [CUSTOM_LANGUAGES.md](CUSTOM_LANGUAGES.md).
|
||||
- **GitHub Action for risk-scored PR reviews**: composite `action.yml` builds/restores the graph from CI cache, runs `detect-changes` against the PR base, and upserts a sticky comment with risk table, affected flows, test gaps, and the Token Savings line. Optional `fail-on-risk` merge gate. Dogfooded on this repo via `.github/workflows/pr-review.yml`. See [GITHUB_ACTION.md](GITHUB_ACTION.md).
|
||||
- **`agent_baseline` eval benchmark**: compares graph queries against a realistic grep-and-read-top-k agent baseline instead of the whole-corpus strawman; wired into all six pinned eval configs.
|
||||
- **Co-change ground truth for `impact_accuracy`**: predictions are also graded against files actually co-changed in the same commit; the legacy metric is explicitly labelled "graph-derived (circular — upper bound)".
|
||||
- **Weekly eval CI**: `.github/workflows/eval.yml` runs a report-only cron of the two smallest pinned configs with CSV artifacts and a job summary.
|
||||
- **docs/FAQ.md**: how CRG compares to LSP, RAG, grep/agentic search, and adjacent tools; when NOT to use it; verification steps; monorepo/worktree and registry guidance.
|
||||
- **Contribution scaffolding**: GitHub issue forms (bug/feature/platform), a PR template mirroring the CONTRIBUTING checklist, and dependabot config for pip + GitHub Actions.
|
||||
- **Windows fixes**: `daemon status` no longer crashes with WinError 87 (#511), and CLI `detect-changes` maps diff paths to absolute native paths so it no longer reports 0 functions (#528).
|
||||
- **Provider-name validation**: unknown embedding provider names raise a clear error listing valid providers instead of silently falling back to the local model.
|
||||
- **Store-leak fixes**: the five analysis MCP tools and the wiki-page tool no longer leak SQLite connections (try/finally `store.close()`).
|
||||
- **`fastmcp<4` cap**: the next fastmcp major can no longer silently break the server.
|
||||
- **Worktree-safe git hooks**: `install` resolves the real hooks directory via `git rev-parse --git-path hooks`, so linked worktrees and `core.hooksPath` (husky) setups get a working pre-commit hook.
|
||||
|
||||
## v2.3.5
|
||||
- **Token Savings panel on every brief CLI call**: `code-review-graph detect-changes --brief` and the new `code-review-graph update --brief` print a boxed `Token Savings` panel — full-context baseline, graph response, saved tokens, percent, and per-category breakdown (Functions / Tests / Risk / Other) that sums exactly to the graph response size.
|
||||
- **`--verify` flag**: cross-checks the displayed numbers against OpenAI's `cl100k_base` tokenizer (the GPT-4 family). Adds a second `Verified (tiktoken)` row showing real token counts. Calibration across 222 mixed-language files shows the estimate is within ~1% of real tokens in aggregate.
|
||||
- **`update --brief`**: incremental update + the same risk panel in one command. Distinct from `detect-changes --brief` (which is read-only against the existing graph) — use update when the graph might be stale (post-rebase, large change set).
|
||||
- **`code-review-graph embed` CLI subcommand**: explicit shell-level access to embedding generation. Previously only reachable via MCP.
|
||||
- **Deterministic eval pipeline**: all 6 eval configs pin upstream SHAs, `eval/runner.py` uses full clones with explicit `returncode` checks, and Leiden community detection uses a fixed seed (`CRG_LEIDEN_SEED=42`). Two runs on different machines produce identical numbers.
|
||||
- **`multi_hop_retrieval` benchmark**: 11 hand-curated 2-step tool-chain tasks (`hybrid_search` → `query_graph`) across the 6 test repos. Average score 0.909.
|
||||
- **Richer semantic search**: `embeddings._node_to_text` now includes the dotted form (`Module.Class.method`), word-split identifiers, and enclosing module directory. Search ranking on natural-language queries improved from 0.545 → 0.909 on the multi-hop benchmark.
|
||||
- **Identifier-aware search boost**: `extract_query_identifiers` pulls dotted / snake_case / CamelCase tokens out of NL queries and boosts matching qualified-names ×2.0 in hybrid search.
|
||||
- **Path normalization fix**: `eval/runner.py` now resolves repo paths absolutely before storing, so the eval-built graph matches the CLI/MCP-built graph and `update` doesn't create duplicate nodes for the same source location.
|
||||
- **Test-gap dedup**: the `Untested:` line in the brief summary dedupes by bare name (defensive guard if duplicate qualified_names slip in).
|
||||
- **FTS5 auto-rebuild in eval**: the eval framework now calls `run_post_processing` after `full_build`, so FTS5 is populated automatically instead of leaving the index empty.
|
||||
|
||||
## v2.3.4
|
||||
- **Estimated context savings**: Review, impact, detect-changes, and compact architecture responses include tiny `context_savings` metadata (`estimated`, `saved_tokens`, `saved_percent`) where a baseline can be estimated.
|
||||
- **Compact architecture overview by default**: `get_architecture_overview_tool` defaults to `detail_level="minimal"` to avoid huge member lists and per-edge payloads. Use `detail_level="standard"` for full detail.
|
||||
- **Bounded change analysis**: `CRG_MAX_CHANGED_FUNCS`, `CRG_MAX_TRANSITIVE_FRONTIER`, and `CRG_TOOL_TIMEOUT` help keep large MCP review calls responsive.
|
||||
- **Windows MCP reliability**: Local embedding models are pre-warmed on Windows before FastMCP starts worker dispatch to avoid semantic-search deadlocks.
|
||||
- **Parser correctness**: Rust `#[test]` and common async test attributes now produce `Test` nodes.
|
||||
- **Graph lookup correctness**: Review, impact, and file-summary tools resolve user-facing paths to stored graph paths; `callers_of` includes cross-file callers even when same-file callers exist.
|
||||
- **Install/runtime reliability**: Generated Codex/Claude hooks drain stdin, bundled docs are available from wheels, missing local embeddings report unavailable status, and `.svn` roots pass validation.
|
||||
- **CLI reliability**: `build --skip-postprocess` and `update --skip-flows` honor the requested post-processing level.
|
||||
- **Broad parser surface**: Python, JavaScript/TypeScript/TSX, Go, Rust, Java, C/C++, C#, VB.NET, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, Terraform/OpenTofu structure (`.tf`; generic `.hcl` files are recognized as file nodes), Ansible playbooks/roles/tasks, Vue/Svelte SFCs, Astro files parsed through the TypeScript parser, Jupyter/Databricks notebooks, and Perl XS files. Generic YAML is not treated as source code.
|
||||
- **Local-first by design**: SQLite graph storage remains local, with no telemetry and no cloud-default behavior.
|
||||
|
||||
## v2.0.0
|
||||
- **22 MCP tools** (up from 9): 13 new tools for flows, communities, architecture, refactoring, wiki, multi-repo, and risk-scored change detection.
|
||||
- **5 MCP prompts**: `review_changes`, `architecture_map`, `debug_issue`, `onboard_developer`, `pre_merge_check` workflow templates.
|
||||
- **18 languages** (up from 15): Added Dart, R, Perl support.
|
||||
- **Execution flows**: Trace call chains from entry points (HTTP handlers, CLI commands, tests), sorted by criticality score.
|
||||
- **Community detection**: Cluster related code entities via Leiden algorithm (igraph) or file-based grouping.
|
||||
- **Architecture overview**: Auto-generated architecture map with module summaries and cross-community coupling warnings.
|
||||
- **Risk-scored change detection**: `detect_changes` maps git diffs to affected functions, flows, communities, and test coverage gaps with priority ordering.
|
||||
- **Refactoring tools**: Rename preview with edit list, dead code detection, community-driven refactoring suggestions.
|
||||
- **Wiki generation**: Auto-generate markdown wiki pages for each community with optional LLM summaries (ollama).
|
||||
- **Multi-repo registry**: Register multiple repositories, search across all of them with `cross_repo_search`.
|
||||
- **Full-text search**: FTS5 virtual table with porter stemming for hybrid keyword + vector search.
|
||||
- **Database migrations**: Versioned schema migrations (v1-v5) with automatic upgrade on startup.
|
||||
- **Optional dependency groups**: `[embeddings]`, `[google-embeddings]`, `[communities]`, `[eval]`, `[wiki]`, `[all]`.
|
||||
- **Evaluation framework**: Benchmark suite with matplotlib visualization.
|
||||
- **TypeScript path resolution**: tsconfig.json paths/baseUrl alias resolution for imports.
|
||||
- **486 tests** across 22 test files.
|
||||
|
||||
## v1.8.4
|
||||
- **Multi-word AND search**: `search_nodes` now requires all words to match (case-insensitive), producing more precise results.
|
||||
- **Call target resolution**: Bare call targets are resolved to qualified names using same-file definitions, improving `callers_of`/`callees_of` accuracy.
|
||||
- **Impact radius pagination**: `get_impact_radius` returns `truncated` flag and `total_impacted` count; `max_results` parameter controls output size.
|
||||
- **`find_large_functions_tool`**: New MCP tool to find functions, classes, or files exceeding a line-count threshold.
|
||||
- **15 languages**: Added Vue SFC and Solidity support.
|
||||
- **Documentation overhaul**: All docs updated with accurate language/tool counts, version references, and VS Code extension parity.
|
||||
|
||||
## v1.8.3
|
||||
- **Parser recursion guard**: `_MAX_AST_DEPTH = 180` prevents stack overflow on deeply nested ASTs.
|
||||
- **Module cache bound**: `_MODULE_CACHE_MAX = 15,000` with automatic eviction.
|
||||
- **Embeddings thread safety**: `check_same_thread=False` on EmbeddingStore SQLite.
|
||||
- **Embeddings retry logic**: Exponential backoff for Google Gemini API calls.
|
||||
- **Visualization XSS hardening**: `</` escaped to `<\/` in JSON serialization.
|
||||
- **CLI error handling**: Split broad `except` into specific handlers.
|
||||
- **Git timeout**: Configurable via `CRG_GIT_TIMEOUT` env var.
|
||||
- **Governance files**: CONTRIBUTING.md, SECURITY.md, CODE_OF_CONDUCT.md.
|
||||
|
||||
## v1.8.2
|
||||
- **C# parsing fix**: Renamed language identifier from `c_sharp` to `csharp`.
|
||||
- **Watch mode thread safety**: SQLite connections compatible with Python 3.10/3.11 watchdog threads.
|
||||
- **Full rebuild cleanup**: Purges stale data from deleted files during full rebuild.
|
||||
- **Dependency trim**: Removed unused `gitpython` dependency.
|
||||
|
||||
## v1.7.0
|
||||
- **`install` command**: New primary entry point for setup (`code-review-graph install`). `init` remains as an alias.
|
||||
- **`--dry-run` flag**: Preview what `install`/`init` would write without modifying files.
|
||||
- **PyPI auto-publish**: GitHub releases now automatically publish to PyPI.
|
||||
- **README rewrite**: Professional documentation with real benchmark data from httpx, FastAPI, and Next.js.
|
||||
|
||||
## v1.6.4
|
||||
- **Portable MCP config**: `init` now generates `uvx`-based `.mcp.json` — no absolute paths, works on any machine with `uv` installed
|
||||
- **Removed symlink workaround**: The `_safe_path` helper for spaces-in-paths is no longer needed with `uvx`
|
||||
|
||||
## v1.6.3
|
||||
- **SessionStart hook**: Claude Code automatically prefers graph MCP tools over full codebase scans at session start
|
||||
- **Marketplace ready**: plugin.json corrected for official Claude Code plugin marketplace submission
|
||||
- **README cleanup**: Removed screenshot placeholders
|
||||
|
||||
## v1.6.2
|
||||
- **24 audit fixes**: Critical bug fixes, performance improvements, parser enhancements, expanded test coverage
|
||||
- **Parser: C/C++ support**: Full node extraction for C and C++ (classes, functions, imports, calls, inheritance)
|
||||
- **Parser: name extraction**: Fixed for Kotlin, Swift (simple_identifier), Ruby (constant)
|
||||
- **Performance**: NetworkX graph caching, batch edge queries, chunked embedding search, git subprocess timeouts
|
||||
- **CI hardening**: Coverage enforcement (50%), bandit security scanning, mypy type checking
|
||||
- **Tests**: +40 new tests for incremental updates, embeddings, and 7 new language fixtures
|
||||
- **Docs**: API response schemas, ignore pattern documentation, fixed hook config reference
|
||||
- **Accessibility**: ARIA labels throughout D3.js visualization
|
||||
|
||||
## v1.5.3
|
||||
- **Spaces-in-path handling**: *(superseded in v1.6.4 by `uvx`-based config)* Previously used symlinks for spaces in paths
|
||||
- **No git required**: `build`, `status`, `visualize`, `watch` now work on any directory without git
|
||||
- **Plugin ready**: Skills registered in plugin.json, SKILL.md frontmatter fixed
|
||||
- **File organization**: Generated files moved into `.code-review-graph/` directory (auto-created `.gitignore`, legacy migration)
|
||||
- **Visualization density**: Starts collapsed (File nodes only), search bar, clickable edge type toggles, scale-aware layout for large graphs
|
||||
- **Project cleanup**: Removed redundant `references/`, `agents/`, `settings.json`
|
||||
|
||||
## v1.4.0
|
||||
- **`init` command**: Automatic `.mcp.json` setup for Claude Code integration
|
||||
- **Interactive D3.js graph visualization**: `code-review-graph visualize` generates an HTML graph you can explore in-browser
|
||||
- **Documentation overhaul**: Comprehensive docs audit across all reference files
|
||||
|
||||
## v1.3.0
|
||||
- **Python version check with Docker fallback**: Automatically detects Python 3.10+ and suggests Docker if unavailable
|
||||
- **Universal install**: `pip install code-review-graph` — no git clone needed
|
||||
- **CLI entry point**: `code-review-graph` command available system-wide after pip install
|
||||
|
||||
## v1.2.0
|
||||
- **Logging improvements**: Structured logging throughout the codebase
|
||||
- **Watch debounce**: Smarter file-change detection in watch mode
|
||||
- **tools.py fixes**: Bug fixes and reliability improvements for MCP tools
|
||||
- **CI coverage**: GitHub Actions CI/CD pipeline with test coverage reporting
|
||||
|
||||
## v1.1.0
|
||||
- **Watch mode**: `code-review-graph watch` — auto-rebuilds graph on file changes
|
||||
- **Vector embeddings**: Optional `pip install .[embeddings]` for semantic code search
|
||||
- **Go, Rust, Java verified**: 12+ languages with dedicated test coverage
|
||||
- **47 tests passing**, 8 MCP tools registered
|
||||
- README badges and cleaner install flow
|
||||
|
||||
## v1.0.0 (Foundation)
|
||||
- **Persistent SQLite knowledge graph** — zero external dependencies
|
||||
- **Tree-sitter multi-language parsing** — classes, functions, imports, calls, inheritance
|
||||
- **Incremental updates** via `git diff` + automatic dependency cascade
|
||||
- **Impact-radius / blast-radius analysis** — BFS through call/import/inheritance graph
|
||||
- **6 MCP tools** for full graph interaction
|
||||
- **3 review-first skills**: build-graph, review-delta, review-pr
|
||||
- **PostToolUse hooks** (Write|Edit|Bash) for automatic background updates
|
||||
- **FastMCP 3.0 compatible** stdio MCP server
|
||||
|
||||
## Privacy & Data
|
||||
- Core graph data is stored locally
|
||||
- Graph stored in `.code-review-graph/graph.db` (SQLite), auto-gitignored
|
||||
- No telemetry; core graph/review workflows do not require network access
|
||||
- Optional embedding and wiki features may call configured local or remote services when explicitly enabled
|
||||
- Respects `.gitignore` and `.code-review-graphignore`
|
||||
@@ -0,0 +1,190 @@
|
||||
# GitHub Action: Risk-Scored PR Review
|
||||
|
||||
code-review-graph ships a composite GitHub Action (`action.yml` at the repo
|
||||
root) that posts a risk-scored, graph-aware review comment on every pull
|
||||
request — think of it as a hosted AI review bot (Greptile-style), except the
|
||||
analysis is **local-first**: the knowledge graph is built and queried entirely
|
||||
on your CI runner, and no source code is sent to any external service.
|
||||
|
||||
On each PR run the action:
|
||||
|
||||
1. Installs `code-review-graph` from PyPI.
|
||||
2. Restores the cached `.code-review-graph/` SQLite graph (or builds it from
|
||||
scratch on a cache miss) and incrementally re-parses the files changed by
|
||||
the PR.
|
||||
3. Runs `code-review-graph detect-changes --base origin/<base-branch>` to get
|
||||
risk-scored functions, affected execution flows, and test gaps.
|
||||
4. Renders a markdown report (via `scripts/render_pr_comment.py`) and upserts
|
||||
a single sticky PR comment — the same comment is updated on every push, so
|
||||
the PR thread is never spammed.
|
||||
5. Optionally fails the job when the overall risk score crosses a threshold
|
||||
(`fail-on-risk`).
|
||||
|
||||
## Quick start (external repositories)
|
||||
|
||||
```yaml
|
||||
# .github/workflows/code-review-graph.yml
|
||||
name: code-review-graph
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: tirth8205/[email protected]
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
That is the whole setup. The default `GITHUB_TOKEN` provided by Actions is
|
||||
sufficient — no PAT, no API key, no third-party service.
|
||||
|
||||
Self-hosted runners must be version `2.327.1` or newer. The composite action
|
||||
uses Node 24-based GitHub actions, including `actions/setup-python@v6`,
|
||||
`actions/cache@v6`, and the recommended `actions/checkout@v7` example.
|
||||
|
||||
To turn the review into a merge gate:
|
||||
|
||||
```yaml
|
||||
- uses: tirth8205/[email protected]
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
fail-on-risk: high
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
| Input | Required | Default | Description |
|
||||
|-------|----------|---------|-------------|
|
||||
| `github-token` | yes | — | Token used to post the sticky PR comment via the GitHub API. The workflow's default `GITHUB_TOKEN` works when the job has `pull-requests: write`. |
|
||||
| `comment` | no | `true` | Post (and keep updated) the sticky PR comment. Set to `false` to run analysis/gating without commenting. |
|
||||
| `fail-on-risk` | no | `none` | Fail the job when the overall risk score reaches a level: `none` (never fail), `high` (risk ≥ 0.70), `critical` (risk ≥ 0.85). |
|
||||
| `python-version` | no | `3.12` | Python version used to run code-review-graph (3.10+ supported). |
|
||||
|
||||
## Outputs
|
||||
|
||||
| Output | Description |
|
||||
|--------|-------------|
|
||||
| `comment-file` | Runner-local path to the rendered markdown report. Use with `comment: false` when a separate trusted workflow will publish it. |
|
||||
|
||||
### Risk levels
|
||||
|
||||
`detect-changes` produces a 0.0–1.0 overall risk score (max across changed
|
||||
functions; see `code_review_graph/changes.py:compute_risk_score` for the
|
||||
scoring factors: flow participation, community crossing, test coverage,
|
||||
security-sensitive names, caller count). The action maps it to levels:
|
||||
|
||||
| Level | Score |
|
||||
|-------|-------|
|
||||
| low | < 0.40 |
|
||||
| medium | 0.40 – 0.69 |
|
||||
| high | 0.70 – 0.84 |
|
||||
| critical | ≥ 0.85 |
|
||||
|
||||
## What the comment contains
|
||||
|
||||
- **Overall risk** score and level, with counts of changed functions,
|
||||
affected flows, and test gaps.
|
||||
- **Risk-scored changes** — a table of the top changed symbols ordered by
|
||||
risk, with file:line locations and test-coverage status.
|
||||
- **Affected execution flows** — which entry-point flows the change touches,
|
||||
ordered by criticality.
|
||||
- **Test gaps** — changed functions with no direct test coverage.
|
||||
- **Token savings** — how many tokens the graph-backed report saved versus
|
||||
reading every changed file in full. This is the same `context_savings`
|
||||
estimate the CLI's Token Savings panel shows (a `chars / 4` approximation
|
||||
labelled `estimated: true` — see [REPRODUCING.md](REPRODUCING.md) for the
|
||||
calibration methodology).
|
||||
- A `Powered by code-review-graph` footer.
|
||||
|
||||
The comment starts with a hidden HTML marker
|
||||
(`<!-- code-review-graph-report -->`). The action looks the marker up via
|
||||
`gh api` on each run and PATCHes the existing comment instead of creating a
|
||||
new one (a "sticky" comment).
|
||||
|
||||
## Cache behavior
|
||||
|
||||
The action caches the `.code-review-graph/` directory (the SQLite graph
|
||||
database) with `actions/cache`:
|
||||
|
||||
- **Key**: `code-review-graph-schema9-<runner.os>-<hashFiles(lockfiles)>`,
|
||||
where the lockfile hash covers common Python/JS/Go/Rust/Ruby/PHP lockfiles
|
||||
(`uv.lock`, `poetry.lock`, `requirements*.txt`, `package-lock.json`,
|
||||
`go.sum`, `Cargo.lock`, …).
|
||||
- **Schema segment**: `schema9` tracks the database schema version
|
||||
(`LATEST_VERSION` in `code_review_graph/migrations.py`). It is bumped when
|
||||
the schema changes so stale caches are never restored across incompatible
|
||||
versions.
|
||||
- **Restore keys**: fall back to any cache for the same OS and schema, so a
|
||||
lockfile change still reuses the previous graph.
|
||||
- **On cache hit**: the action runs `code-review-graph update --base
|
||||
origin/<base-branch>`, which re-parses only the files that differ from the
|
||||
PR's base ref. If the restored database turns out to be unusable, it falls
|
||||
back to a full `build`.
|
||||
- **On cache miss**: a full `code-review-graph build` runs (one-time cost;
|
||||
subsequent PR runs are incremental).
|
||||
|
||||
## Security notes
|
||||
|
||||
- **Token scope**: direct commenting needs `contents: read` for checkout and
|
||||
`pull-requests: write` to post the comment. In the split fork-safe setup,
|
||||
the analysis workflow needs only `contents: read`; the trusted commenter
|
||||
needs only `actions: read` and `pull-requests: write`. Grant exactly those
|
||||
permissions in each workflow.
|
||||
- **Local-first**: analysis runs entirely on the runner. No code, diff, or
|
||||
metadata leaves GitHub's infrastructure; there is no external API, account,
|
||||
or key.
|
||||
- **Untrusted input**: all dynamic values (`github.base_ref`, the PR number,
|
||||
action inputs) are passed to scripts through environment variables, never
|
||||
interpolated into shell commands. The markdown renderer escapes
|
||||
table/markup characters and strips control characters from symbol names
|
||||
and file paths before they reach the comment body, on top of the
|
||||
server-side `_sanitize_name()` sanitization.
|
||||
- **Pinning**: when consuming the action from another repository, pin
|
||||
`uses:` to a release tag or commit SHA rather than `@main`.
|
||||
- **Fork PRs**: `pull_request` runs from forks receive a read-only
|
||||
`GITHUB_TOKEN`, so they cannot post the comment directly. Use an
|
||||
unprivileged `pull_request` workflow with `comment: false`, upload the
|
||||
`comment-file` as an artifact, and publish it from a separate trusted
|
||||
`workflow_run` workflow. See
|
||||
[`.github/workflows/pr-review.yml`](../.github/workflows/pr-review.yml) and
|
||||
[`.github/workflows/pr-review-comment.yml`](../.github/workflows/pr-review-comment.yml).
|
||||
GitHub loads the `workflow_run` workflow from the default branch, so the
|
||||
trusted commenting half becomes active only after that workflow is merged.
|
||||
The privileged workflow must verify the source event and analyzed commit,
|
||||
extract only under `runner.temp`, cap and validate the artifact, and add its
|
||||
own sticky marker before posting. Avoid `pull_request_target` with a checkout
|
||||
of PR code because it can execute untrusted code with a privileged token
|
||||
([details](https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/)).
|
||||
|
||||
## Dogfooding
|
||||
|
||||
This repository runs the action on its own PRs via
|
||||
[`.github/workflows/pr-review.yml`](../.github/workflows/pr-review.yml),
|
||||
which runs the local `action.yml` without write permissions and uploads the
|
||||
rendered report. The trusted
|
||||
[`pr-review-comment.yml`](../.github/workflows/pr-review-comment.yml) workflow
|
||||
validates that artifact and posts the sticky comment without checking out or
|
||||
executing PR-controlled code.
|
||||
|
||||
## Rendering script
|
||||
|
||||
The markdown rendering and risk gating logic lives in
|
||||
[`scripts/render_pr_comment.py`](../scripts/render_pr_comment.py) (stdlib
|
||||
only, unit-tested in `tests/test_action_render.py`) rather than inline YAML,
|
||||
so it can be tested and reused:
|
||||
|
||||
```bash
|
||||
code-review-graph detect-changes --base origin/main | \
|
||||
python scripts/render_pr_comment.py # markdown to stdout
|
||||
|
||||
python scripts/render_pr_comment.py --input report.json \
|
||||
--fail-on-risk high --quiet # gate only: exit 3 on breach
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
# Documentation Index
|
||||
|
||||
- [USAGE.md](USAGE.md) -- How to install and use
|
||||
- [FAQ.md](FAQ.md) -- How it compares to LSP, RAG, grep, and similar tools; when not to use it
|
||||
- [FEATURES.md](FEATURES.md) -- What's included, changelog
|
||||
- [COMMANDS.md](COMMANDS.md) -- All 31 MCP tools, 7 MCP prompts, skills, and CLI commands
|
||||
- [GITHUB_ACTION.md](GITHUB_ACTION.md) -- Risk-scored PR review comments via GitHub Actions
|
||||
- [CUSTOM_LANGUAGES.md](CUSTOM_LANGUAGES.md) -- Bring your own language via `.code-review-graph/languages.toml`
|
||||
- [LLM-OPTIMIZED-REFERENCE.md](../code_review_graph/docs/LLM-OPTIMIZED-REFERENCE.md) -- Token-optimized reference for MCP-capable AI coding agents
|
||||
- [architecture.md](architecture.md) -- System design and data flow
|
||||
- [schema.md](schema.md) -- Graph node/edge schema, SQLite tables (including flows, communities, FTS5)
|
||||
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) -- Common issues and fixes (including Windows/WSL)
|
||||
- [REPRODUCING.md](REPRODUCING.md) -- Reproducing every benchmark number (pinned SHAs, seeded runs, tokenizer calibration)
|
||||
- [ROADMAP.md](ROADMAP.md) -- Shipped and planned features
|
||||
- [LEGAL.md](LEGAL.md) -- License and privacy
|
||||
@@ -0,0 +1,16 @@
|
||||
# Legal & Privacy
|
||||
|
||||
**License:** MIT (see [LICENSE](../LICENSE) in project root)
|
||||
|
||||
**Privacy:**
|
||||
- Zero telemetry
|
||||
- All graph data stored locally in `.code-review-graph/graph.db`
|
||||
- Core graph build, review, search, and CLI/MCP workflows run locally
|
||||
- Optional local embeddings may download a sentence-transformers model from HuggingFace when first used
|
||||
- Optional cloud embedding providers (`openai`, `google`, `minimax`, `voyage`) send embedded source snippets to the configured provider only when explicitly selected
|
||||
- Remote embedding providers print an egress warning unless `CRG_ACCEPT_CLOUD_EMBEDDINGS=1` is set
|
||||
- Streamable HTTP MCP transport binds to localhost by default
|
||||
|
||||
**Data:** Core graph data stays on your machine. If you opt into a cloud embedding provider, the text being embedded leaves your machine under that provider's terms.
|
||||
|
||||
**Warranty:** Provided as-is, without warranty of any kind.
|
||||
@@ -0,0 +1,291 @@
|
||||
# Maintainer reconciliation — 2026-07-17
|
||||
|
||||
Status: local and remote CI validation complete; ready for maintainer review.
|
||||
|
||||
Base: `main` at `b72413c`
|
||||
Integration branch: `codex/reconcile-open-contributions-2026-07-17`
|
||||
Tracking issue: `crg-nqi`
|
||||
|
||||
## Outcome
|
||||
|
||||
This branch is a deliberately narrow reconciliation of independently useful,
|
||||
evidence-backed fixes. It is not a release branch and it does not merge any
|
||||
large contribution wholesale. Contributor commits were retained where the
|
||||
patch was already the strongest implementation; conflict resolutions preserve
|
||||
current `main` behavior and are called out below.
|
||||
|
||||
The audit snapshot covered:
|
||||
|
||||
- every local branch, worktree, stash, tracked change, and untracked path;
|
||||
- all 104 open pull requests, using paginated live data rather than a
|
||||
single-page search result;
|
||||
- all 84 open issues, excluding pull requests;
|
||||
- all 29 repository discussions; and
|
||||
- the repository knowledge graph, affected flows, tests, release notes, and
|
||||
current CI/review evidence.
|
||||
|
||||
No remote issue or source pull request is closed by this branch. Those actions
|
||||
should happen only after this integration passes review and is merged.
|
||||
|
||||
## Preservation and safety record
|
||||
|
||||
The primary checkout remains on `main` at `b72413c`, equal to `origin/main`.
|
||||
Its 31 untracked paths were not moved, cleaned, staged, or rewritten:
|
||||
|
||||
- 26 iCloud-suffixed `* 2.*` copies are byte-identical to tracked files; and
|
||||
- five unique local artifacts remain private to the checkout: `.codex/`, a
|
||||
local transcript, `OC3_TECHNICAL_CONTRIBUTION.md`, its PDF, and
|
||||
`PRESENTATION_BRIEF.md`.
|
||||
|
||||
All three stashes were preserved:
|
||||
|
||||
- `stash@{0}` — CI lint/test fixes from merged PRs;
|
||||
- `stash@{1}` — local `uv.lock` bump; and
|
||||
- `stash@{2}` — scaling/token-efficiency mypy fixes.
|
||||
|
||||
All pre-existing worktrees and branches were preserved, including
|
||||
`claude/hungry-morse`, `fix/incremental-flow-path-mismatch`,
|
||||
`release/v2.3.7`, `release/v2.4.0`, `review/local-fixes`, the old workflow
|
||||
branches, and their untracked worktree files. The reconciliation was performed
|
||||
only in `.claude/worktrees/codex-reconciliation`.
|
||||
|
||||
Important local-branch conclusions:
|
||||
|
||||
- `review/local-fixes` is a preservation source, not a merge candidate. Its
|
||||
useful TESTED_BY work was extracted. Its incremental path work was disproven
|
||||
under the real node-replacement lifecycle; unsafe PID cleanup, over-broad
|
||||
ignore rules, raw C++ header sniffing, and scoped-call false positives also
|
||||
remain excluded.
|
||||
- `release/v2.4.0` is the head of PR #559. Its token-budget, doctor, eval, and
|
||||
installer surfaces remain coupled and have correctness/supply-chain
|
||||
blockers. Four patch-equivalent commits were selected: the three-commit
|
||||
TESTED_BY series and the independent Action path-rendering fix.
|
||||
- `release/v2.3.7` is PR #559 plus a version downgrade and must not be merged or
|
||||
pushed as a release candidate.
|
||||
- `issue-194-specific-exception-logging` is patch-equivalent to work already on
|
||||
`main`; the multi-word search branches are superseded or need decomposition.
|
||||
|
||||
## Selected integration
|
||||
|
||||
| Branch commit(s) | Source | Decision and evidence |
|
||||
| --- | --- | --- |
|
||||
| `34c5d00` | PR #564 | Use `#graph-svg` instead of a page-wide `svg` selector in both templates; carries focused regressions. |
|
||||
| `d8e5453` | PR #565 | Remove machine-specific hook paths, add a PATH guard, and avoid applying Bash hooks to unrelated tools. |
|
||||
| `cbf9355` | PR #573 | Resolve PHP `use`, grouped imports, aliases, functions, and constants to local files; preserves contributor attribution. |
|
||||
| `ddc8544`, `918ef13`, `580205d` | exact patch-equivalents of PR #559 commits `278e400`, `03e319e`, `a11dc04` | Correct TESTED_BY direction at every selected consumer, update dead-code analysis, and add a parser-to-store-to-query regression. This incorporates the #527 work and supersedes overlapping PR #598. |
|
||||
| `6ece151` | PR #559 commit | Render repository-relative paths in Action comments without taking the rest of the release branch. |
|
||||
| `771307e` | issue #612 | Capture bare, member, chained, and null-conditional C# receiver calls with correct caller attribution; implemented red/green with focused tests. |
|
||||
| `eeef686` | issue #613 | Keep packaged documentation fallback available through the real MCP wrapper; implemented red/green with an installed-layout regression. |
|
||||
| `571f665` | content-equivalent/rebased port of PR #578 | Replace regex JSONC stripping with a string-aware scanner so URLs and comment-like string contents survive; import-neighborhood context differs from the source patch. |
|
||||
| `df87b60`, `e5f563b` | PR #563 | Generate the uppercase `SKILL.md` filename required by the [Claude Code skills documentation](https://code.claude.com/docs/en/skills) and update regressions. This does not adopt PR #562's unnecessary lowercasing of the display name. |
|
||||
| `90408c9` | PR #354 | Refuse and preserve valid top-level arrays/scalars, while treating empty/comment-only configs as fresh objects. Conflict resolution retained PR #578's stronger string-aware JSONC parser. Production fixes for #312/#350 were intentionally omitted because they are already on `main`; their regressions remain. |
|
||||
| `0abd789` | PR #353 | Persist Kotlin/C# annotations using the established metadata shape and resolve C# namespace importers. This does not claim to solve the remaining impact-radius design in #310. |
|
||||
| `b9ec19d` | PR #393 | Repair advertised Zig parsing and add structure/call/import/test fixtures. Conflict resolution retained the newer Nix implementation on `main`. |
|
||||
| `fc549ae` | reconciliation review fix | Preserve an existing platform config byte-for-byte when its nested server collection has the wrong array/object type; red/green coverage exercises both schemas. |
|
||||
| `d611a2d` | reconciliation review fix | Generate TESTED_BY for in-source Zig tests regardless of filename and carry effective parent names through nested C# namespaces; both gaps were reproduced before implementation. |
|
||||
| `c7d7211` | reconciliation review fix | Replace recursive C# namespace discovery with an explicit stack; a 1,200-level AST regression failed before the change and now passes without truncating namespace metadata. |
|
||||
|
||||
The final diff size and repository-wide validation results are recorded below.
|
||||
|
||||
## Pull-request inventory and dispositions
|
||||
|
||||
Live pagination returned 104 open PRs: 100 on page 1 and four on page 2. All
|
||||
target `main`; 102 are non-draft, while #582 and #618 are drafts. Each open PR
|
||||
appears exactly once in the routing inventory below.
|
||||
|
||||
- Selected-area or directly overlapping work (24): #621, #618, #611, #601,
|
||||
#598, #586, #583, #582, #578, #573, #572, #568, #566, #565, #564, #562,
|
||||
#559, #538, #530, #527, #477, #354, #353, #92.
|
||||
- Parser/language work (29): #614, #602, #591, #590, #589, #580, #577, #560,
|
||||
#539, #526, #522, #517, #516, #514, #462, #459, #393, #415, #339, #338,
|
||||
#337, #333, #332, #331, #330, #329, #328, #252, #95.
|
||||
- Graph/search/performance/product work (25): #615, #606, #605, #604, #603,
|
||||
#600, #599, #581, #555, #552, #536, #509, #468, #460, #458, #457, #452,
|
||||
#394, #341, #340, #336, #335, #334, #327, #326.
|
||||
- Platform/install/CI/dependency/docs work (26): #617, #597, #596, #595, #584,
|
||||
#563, #557, #556, #554, #548, #547, #546, #545, #544, #543, #542, #540,
|
||||
#531, #505, #495, #491, #453, #449, #373, #347, #129.
|
||||
|
||||
The routing groups are not blanket approvals. Material non-selection decisions:
|
||||
|
||||
- PR #559 is not safe to merge wholesale. Its advertised hard token cap only
|
||||
constrains snippets: a 44-file run with source disabled and a nominal 6,000
|
||||
token limit still returned roughly 1.67 million characters. The lean default
|
||||
hides tools that its own prompts and recovery text require. Eval can reuse
|
||||
stale results after ignored failures; doctor can report false health and
|
||||
mutate the database; installers execute floating network content. Separate
|
||||
Beads issues `crg-1nx` and `crg-4ys` track the redesign.
|
||||
- PR #601's bare endpoint resolver is complementary to the TESTED_BY direction
|
||||
fix, but it activates global unique-name resolution without import evidence
|
||||
and materially changes graph communities. It needs precision and performance
|
||||
evaluation before adoption.
|
||||
- PR #568 and the related local scoped resolver can manufacture global
|
||||
`Class.method` edges from uniqueness alone and add full-scan work. They remain
|
||||
excluded pending scoped identity semantics.
|
||||
- PR #586 prevents row loss but still binds ambiguous overload calls to the
|
||||
first definition. Stable symbol identity is tracked in `crg-lw5`.
|
||||
- PR #611 plausibly avoids an embedding import race but adds about seven seconds
|
||||
of eager startup latency. It needs concurrency coverage and an explicit
|
||||
latency decision.
|
||||
- Draft PR #618 is stronger than #566 for Git paths because it uses NUL-delimited
|
||||
bytes and `os.fsdecode`; it remains separate until its draft/CI state and
|
||||
overlap with branch/tracked-output behavior are resolved.
|
||||
- PR #621 is the focused Windows Codex-hook candidate, but target-native command
|
||||
execution was not covered by this branch's Linux CI. Its contributor-authored
|
||||
patch was removed from this integration and moved to dedicated
|
||||
[draft PR #626](https://github.com/tirth8205/code-review-graph/pull/626) for
|
||||
Windows testing.
|
||||
- PRs #595, #597, and #596 form a promising Windows daemon sequence, but they
|
||||
require genuine Windows execution and should not be hidden inside this
|
||||
cross-platform reconciliation.
|
||||
- PR #615 contains a credible small inherited-file-descriptor fix but no
|
||||
regression. Reproduce the zombie-process failure and add one first.
|
||||
- PR #477 contains useful second-template visualization work but emits a literal
|
||||
escaped quote in generated JavaScript. PR #564 is the safe subset; remaining
|
||||
behavior needs browser/`node --check` coverage.
|
||||
- PRs #457 and #552 have the same head and an under-specified three-second cache
|
||||
key. PRs #458 and #460 are stale/unmergeable token alternatives; #604 adds a
|
||||
broad provenance surface; #536 adds a large optional DSL. These need isolated
|
||||
product/API review.
|
||||
- PRs #326–#341 are a cumulative stale stack whose tip includes large obsolete
|
||||
deletions. Broad parser/framework PRs, platform integrations, dependencies,
|
||||
translations, and product features remain independent review units rather
|
||||
than being bundled here.
|
||||
- PRs #556/#557 address fork-PR comments but need a clean port, explicit
|
||||
`actions: read` and `issues: write` permissions, actionlint, and fork security
|
||||
verification; #557's raw head also contains unrelated parser/package-lock
|
||||
changes.
|
||||
- PR #491's uninstall design can delete user-owned Cursor scripts, misses Gemini
|
||||
MCP state, parses JSONC unsafely, and duplicates platform inventories.
|
||||
- PR #459 may spawn a parser-probe subprocess per file. PR #394 is optional
|
||||
defense in depth because the supported FastMCP version already threadpools
|
||||
synchronous handlers.
|
||||
|
||||
CI evidence is sparse: only PR #559 had both successful CI and PR Review runs at
|
||||
the audit snapshot. Many fork workflows show `action_required`, which is neither
|
||||
a pass nor a failure. Contributor-reported results were treated as supporting
|
||||
evidence, never as a substitute for validation of this combined branch.
|
||||
|
||||
## Open-issue inventory
|
||||
|
||||
All 84 open issues were read and classified exactly once:
|
||||
|
||||
- Confirmed/actionable (21): #623, #622, #620, #619, #616, #613, #612, #610,
|
||||
#609, #585, #579, #576, #500, #475, #473, #461, #343, #310, #291, #173,
|
||||
#63.
|
||||
- Local/release partial or fixed (18): #574, #569, #567, #561, #558, #553,
|
||||
#551, #550, #549, #537, #534, #523, #515, #497, #463, #450, #419, #295.
|
||||
- Already solved on `main` or release-pending (10): #524, #471, #243, #218,
|
||||
#212, #190, #132, #91, #87, #83.
|
||||
- Support/retest (5): #474, #314, #262, #209, #189.
|
||||
- Feature backlog (25): #607, #593, #592, #588, #587, #521, #518, #504,
|
||||
#482, #478, #436, #434, #430, #429, #369, #348, #346, #320, #311, #305,
|
||||
#269, #265, #232, #210, #199.
|
||||
- Insufficient evidence/discussion (5): #535, #532, #506, #492, #426.
|
||||
|
||||
Selected patches address or materially advance #523 (visualization), #549 and
|
||||
#558 (portable hooks), #574 (PHP imports), #515 (TESTED_BY via the #559 subset),
|
||||
#553 (JSONC), #612, #613, and #295. PR #353 advances only the namespace
|
||||
importer portion of #310; its impact-radius/detect-changes BFS remains open.
|
||||
Issues #561 and #567 remain unaddressed because PRs #562 and #568 are absent.
|
||||
Issue #622's collision/overload problem is intentionally deferred because the
|
||||
open patch is not a complete identity model. Issues #619, #616, and #610 need
|
||||
separate API, platform-discovery, and startup-latency decisions respectively.
|
||||
|
||||
Issue #569 remains open. The audited local/PR #572 variants normalize paths
|
||||
after incremental reparsing has already replaced node IDs; existing flow and
|
||||
community memberships therefore reference deleted nodes and modified files can
|
||||
still be skipped. A lifecycle-aware fix needs a regression that changes graph
|
||||
topology, not only a path-format fixture.
|
||||
|
||||
## Discussion inventory
|
||||
|
||||
All 29 discussions were enumerated and read. The threads with direct engineering
|
||||
implications are #501, #464, #137, #376, #355, #414, #410, #318, #467, #479,
|
||||
and #405:
|
||||
|
||||
- #467 is real evidence for a lean tool surface, but does not validate PR #559's
|
||||
current cap implementation.
|
||||
- #318 and #410 support trustworthy status/doctor UX, while strengthening the
|
||||
requirement that diagnostics be non-mutating and fail honestly.
|
||||
- #501 supports portable PowerShell/Codex hooks and informed the dedicated
|
||||
validation path for PR #621; #405 shows the hook contract still needs clearer
|
||||
documentation.
|
||||
- #464 and #137 reinforce worktree/monorepo-safe path and registry behavior.
|
||||
- #376 and #355 reinforce explicit inclusion/exclusion semantics; they do not
|
||||
justify hiding source paths with broad ignore patterns.
|
||||
- #414 informs scalability claims, and #479 supports fixing both visualization
|
||||
templates rather than only the first page shape.
|
||||
|
||||
The remaining support, setup, product, or announcement discussions were #525,
|
||||
#411, #105, #375, #109, #254, #206, #111, #131, #89, #186, #178, #134, #113,
|
||||
#101, #96, #85, and #84. They provide documentation/backlog context but no
|
||||
additional change was safe to couple into this branch.
|
||||
|
||||
## Follow-up tracking and merge sequence
|
||||
|
||||
The audit created focused Beads issues rather than hiding unresolved work in a
|
||||
large branch:
|
||||
|
||||
- `crg-1nx` — redesign v2.4 token budgeting and the lean tool surface;
|
||||
- `crg-4ys` — split/harden doctor, eval, and installers;
|
||||
- `crg-ys0` — remove unsafe blockers from `review/local-fixes`;
|
||||
- `crg-lw5` — design stable symbol identity for collisions/overloads;
|
||||
- `crg-o1d` — repair #569 across incremental node-ID replacement;
|
||||
- `crg-dtv` — close SQLite connections exposed by coverage warnings;
|
||||
- `crg-8u4` — exclude maintainer-only `.beads` hooks from the sdist; and
|
||||
- existing platform/performance issues remain the owners for Windows daemon,
|
||||
HOME isolation, and daemon-stop behavior.
|
||||
|
||||
Recommended review order:
|
||||
|
||||
1. path/edge semantics and their end-to-end tests;
|
||||
2. parser changes by language (PHP, C#, Kotlin, Zig);
|
||||
3. skills/config/hook compatibility and Windows CI;
|
||||
4. visualization, Action rendering, and packaged docs;
|
||||
5. release-note accuracy and potential source-PR/issue closure only after merge.
|
||||
|
||||
## Validation record
|
||||
|
||||
Completed on the assembled branch:
|
||||
|
||||
- final Python 3.13 suite excluding the known native WatchDaemon failure:
|
||||
`1,447 passed`, `13 deselected`, `2 xpassed`;
|
||||
- isolated CI-equivalent coverage run: `1,446 passed`, `1 skipped`,
|
||||
`13 deselected`, `2 xpassed`; coverage `72.95%` against a `65%` threshold;
|
||||
- combined skills/multilingual regression run: `501 passed`, plus the final
|
||||
C# namespace regression class: `6 passed`;
|
||||
- ruff: clean; mypy: no issues in 62 source files; Bandit: no issues;
|
||||
- Python and VS Code schema versions both `9`;
|
||||
- wheel and sdist built successfully, and both contain the packaged
|
||||
`LLM-OPTIMIZED-REFERENCE.md` required by the docs fallback;
|
||||
- full knowledge-graph rebuild: 181 parsed files, 3,415 nodes, 24,940 edges,
|
||||
200 flows, 16 communities, and no build errors;
|
||||
- graph review: 25 changed files, risk score `0.65`, 26 affected flows; the
|
||||
parser breadth is the main blast radius and received two independent review
|
||||
passes plus focused language regressions; and
|
||||
- draft PR #624: lint, mypy, Bandit, schema sync, PR Review, GitGuardian, and
|
||||
the Python 3.10, 3.11, 3.12, and 3.13 test jobs all passed; and
|
||||
- `git diff --check`: clean.
|
||||
|
||||
The independent reviews first found five P1/P2 gaps: the two #569 lifecycle
|
||||
defects were removed, while the nested-config, Zig TESTED_BY, and nested-C#
|
||||
cases were fixed red/green. A second pass found the deep C# recursion failure;
|
||||
that too was fixed red/green. No other P1/P2 finding remained.
|
||||
|
||||
The macOS Python 3.13 baseline has a native watchdog/FSEvents `SIGBUS` in
|
||||
`TestWatchDaemon` on both `main` and the integration branch. It is tracked in
|
||||
`crg-229` and is excluded from broad comparison runs; it must not be represented
|
||||
as a regression introduced here.
|
||||
|
||||
The isolated coverage run emits 29 `ResourceWarning`s for unclosed SQLite
|
||||
connections; `crg-dtv` tracks turning those warnings into deterministic closes.
|
||||
Package inspection also found pre-existing maintainer-only `.beads` hooks in the
|
||||
sdist; `crg-8u4` tracks the manifest policy fix. Neither is hidden as a passing
|
||||
claim.
|
||||
|
||||
The Windows hook patch from PR #621 is intentionally absent from this
|
||||
integration. It remains in
|
||||
[draft PR #626](https://github.com/tirth8205/code-review-graph/pull/626) until
|
||||
target-native CI and maintainer review verify command execution, stdin draining,
|
||||
failure behavior, and upgrades from existing Unix-only hook entries.
|
||||
@@ -0,0 +1,480 @@
|
||||
# Reproducing the Benchmarks
|
||||
|
||||
This document gives the exact commands to reproduce every benchmark number
|
||||
shown in the README and the `diagrams/`. Two people running the recipe below
|
||||
on different machines on different days should produce identical numbers,
|
||||
within float rounding.
|
||||
|
||||
If you get different numbers, that's a bug — please file an issue.
|
||||
|
||||
## Verifying the "saved tokens" number
|
||||
|
||||
The CLI's `Token Savings` panel uses a `chars / 4` approximation labelled
|
||||
`estimated: true`, not a model-specific tokenizer. The approximation is
|
||||
designed to be both fast (no model load, no inference) and conservative.
|
||||
|
||||
### How to verify against a real tokenizer
|
||||
|
||||
```bash
|
||||
pip install tiktoken
|
||||
code-review-graph detect-changes --brief --verify
|
||||
```
|
||||
|
||||
The panel grows a `Verified (tiktoken)` row showing the same calculation
|
||||
done with OpenAI's `cl100k_base` tokenizer (the GPT-4 family). If the
|
||||
estimate is significantly off, you'll see it immediately:
|
||||
|
||||
```text
|
||||
┌───────────────────────── Token Savings ─────────────────────────┐
|
||||
│ Full context would be: 12,921 tokens │
|
||||
│ Graph context used: 762 tokens │
|
||||
│ Saved: 12,159 tokens (~94%) │
|
||||
│ Verified (tiktoken): 10,835 tokens (~93%) [11,611 → 776] │
|
||||
│ Breakdown: Functions 244 · Tests 191 · Risk 244 · Other 83 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Calibration result (committed)
|
||||
|
||||
A one-time calibration across 222 files / 2.2 MB of mixed source
|
||||
(Python, JS, TS, Go, Rust, RST, MD) pulled from the 6 test repos:
|
||||
|
||||
| Repo | sample files | bytes | chars/4 estimate | tiktoken real | ratio est/real |
|
||||
|---|---:|---:|---:|---:|---:|
|
||||
| flask | 46 | 470,179 | 117,559 | 109,969 | 1.069 |
|
||||
| fastapi | 38 | 156,224 | 39,072 | 34,897 | 1.120 |
|
||||
| gin | 30 | 471,793 | 117,962 | 132,296 | 0.892 |
|
||||
| express | 23 | 296,805 | 74,207 | 83,575 | 0.888 |
|
||||
| httpx | 38 | 254,184 | 63,556 | 62,909 | 1.010 |
|
||||
| code-review-graph | 47 | 539,206 | 134,820 | 120,760 | 1.116 |
|
||||
| **OVERALL** | **222** | **2,188,391** | **547,176** | **544,406** | **1.005** |
|
||||
|
||||
`chars / 4` is within **+0.5%** of real GPT-4 tokens in aggregate. Per-repo
|
||||
it swings between **-11%** (gin: lots of short Go identifiers) and **+12%**
|
||||
(fastapi: heavy docstrings and type hints), but the **ratio** stabilizes
|
||||
because both sides of the divide are equally biased.
|
||||
|
||||
Reproduce the calibration with the snippet in this commit's
|
||||
`code_review_graph/context_savings.py:verify_with_tiktoken`, or
|
||||
inline-run the `--verify` flag on any commit.
|
||||
|
||||
## What is and isn't deterministic
|
||||
|
||||
| Reproducible | Reason |
|
||||
|---|---|
|
||||
| Tree-sitter parsing | Pure function of input bytes |
|
||||
| Node / edge counts | Deterministic upserts keyed by `qualified_name` |
|
||||
| FTS5 BM25 scores | Deterministic |
|
||||
| Embeddings via `all-MiniLM-L6-v2` on CPU | Model weights cache-pinned by SHA in HuggingFace cache |
|
||||
| Leiden community IDs | Seeded — `_LEIDEN_SEED=42` in `communities.py`, override with `CRG_LEIDEN_SEED` env var |
|
||||
| `naive_corpus_tokens` | Deterministic for a fixed git checkout |
|
||||
| `git clone` at a pinned SHA | Determines the source-of-truth byte stream |
|
||||
|
||||
What used to make it **non**-reproducible (now fixed):
|
||||
|
||||
- `commit: HEAD` in every `code_review_graph/eval/configs/*.yaml` — replaced with the pinned latest test-commit SHA per repo
|
||||
- `git clone --depth 50` silently fell back to wrong commits when the pinned SHAs were beyond the shallow window — now uses full clones with explicit `returncode` checks
|
||||
- Leiden ran with an unseeded RNG — now seeded
|
||||
- `nextjs.yaml` was a misnamed config evaluating this repo — renamed to `code-review-graph.yaml`
|
||||
- FTS5 was created but never populated by the eval framework's `full_build` call — `code_review_graph/eval/runner.py` now calls `postprocessing.run_post_processing` directly
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.10 or newer
|
||||
- `git` on PATH
|
||||
- Network access (~600 MB to clone the 6 upstream repos)
|
||||
- ~3 GB free disk
|
||||
- For the embedding step: roughly 700 MB extra for `torch` + `sentence-transformers`
|
||||
|
||||
## Step 1 — Install with the right extras
|
||||
|
||||
```bash
|
||||
git clone https://github.com/tirth8205/code-review-graph
|
||||
cd code-review-graph
|
||||
|
||||
# eval extras: pyyaml + matplotlib (matplotlib only needed for `--report`)
|
||||
# embeddings extras: sentence-transformers + numpy
|
||||
uv sync --extra eval --extra embeddings # or: pip install -e ".[eval,embeddings]"
|
||||
```
|
||||
|
||||
## Step 2 — Run the formal eval
|
||||
|
||||
This step clones 6 upstream repositories at pinned SHAs, builds a full graph
|
||||
for each (parser + cross-file resolvers + signatures + FTS5 + flows + Leiden
|
||||
communities), then runs the `token_efficiency`, `impact_accuracy`,
|
||||
`agent_baseline`, and `multi_hop_retrieval` benchmarks.
|
||||
|
||||
```bash
|
||||
uv run code-review-graph eval \
|
||||
--benchmark token_efficiency,impact_accuracy,agent_baseline,multi_hop_retrieval
|
||||
```
|
||||
|
||||
Failure semantics (applies to every benchmark): a thrown tool call is **not**
|
||||
a measurement. The row is kept in the CSV with `status=error` for forensics,
|
||||
but excluded from every aggregate. (Two historical bugs made failures look
|
||||
like wins: a thrown `get_review_context` produced `graph_tokens=0` and a
|
||||
ratio of `naive/1`, and a thrown `analyze_changes` silently set
|
||||
`predicted = changed`, guaranteeing recall 1.0. Both are fixed; regression
|
||||
tests live in `tests/test_eval.py`.)
|
||||
|
||||
Expected runtime on an M1/M2 Mac: roughly 8–15 minutes for the build phase,
|
||||
plus seconds per benchmark.
|
||||
|
||||
Outputs:
|
||||
|
||||
- `evaluate/test_repos/{express,fastapi,flask,gin,httpx,code-review-graph}/`
|
||||
- `evaluate/test_repos/<name>/.code-review-graph/graph.db`
|
||||
- `evaluate/results/<name>_<benchmark>_<date>.csv`
|
||||
|
||||
## Step 3 — Generate embeddings (required for the standalone benchmark)
|
||||
|
||||
The standalone token benchmark ships with 5 hardcoded natural-language
|
||||
questions. Without embeddings, hybrid search can't match them and the
|
||||
benchmark silently returns 0× reduction ratios (a loud warning will print).
|
||||
|
||||
```bash
|
||||
for repo in express fastapi flask gin httpx code-review-graph; do
|
||||
uv run code-review-graph embed --repo "evaluate/test_repos/$repo"
|
||||
done
|
||||
```
|
||||
|
||||
Expected runtime: 2–5 minutes total. Vectors live inside the same `graph.db`.
|
||||
|
||||
## Step 4 — Run the standalone token benchmark
|
||||
|
||||
This benchmark compares **all source-file tokens** in the repo against
|
||||
**5 search hits + a few neighbor edges** for each of 5 sample questions. The
|
||||
ratio answers: *how many tokens does the graph let me skip on a typical
|
||||
question?*
|
||||
|
||||
```bash
|
||||
uv run python <<'PY'
|
||||
import json
|
||||
from pathlib import Path
|
||||
from code_review_graph.graph import GraphStore
|
||||
from code_review_graph.token_benchmark import run_token_benchmark
|
||||
|
||||
results = {}
|
||||
for repo in sorted(Path("evaluate/test_repos").iterdir()):
|
||||
db = repo / ".code-review-graph" / "graph.db"
|
||||
if not db.exists():
|
||||
continue
|
||||
store = GraphStore(str(db))
|
||||
try:
|
||||
results[repo.name] = run_token_benchmark(store, repo)
|
||||
finally:
|
||||
store.close()
|
||||
|
||||
print(f"{'Repo':<22}{'naive_tokens':>16}{'avg_graph_tokens':>20}{'avg_ratio':>14}")
|
||||
print("-" * 72)
|
||||
for name, out in sorted(results.items(), key=lambda x: -x[1]["average_reduction_ratio"]):
|
||||
pq = out["per_question"]
|
||||
avg_graph = int(sum(r["graph_tokens"] for r in pq) / max(len(pq), 1))
|
||||
print(f"{name:<22}{out['naive_corpus_tokens']:>16,}"
|
||||
f"{avg_graph:>20,}{out['average_reduction_ratio']:>13.1f}×")
|
||||
|
||||
Path("evaluate/standalone_token_benchmark.json").write_text(json.dumps(results, indent=2))
|
||||
PY
|
||||
```
|
||||
|
||||
## Canonical numbers
|
||||
|
||||
<!-- BEGIN canonical-stats -->
|
||||
Re-captured **2026-08-02** on macOS arm64 (Apple M4 Pro, 14 cores, 24 GB),
|
||||
Python 3.13.12, code-review-graph 2.3.7, sentence-transformers 5.6.1,
|
||||
`all-MiniLM-L6-v2`, `CRG_LEIDEN_SEED=42`, from clean clones at the pinned SHAs.
|
||||
If your numbers differ by more than rounding, something in the chain has
|
||||
drifted — file an issue.
|
||||
|
||||
### Standalone token benchmark (`code_review_graph/token_benchmark.py`)
|
||||
|
||||
Each row is the average of 5 sample questions (`how does authentication work`,
|
||||
`what is the main entry point`, `how are database connections managed`,
|
||||
`what error handling patterns are used`, `how do tests verify core functionality`).
|
||||
|
||||
| Repo | snapshot SHA | naive_corpus_tokens | avg graph_tokens | avg ratio |
|
||||
|---|---|---:|---:|---:|
|
||||
| fastapi | `22381558` | 948,793 | 2,653 | **375.6×** |
|
||||
| flask | `a29f88ce` | 143,594 | 2,196 | **71.0×** |
|
||||
| code-review-graph | `84bde354` | 208,821 | 3,190 | **68.1×** |
|
||||
| gin | `5c00df8a` | 166,868 | 2,766 | **61.9×** |
|
||||
| httpx | `b55d4635` | 142,356 | 2,661 | **60.6×** |
|
||||
| express | `b4ab7d65` | 136,052 | 3,936 | **36.0×** |
|
||||
|
||||
Range across 6 repos: **36× – 376×**; median **~65×**.
|
||||
|
||||
These replace the 2026-05-25 capture, and every ratio is lower. Two causes,
|
||||
both verified by re-running from clean clones: `avg graph_tokens` rose in
|
||||
every repo (the per-node embedding text grew richer, so a 5-hit search
|
||||
response carries more text), and fastapi is now measured at its current pin
|
||||
`22381558` instead of the retired `0227991a`. `naive_corpus_tokens` is
|
||||
unchanged for `code-review-graph` and `gin`, confirming the corpus side of
|
||||
the ratio is stable and the movement is on the graph-response side.
|
||||
|
||||
### Formal `token_efficiency` benchmark (`code_review_graph/eval/benchmarks/token_efficiency.py`)
|
||||
|
||||
A different denominator: just the **changed-file content** for each commit,
|
||||
vs the full `get_review_context()` JSON. For small commits the response is
|
||||
larger than the input (it carries impact-radius edges + source snippets), so
|
||||
ratios here are intentionally < 1.0 — that is not a bug, it measures a
|
||||
different thing than the standalone benchmark.
|
||||
|
||||
Raw per-commit CSVs in `evaluate/results/<repo>_token_efficiency_*.csv`.
|
||||
|
||||
### Impact accuracy (`code_review_graph/eval/benchmarks/impact_accuracy.py`)
|
||||
|
||||
13 commits across 6 repos. The benchmark emits two ground-truth modes side
|
||||
by side, distinguished by the `ground_truth_mode` CSV column:
|
||||
|
||||
| Mode | Ground truth | What it tells you |
|
||||
|---|---|---|
|
||||
| `graph-derived (circular — upper bound)` | changed files + files with CALLS/IMPORTS_FROM edges into them — **derived from the same graph the predictor traverses** | An upper bound. Recall 1.0 here is partly true by construction, not independent evidence. |
|
||||
| `co-change (same commit, seed excluded)` | the *other* files the author actually touched in the same commit, given a single seed file | Independent-ish evidence from git history. Expect substantially lower recall. |
|
||||
|
||||
The canonical numbers below were captured **in graph-derived mode only**
|
||||
(the co-change mode did not exist at capture time). Treat the recall row as
|
||||
a circular upper bound, not as "100% recall":
|
||||
|
||||
| Metric (graph-derived mode — circular upper bound) | Value |
|
||||
|---|---|
|
||||
| Recall (mean across 13 commits) | **1.000** (upper bound on every commit) |
|
||||
| F1 (mean) | **0.693** |
|
||||
| F1 (median) | 0.667 |
|
||||
| F1 (min / max) | 0.465 / 1.000 |
|
||||
|
||||
Co-change mode was captured on 2026-08-02 and is **not yet usable**: every
|
||||
graded commit came back with `predicted_files = 0`, so the resulting F1 of
|
||||
0.000 measures a broken harness, not the predictor. No co-change number is
|
||||
quoted until that is fixed. Single-file commits are separately recorded with
|
||||
`status=skipped` (there is nothing independent to grade against).
|
||||
|
||||
The blast-radius analysis over-predicts in some commits (precision ≈ 0.30 in the
|
||||
worst case, where 34 files are flagged for a 10-file change). That is
|
||||
intentional: a missed dependency is worse than an extra reviewed file.
|
||||
|
||||
### Multi-hop retrieval (`code_review_graph/eval/benchmarks/multi_hop_retrieval.py`)
|
||||
|
||||
11 hand-curated tasks across the 6 repos. Each task is a 2-step tool chain:
|
||||
|
||||
1. `hybrid_search(nl_query, limit=10)` looks for a starting anchor node.
|
||||
2. `query_graph(<traversal_pattern>, target=<anchor>)` walks one hop along
|
||||
`callers_of` / `callees_of` / `tests_for` / `imports_of` / etc.
|
||||
|
||||
The task **scores 1.0** only if both the anchor is found in the top-K *and*
|
||||
the expected neighbor names are returned by the traversal. **Scores 0.0**
|
||||
otherwise (which collapses both "search missed the anchor" and "traversal
|
||||
returned the wrong set" — split those by inspecting `anchor_found` and
|
||||
`neighbor_recall` in the per-task CSV row).
|
||||
|
||||
| Repo | Task | Anchor found | Rank | Neighbor recall | Score |
|
||||
|---|---|---|---:|---:|---:|
|
||||
| code-review-graph | crg-parse-file-callers | yes | 0 | 1.00 | **1.00** |
|
||||
| code-review-graph | crg-upsert-node-callers | yes | 4 | 1.00 | **1.00** |
|
||||
| express | express-create-application-callees | yes | 1 | 1.00 | **1.00** |
|
||||
| fastapi | fastapi-route-handler-callers | yes | 6 | 1.00 | **1.00** |
|
||||
| fastapi | fastapi-get-dependant-callers | no | — | 0.00 | **0.00** |
|
||||
| flask | flask-dispatch-callers | yes | 3 | 1.00 | **1.00** |
|
||||
| flask | flask-exception-callers | yes | 5 | 1.00 | **1.00** |
|
||||
| gin | gin-serve-http-callees | yes | 5 | 1.00 | **1.00** |
|
||||
| gin | gin-context-next-callers | yes | 0 | 1.00 | **1.00** |
|
||||
| httpx | httpx-client-request-callers | yes | 0 | 1.00 | **1.00** |
|
||||
| httpx | httpx-async-request-tests | yes | 7 | 1.00 | **1.00** |
|
||||
|
||||
**Average score across 11 tasks: 0.909**. 10/11 tasks pass; the one remaining
|
||||
miss (`fastapi-get-dependant-callers`) targets a function spelled `get_dependant`
|
||||
("dependant" with an `a`) from a query phrased as "dependency declarations into
|
||||
a tree" — there is no lexical overlap and no extractable identifier in the
|
||||
query for the boosting heuristic to lock onto. Left as an honest miss; the
|
||||
fix would be either query rewriting or a richer embedding model.
|
||||
|
||||
#### How the score went from 0.545 to 0.909 (the same-day fix)
|
||||
|
||||
The v1 scaffold first scored **0.545** (6/11). Two changes brought it to
|
||||
**0.909** (10/11), both deterministic, both small, both committed in this
|
||||
same session:
|
||||
|
||||
1. **`embeddings.py:_node_to_text`** — the embedded text per node used to be
|
||||
just `"{name} {kind} in {parent}"`. It now also includes the dotted form
|
||||
(`APIRoute.get_route_handler`), the identifier split into words
|
||||
(`get route handler`), and the enclosing module directory (`routing`,
|
||||
`fastapi`, `dependencies`). All re-embeddings are automatic — the text
|
||||
hash changes, `EmbeddingStore.embed_nodes` re-embeds. See
|
||||
`_split_identifier` for the casing/separator rules.
|
||||
|
||||
2. **`search.py:extract_query_identifiers`** — natural-language queries
|
||||
like "Who advances the gin middleware chain via Context.Next" now have
|
||||
their dotted / snake_case / CamelCase identifier tokens extracted. Search
|
||||
results whose `qualified_name` contains any extracted identifier get a
|
||||
2.0× boost. This pushed `Context.Next` from rank 11 to rank 0.
|
||||
|
||||
The remaining `fastapi-get-dependant-callers` failure cannot be fixed by
|
||||
either change because the query doesn't share any identifier or substring
|
||||
with the target — that's the boundary of the heuristic.
|
||||
|
||||
This benchmark is a v1 scaffold (11 tasks). The intent is to track the
|
||||
**multi-hop tool chain** as the agent's actual usage pattern rather than just
|
||||
single-shot retrieval. Adding more tasks: append `multi_hop_tasks:` entries
|
||||
to any config under `code_review_graph/eval/configs/*.yaml` with the schema:
|
||||
|
||||
```yaml
|
||||
multi_hop_tasks:
|
||||
- id: my-task-id # required, unique
|
||||
nl_query: "natural language" # required, what an agent would ask
|
||||
anchor_qualified_suffix: # required, lowercased suffix of expected
|
||||
"rel/path.py::owner.symbol" # qualified_name (case-insensitive endswith)
|
||||
traversal_pattern: callers_of # one of callers_of|callees_of|imports_of|
|
||||
# importers_of|tests_for|inheritors_of|children_of
|
||||
expected_neighbor_names: # required, list of bare names that should
|
||||
- "expected_one" # appear in the traversal result
|
||||
k: 10 # optional, top-K depth for the search step
|
||||
```
|
||||
|
||||
### Build stats
|
||||
|
||||
| Repo | Nodes | Edges | Flows | Communities | Embeddings | FTS idx rows |
|
||||
|---|---:|---:|---:|---:|---:|---:|
|
||||
| fastapi | 6,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 | — |
|
||||
|
||||
Node and edge counts are from the same 2026-08-02 clean-room build as the
|
||||
table above. Flow, community and FTS-segment counts were not re-captured in
|
||||
that run and are left as `—` rather than carried over from the older capture.
|
||||
|
||||
Embeddings count is lower than node count because File nodes aren't
|
||||
embedded. FTS idx rows are far lower than node count because FTS5 stores
|
||||
inverted-index segments, not one row per indexed document.
|
||||
<!-- END canonical-stats -->
|
||||
|
||||
## Incremental update latency
|
||||
|
||||
The README and diagram 4 quote an incremental-update time. This is how it was
|
||||
measured. Unlike the benchmarks above there is no runner for it — it is a
|
||||
stopwatch on the CLI — so the recipe is written out in full.
|
||||
|
||||
Corpus: a shallow clone of `django/django` (2,927 `.py` files; the graph
|
||||
indexes 2,998 files, 46,683 nodes, 392,758 edges). Machine: Apple M4 Pro
|
||||
(14 cores, 24 GB), macOS 26.5.2, Python 3.13.12, code-review-graph 2.3.7.
|
||||
|
||||
```bash
|
||||
git clone --depth 1 https://github.com/django/django.git
|
||||
cd django
|
||||
/usr/bin/time -p code-review-graph build # cold build
|
||||
|
||||
/usr/bin/time -p code-review-graph update # no-op: nothing changed
|
||||
echo "# edit" >> django/db/models/query.py
|
||||
echo "# edit" >> django/http/response.py
|
||||
/usr/bin/time -p code-review-graph update --skip-flows # the path the hooks run
|
||||
/usr/bin/time -p code-review-graph update # full post-processing
|
||||
```
|
||||
|
||||
| Scenario | Wall clock | Files re-parsed |
|
||||
|---|---:|---:|
|
||||
| Cold full build | 40.3 s | 2,998 |
|
||||
| `update`, nothing changed | 1.4 s | 0 |
|
||||
| `update --skip-flows`, 2 files edited (hook path) | 2.4 – 2.9 s | 2 |
|
||||
| `update`, 2 files edited (full post-processing) | 9.8 s | 2 |
|
||||
|
||||
Two things worth reading off this table. First, roughly 1.4 s of every figure
|
||||
is process start-up — that is what the no-op run costs — so the marginal cost
|
||||
of a two-file edit on the hook path is about 1 s. Second, only the **2 edited
|
||||
files** are re-parsed. Dependents are found through the graph's import and
|
||||
call edges, but any dependent whose SHA-256 is unchanged is skipped before
|
||||
parsing (`incremental.py`), so the re-parse count tracks what you edited, not
|
||||
the size of the dependency cascade.
|
||||
|
||||
Earlier versions of this project quoted "under 2 seconds on a ~2,900-file
|
||||
repo". At that corpus size that holds only for the no-op case; a real edit on
|
||||
the hook path is ~2.5 s, and ~10 s if flow and community detection also run.
|
||||
|
||||
## Agent baseline benchmark (`code_review_graph/eval/benchmarks/agent_baseline.py`)
|
||||
|
||||
The whole-corpus baseline in the standalone token benchmark is an upper
|
||||
bound no real agent pays. This benchmark simulates what an agent actually
|
||||
does without the graph:
|
||||
|
||||
1. Derive search terms from each question in the config's `agent_questions:`
|
||||
list (identifier-shaped tokens via `search.extract_query_identifiers`,
|
||||
plus plain keywords; falls back to the `search_queries` query strings
|
||||
when absent).
|
||||
2. Pure-python grep over the corpus (no external `rg`/`grep` binary),
|
||||
ranking source files by total case-insensitive match count
|
||||
(deterministic; ties break on path).
|
||||
3. Read the top-3 files and token-count them (`chars/4`) as
|
||||
`baseline_tokens`.
|
||||
4. Compare against the graph-query cost for the same question (5 hybrid
|
||||
search hits + up to 5 neighbor edges per hit — the same accounting as the
|
||||
standalone benchmark).
|
||||
|
||||
Output: `evaluate/results/<repo>_agent_baseline_<date>.csv` with a
|
||||
`baseline_to_graph_ratio` per question. Rows where either side is zero are
|
||||
marked `status=no_graph_results` / `status=no_baseline_match` and excluded
|
||||
from aggregates (`agent_baseline.aggregate`). No canonical capture exists
|
||||
yet; numbers will be added to the canonical block above once captured —
|
||||
they are not quoted before being measured.
|
||||
|
||||
## Weekly CI run (report-only)
|
||||
|
||||
`.github/workflows/eval.yml` runs every Monday at 06:23 UTC (plus manual
|
||||
`workflow_dispatch`) against the two smallest pinned configs (`httpx`,
|
||||
`flask`) with the `token_efficiency`, `impact_accuracy`, and
|
||||
`agent_baseline` benchmarks. It uploads the CSVs as an artifact and writes
|
||||
a job-summary table. It is deliberately **report-only**: regressions do not
|
||||
fail the default branch yet.
|
||||
|
||||
## Which benchmark measures what
|
||||
|
||||
There are four different "token" benchmarks in the repo. They are all valid
|
||||
but measure different scenarios:
|
||||
|
||||
| Benchmark | Naive baseline | Graph cost | Question answered |
|
||||
|---|---|---|---|
|
||||
| `code_review_graph/eval/benchmarks/token_efficiency.py` | sum of **changed-file content** for a specific commit | full `get_review_context()` JSON | "Is the graph cheaper than just reading the diffed files?" |
|
||||
| `code_review_graph/eval/benchmarks/agent_baseline.py` | **grep top-3 files** for the question's identifiers | 5 search hits + 5 neighbor edges per question | "Is the graph cheaper than a realistic grep-and-read agent?" |
|
||||
| `code_review_graph/eval/token_benchmark.py` | none — absolute per-workflow cost | sum of 5 MCP-tool responses | "How many tokens does a complete agent workflow cost?" |
|
||||
| `code_review_graph/token_benchmark.py` (standalone) | sum of **all source files** in repo | 5 search hits + 5 neighbor edges per question | "Is the graph cheaper than reading the whole repo?" |
|
||||
|
||||
The `code_review_graph/eval/benchmarks/token_efficiency.py` numbers can be **less than 1.0×**
|
||||
for small commits (`get_review_context` carries impact-radius metadata and
|
||||
source snippets, which outweigh a tiny changed-file set). The standalone
|
||||
benchmark numbers are **always large** because the baseline is the entire
|
||||
repo — that is why the README leads with the median (~65×) and treats 376×
|
||||
as the max, and why `agent_baseline` exists as the realistic middle ground.
|
||||
Pick the one that matches the scenario you're talking about.
|
||||
|
||||
## Generating diagrams
|
||||
|
||||
The 9 diagrams in `diagrams/` are produced from `diagrams/generate_diagrams.py`.
|
||||
Excalidraw source files (`.excalidraw`) are gitignored (`*.excalidraw` line in
|
||||
`.gitignore`); only the rendered PNGs are tracked. Regenerate after a
|
||||
benchmark refresh:
|
||||
|
||||
```bash
|
||||
uv run python diagrams/generate_diagrams.py
|
||||
# Open each .excalidraw at https://excalidraw.com to render/export
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**`git clone failed`** — Network or upstream rate-limit. The fix is a clean
|
||||
retry; the eval doesn't auto-retry by design (loud failures > silent
|
||||
fallback).
|
||||
|
||||
**`git checkout <sha> failed`** — Upstream rewrote history or removed the
|
||||
SHA. File an issue with the failing config so we can re-pin.
|
||||
|
||||
**`No embeddings found in this graph`** warning during the standalone
|
||||
benchmark — you skipped Step 3. Run it.
|
||||
|
||||
**Different community IDs between runs** — Make sure you're on the seeded
|
||||
`communities.py`. Check `grep _LEIDEN_SEED code_review_graph/communities.py`.
|
||||
You can override the seed via `CRG_LEIDEN_SEED=<int>` but all collaborators
|
||||
must agree on the same value.
|
||||
|
||||
**Different `naive_corpus_tokens` than the canonical table** — Make sure
|
||||
`git rev-parse HEAD` inside each `evaluate/test_repos/<name>` matches the
|
||||
`commit:` field in the corresponding config file. If not, delete the clone
|
||||
and let Step 2 re-clone at the pinned SHA.
|
||||
@@ -0,0 +1,114 @@
|
||||
# Roadmap
|
||||
|
||||
## Shipped
|
||||
|
||||
### v2.3.6
|
||||
- **Custom languages without forking**: `.code-review-graph/languages.toml` maps extensions and node types to any tree-sitter-language-pack grammar (`docs/CUSTOM_LANGUAGES.md`)
|
||||
- **GitHub Action** for risk-scored PR review comments: graph built/restored on the CI runner, sticky comment upserted per push, optional `fail-on-risk` merge gate; dogfooded via `.github/workflows/pr-review.yml` (`docs/GITHUB_ACTION.md`)
|
||||
- **`agent_baseline` benchmark**: graph queries vs a realistic grep-and-read-top-k agent baseline, wired into all six pinned eval configs
|
||||
- **Co-change ground truth** for `impact_accuracy`; the legacy graph-derived metric is labelled as a circular upper bound
|
||||
- **Weekly eval CI**: report-only cron run of the two smallest configs (`.github/workflows/eval.yml`)
|
||||
- **`docs/FAQ.md`**: comparisons with LSP, RAG, grep/agentic search, and adjacent tools, plus when-not-to-use guidance
|
||||
- **Contribution scaffolding**: issue forms, PR template, dependabot config
|
||||
- **Windows fixes** for `daemon status` (#511) and `detect-changes` path mapping (#528)
|
||||
- **Reliability**: embedding provider-name validation, SQLite store-leak fixes in analysis/wiki tools, `fastmcp<4` cap, hooks installed via `git rev-parse --git-path hooks`
|
||||
|
||||
### v2.3.5
|
||||
- **Token Savings panel** on `detect-changes --brief` and the new `update --brief` — boxed CLI output with per-category breakdown that sums exactly to the graph response size
|
||||
- **`--verify` flag** cross-checks the displayed savings against OpenAI's `cl100k_base` tokenizer; calibration data committed in `docs/REPRODUCING.md` shows the estimate is within ~1% of real GPT-4 tokens in aggregate
|
||||
- **`code-review-graph embed`** CLI subcommand for explicit embedding generation
|
||||
- **Deterministic eval pipeline**: pinned upstream SHAs in every config, full clones with `returncode` checks, fixed-seed Leiden community detection (`CRG_LEIDEN_SEED`)
|
||||
- **`multi_hop_retrieval` benchmark**: 11 curated 2-step tool-chain tasks; average score 0.909
|
||||
- **Richer embedding text** and **identifier-aware search boost** lift multi-hop accuracy from 0.545 to 0.909
|
||||
- **Path normalization fix** in the eval pipeline + test-gap dedup in the brief summary
|
||||
- **`docs/REPRODUCING.md`**: end-to-end recipe with canonical numbers and tiktoken calibration table
|
||||
- Demo GIF (`diagrams/context-savings-demo.gif`) showing both CLI surfaces and `--verify`
|
||||
|
||||
### v2.3.4
|
||||
- 30 MCP tools and 5 MCP prompts
|
||||
- Estimated context savings metadata for review, impact, detect-changes, and compact architecture responses
|
||||
- Compact architecture overview by default to reduce large MCP payloads
|
||||
- Bounded change-analysis controls for large diffs (`CRG_MAX_CHANGED_FUNCS`, `CRG_MAX_TRANSITIVE_FRONTIER`, `CRG_TOOL_TIMEOUT`)
|
||||
- Windows FastMCP semantic-search deadlock mitigation
|
||||
- Rust test detection and path lookup correctness fixes
|
||||
- Documentation and release metadata refreshed for the 2.3.4 release
|
||||
|
||||
### v2.3.3
|
||||
- Broad parser surface expansion across source languages, shell scripts, notebooks, and SFC-style files
|
||||
- Additional AI coding platform install targets including Gemini CLI, Qwen, Kiro, Qoder, and GitHub Copilot variants
|
||||
- Streamable HTTP MCP transport on localhost
|
||||
- Parser/resolver, Windows, FastMCP, and daemon reliability fixes
|
||||
- Community PR sweep and VS Code accessibility improvements
|
||||
|
||||
### v2.2.0
|
||||
- Multi-repo watch daemon (`crg-daemon` / `code-review-graph daemon`)
|
||||
- TOML-based daemon configuration (`~/.code-review-graph/watch.toml`)
|
||||
- Child process management: one `code-review-graph watch` process per repo
|
||||
- Config file watching with automatic reconciliation of watcher processes
|
||||
- Daemonization with PID file management
|
||||
- Health checking with automatic restart of dead watchers
|
||||
- Standalone `crg-daemon` CLI entry point (7 subcommands)
|
||||
- Integrated `daemon` subcommand group in main CLI
|
||||
|
||||
### v2.0.0
|
||||
- 22 MCP tools (up from 9) and 5 MCP prompts
|
||||
- 18 languages (added Dart, R, Perl)
|
||||
- Execution flow detection with criticality scoring
|
||||
- Community detection (Leiden algorithm via igraph, file-based fallback)
|
||||
- Architecture overview with coupling warnings
|
||||
- Risk-scored change detection (`detect_changes`)
|
||||
- Refactoring tools (rename preview, dead code, suggestions)
|
||||
- Wiki generation from community structure
|
||||
- Multi-repo registry with cross-repo search
|
||||
- FTS5 full-text search with porter stemming
|
||||
- Database migrations (v1-v5)
|
||||
- Evaluation framework with matplotlib visualization
|
||||
- TypeScript tsconfig path alias resolution
|
||||
- MiniMax embedding provider (embo-01)
|
||||
- Optional dependency groups: `[embeddings]`, `[google-embeddings]`, `[communities]`, `[eval]`, `[wiki]`, `[all]`
|
||||
- 486 tests across 22 test files
|
||||
|
||||
### v1.8.4
|
||||
- Multi-word AND search, call target resolution, impact radius pagination
|
||||
- `find_large_functions_tool`, Vue SFC and Solidity support
|
||||
- Documentation overhaul
|
||||
|
||||
### v1.7.0
|
||||
- `install` command as primary entry point (`init` kept as alias)
|
||||
- `--dry-run` flag for previewing install/init changes
|
||||
- Automatic PyPI publishing via GitHub Actions on release
|
||||
- README rewrite with real benchmark data from httpx, FastAPI, and Next.js
|
||||
|
||||
### v1.6.x
|
||||
- Portable `uvx`-based MCP config
|
||||
- SessionStart hook for automatic graph tool preference
|
||||
- 24 audit fixes: C/C++ support, performance, CI hardening
|
||||
|
||||
### v1.5.x
|
||||
- Generated files in `.code-review-graph/` directory
|
||||
- Visualization density: collapsed start, search, edge toggles
|
||||
- Works without git
|
||||
|
||||
### v1.4.0
|
||||
- `init` command, interactive D3.js visualization, `serve` command
|
||||
|
||||
### v1.3.0
|
||||
- Universal pip install, CLI entry point, Python version check
|
||||
|
||||
### v1.1.0-v1.2.0
|
||||
- Watch mode, vector embeddings, logging, CI coverage
|
||||
|
||||
### v1.0.0 (Foundation)
|
||||
- Persistent SQLite knowledge graph, Tree-sitter parsing, incremental updates
|
||||
- Impact radius analysis, 6 MCP tools, 3 skills
|
||||
|
||||
## Planned
|
||||
|
||||
- GitHub App / bot mode beyond the shipped GitHub Action (org-wide install, check runs)
|
||||
- Team sync (shared graph via git-tracked DB)
|
||||
- Performance optimization for monorepos (>50k files)
|
||||
|
||||
## Ongoing
|
||||
|
||||
- Additional language grammars as requested
|
||||
- Integration updates as AI coding platforms evolve
|
||||
@@ -0,0 +1,190 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Quick reference for common install/setup problems
|
||||
|
||||
Four issues account for most support questions. Check these first:
|
||||
|
||||
### 1. `Hooks use a matcher + hooks array` error in `.claude/settings.json`
|
||||
|
||||
**You're on a pre-v2.2.3 release.** v2.2.1 and v2.2.2 shipped a broken hook schema — flat `{matcher, command, timeout}` entries without the required nested `hooks: []` array, timeouts in milliseconds instead of seconds, and a `PreCommit` event that isn't a real Claude Code event. PR #208 (shipped in v2.2.3) rewrote the generator to emit the correct v1.x+ schema.
|
||||
|
||||
**Fix:**
|
||||
|
||||
```bash
|
||||
pip install --upgrade code-review-graph # → v2.2.4 or later
|
||||
cd /path/to/your/project
|
||||
code-review-graph install # rewrites .claude/settings.json
|
||||
```
|
||||
|
||||
The re-install merge-replaces the entire broken `hooks` block with the new nested format and drops a real git pre-commit hook into the hooks directory resolved via `git rev-parse --git-path hooks` — typically `.git/hooks/pre-commit`, but linked worktrees and `core.hooksPath` (husky) setups are handled too. That's where "check before commit" lives in v2.2.3+, not in Claude Code settings.
|
||||
|
||||
Valid Claude Code hook events are: `PreToolUse`, `PostToolUse`, `UserPromptSubmit`, `Stop`, `SubagentStop`, `SessionStart`, `SessionEnd`, `PreCompact`, `Notification`. There is no `PreCommit`.
|
||||
|
||||
### 2. `code-review-graph: command not found` after `pip install`
|
||||
|
||||
`pip install` put the console script into a `bin/` directory that isn't on your `$PATH`. Four fixes, in order of recommendation:
|
||||
|
||||
**Option 1 — Use `pipx` (cleanest):**
|
||||
|
||||
```bash
|
||||
pip uninstall code-review-graph
|
||||
pipx install code-review-graph
|
||||
```
|
||||
|
||||
`pipx` installs CLI tools in an isolated venv. If the command is not found afterwards, run `pipx ensurepath` or add `~/.local/bin` to your PATH.
|
||||
|
||||
**Option 2 — Use `uvx` (no install needed):**
|
||||
|
||||
```bash
|
||||
uvx code-review-graph install
|
||||
uvx code-review-graph build
|
||||
```
|
||||
|
||||
**Option 3 — Run it as a Python module (always works):**
|
||||
|
||||
```bash
|
||||
python -m code_review_graph install
|
||||
python -m code_review_graph build
|
||||
```
|
||||
|
||||
**Option 4 — Fix PATH manually:**
|
||||
|
||||
```bash
|
||||
pip show code-review-graph | grep Location
|
||||
# Find the sibling `bin/` directory; on macOS user installs this is
|
||||
# typically ~/Library/Python/3.X/bin. Add it to your shell rc:
|
||||
echo 'export PATH="$HOME/Library/Python/3.12/bin:$PATH"' >> ~/.zshrc
|
||||
source ~/.zshrc
|
||||
```
|
||||
|
||||
### 3. Is code-review-graph project-scoped or user-scoped?
|
||||
|
||||
**Both** — four different pieces, each scoped differently:
|
||||
|
||||
| Piece | Scope | Where |
|
||||
|-------------------------------|----------------|------------------------------------------------------------------|
|
||||
| The Python package | User-scoped | Install once via `pip`/`pipx`/`uvx` |
|
||||
| The graph database | Project-scoped | `.code-review-graph/graph.db` inside each project |
|
||||
| MCP server config (`.mcp.json`) | Project-scoped | Claude Code launches one MCP server per project, with `cwd=<project>` |
|
||||
| Multi-repo registry | User-scoped | `~/.code-review-graph/registry.json` (only for `cross_repo_search`) |
|
||||
|
||||
**TL;DR**: install the tool **once**, then run `code-review-graph install && code-review-graph build` inside **each** project you want graph-aware reviews in.
|
||||
|
||||
### 4. Using a venv? You must update `settings.json` manually
|
||||
|
||||
Claude Code hooks and MCP tool paths in `.claude/settings.json` are **hardcoded at install time**. If you switch to (or create) a virtual environment after running `code-review-graph install`, the paths will still point to the old interpreter and the server will silently fail or use the wrong Python.
|
||||
|
||||
**Fix — update the `command`/`args` in `.mcp.json` and any hook commands in `.claude/settings.json` to match your venv:**
|
||||
|
||||
```json
|
||||
// .mcp.json — point to your venv's Python or uvx inside the venv
|
||||
{
|
||||
"mcpServers": {
|
||||
"code-review-graph": {
|
||||
"command": "/path/to/your/venv/bin/uvx",
|
||||
"args": ["code-review-graph", "serve"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Or simply re-run `code-review-graph install` **from within the activated venv** so the paths are regenerated correctly:
|
||||
|
||||
```bash
|
||||
source .venv/bin/activate # activate your venv first
|
||||
code-review-graph install # rewrites .mcp.json and hook paths
|
||||
```
|
||||
|
||||
Then fully quit and reopen Claude Code so it picks up the new config.
|
||||
|
||||
### 5. "I built the graph but Claude Code doesn't see it in a new session"
|
||||
|
||||
Most likely causes, ranked:
|
||||
|
||||
1. **You didn't restart Claude Code after `install`.** Claude Code reads `.mcp.json` at startup — if you ran `install` in one session, fully quit and reopen Claude Code for the MCP server to register.
|
||||
2. **New session's `cwd` is a different directory.** The MCP server is launched with `cwd=<project>` and it reads `.code-review-graph/graph.db` from there. If your new session opened in a parent folder or a different project, it won't find the graph you built.
|
||||
3. **You ran `build` but not `install`.** `build` creates `graph.db`; `install` is what registers the MCP server with Claude Code via `.mcp.json`. You need both.
|
||||
4. **MCP server is crashing on startup.** Run `/mcp` inside Claude Code to see server status, or check `~/Library/Logs/Claude/mcp*.log` on macOS.
|
||||
|
||||
**Quick checklist:**
|
||||
|
||||
```bash
|
||||
cd /path/to/your/project
|
||||
code-review-graph status # should print Files/Nodes/Edges from the built graph
|
||||
ls .mcp.json # should exist
|
||||
cat .mcp.json # should reference `code-review-graph serve`
|
||||
# then: fully quit Claude Code and reopen it inside this project
|
||||
```
|
||||
|
||||
If `status` shows the graph but `/mcp` in the new session doesn't list `code-review-graph`, the `.mcp.json` isn't in the session's `cwd` — re-run `code-review-graph install` from the correct project root.
|
||||
|
||||
---
|
||||
|
||||
## Database lock errors
|
||||
The graph uses SQLite with WAL mode. If you see lock errors:
|
||||
- Ensure only one build process runs at a time
|
||||
- The database auto-recovers; just retry
|
||||
- Delete `.code-review-graph/graph.db-wal` and `.code-review-graph/graph.db-shm` if corrupt
|
||||
|
||||
## Large repositories (>10k files)
|
||||
- First build may take 30-60 seconds
|
||||
- Subsequent incremental updates are fast (~2.5s on a ~3,000-file repo, hook path)
|
||||
- Add more ignore patterns to `.code-review-graphignore`:
|
||||
```
|
||||
generated/**
|
||||
vendor/**
|
||||
*.min.js
|
||||
```
|
||||
|
||||
## Missing nodes after build
|
||||
- Check that the file's language is supported (see [FEATURES.md](FEATURES.md))
|
||||
- Check that the file isn't matched by an ignore pattern
|
||||
- Run with `full_rebuild=True` to force a complete re-parse
|
||||
|
||||
## Graph seems stale
|
||||
- Hooks auto-update on edit/commit
|
||||
- If stale, run `/code-review-graph:build-graph` manually
|
||||
- Check that hooks are configured in `.claude/settings.json` (re-run `code-review-graph install` to regenerate)
|
||||
|
||||
## Embeddings not working
|
||||
- Install with: `pip install "code-review-graph[embeddings]"`
|
||||
- Run `embed_graph_tool` to compute vectors
|
||||
- First embedding run downloads the model (~90MB, one time)
|
||||
|
||||
## MCP server won't start
|
||||
- Verify `uv` is installed (`uv --version`; install with `pip install uv` or `brew install uv`)
|
||||
- Check that `uvx code-review-graph serve` runs without errors
|
||||
- If using a custom `.mcp.json`, ensure it uses `"command": "uvx"` with `"args": ["code-review-graph", "serve"]`
|
||||
- Re-run `code-review-graph install` to regenerate the config
|
||||
|
||||
## Windows / WSL
|
||||
|
||||
- Upgrade to v2.3.6+ if `daemon status` crashes with WinError 87 (#511) or CLI `detect-changes` maps 0 functions on Windows (#528) — both are fixed there
|
||||
- Use forward slashes in paths when passing `repo_root` to MCP tools
|
||||
- In WSL, ensure `uv` is installed inside WSL (not the Windows version): `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
||||
- If `uv` is not found after install, add `~/.cargo/bin` to your PATH
|
||||
- File watching (`code-review-graph watch`) may have delays on WSL1 due to filesystem event limitations; WSL2 is recommended
|
||||
- On Windows native (non-WSL), long path support may need to be enabled: `git config --system core.longpaths true`
|
||||
|
||||
## Community detection requires igraph
|
||||
|
||||
- Install with: `pip install "code-review-graph[communities]"`
|
||||
- Without igraph, community detection falls back to file-based grouping (less precise but functional)
|
||||
|
||||
## Wiki generation with LLM summaries
|
||||
|
||||
- Install with: `pip install "code-review-graph[wiki]"`
|
||||
- Requires a running Ollama instance for LLM-powered summaries
|
||||
- Without Ollama, wiki pages are generated with structural information only (no prose summaries)
|
||||
|
||||
## Optional dependency groups
|
||||
|
||||
If a tool returns an ImportError, install the relevant optional group:
|
||||
- `pip install "code-review-graph[embeddings]"` for semantic search
|
||||
- `pip install "code-review-graph[google-embeddings]"` for Google Gemini embeddings
|
||||
- OpenAI-compatible and MiniMax embeddings use stdlib HTTP clients and require only their environment variables
|
||||
- `pip install "code-review-graph[communities]"` for igraph-based community detection
|
||||
- `pip install "code-review-graph[enrichment]"` for Python call-resolution enrichment via Jedi
|
||||
- `pip install "code-review-graph[eval]"` for evaluation benchmarks (matplotlib)
|
||||
- `pip install "code-review-graph[wiki]"` for wiki LLM summaries (ollama)
|
||||
- `pip install "code-review-graph[all]"` for everything
|
||||
@@ -0,0 +1,179 @@
|
||||
# Code Review Graph — User Guide
|
||||
|
||||
**Applies to:** v2.3.6
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install code-review-graph
|
||||
code-review-graph install # auto-detects and configures all supported platforms
|
||||
code-review-graph build # parse your codebase
|
||||
```
|
||||
|
||||
`install` detects which AI coding tools you have, writes the correct MCP configuration for each one, and installs platform-native hooks where supported. Restart your editor/tool after installing.
|
||||
|
||||
To target a specific platform instead of auto-detecting all:
|
||||
|
||||
```bash
|
||||
code-review-graph install --platform codex
|
||||
code-review-graph install --platform cursor
|
||||
code-review-graph install --platform claude-code
|
||||
code-review-graph install --platform codebuddy
|
||||
```
|
||||
|
||||
### Supported Platforms
|
||||
|
||||
| Platform | Config file |
|
||||
|----------|-------------|
|
||||
| **Codex** | `~/.codex/config.toml` + `~/.codex/hooks.json` |
|
||||
| **Claude Code** | `.mcp.json` + `.claude/settings.json` |
|
||||
| **CodeBuddy Code** | `.mcp.json` + `CODEBUDDY.md` + `.codebuddy/settings.json` + `.codebuddy/skills/<name>/SKILL.md` |
|
||||
| **Cursor** | `.cursor/mcp.json` |
|
||||
| **Windsurf** | `~/.codeium/windsurf/mcp_config.json` |
|
||||
| **Zed** | `~/Library/Application Support/Zed/settings.json` (macOS) or `~/.config/zed/settings.json` |
|
||||
| **Continue** | `~/.continue/config.json` |
|
||||
| **OpenCode** | `opencode.jsonc` (preferred) or `opencode.json` |
|
||||
| **Antigravity** | `~/.gemini/antigravity/mcp_config.json` |
|
||||
| **Gemini CLI** | `.gemini/settings.json` |
|
||||
| **Qwen Code** | `~/.qwen/settings.json` |
|
||||
| **Kiro** | `.kiro/settings/mcp.json` |
|
||||
| **Qoder** | `.qoder/mcp.json` |
|
||||
| **GitHub Copilot** | `.vscode/mcp.json` |
|
||||
| **GitHub Copilot CLI** | `~/.copilot/mcp-config.json` |
|
||||
|
||||
The CodeBuddy project layout follows its official documentation for
|
||||
[MCP configuration](https://www.codebuddy.ai/docs/cli/mcp),
|
||||
[skills](https://www.codebuddy.ai/docs/cli/skills), and
|
||||
[hooks](https://www.codebuddy.ai/docs/cli/hooks). The shared `.mcp.json` is
|
||||
merged with JSONC awareness, while hook commands resolve the repository at
|
||||
runtime so committed settings do not contain one developer's checkout path.
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### 1. Build the graph (first time only)
|
||||
```
|
||||
/code-review-graph:build-graph
|
||||
```
|
||||
Parses your entire codebase. Takes ~10s for 500 files.
|
||||
|
||||
### 2. Review changes (daily use)
|
||||
```
|
||||
/code-review-graph:review-delta
|
||||
```
|
||||
Reviews only files changed since last commit plus the graph-derived impact radius. Relevant review and impact responses include compact estimated `context_savings` metadata. Across the 6 benchmark repositories, graph queries use ~65x fewer tokens per question (median; range 36x–376x) than reading the whole corpus — see the [README benchmarks](../README.md#benchmarks) and [REPRODUCING.md](REPRODUCING.md) for the methodology.
|
||||
|
||||
### 3. Review a PR
|
||||
```
|
||||
/code-review-graph:review-pr
|
||||
```
|
||||
Comprehensive structural review of a branch diff with blast-radius analysis.
|
||||
|
||||
### 4. Watch mode (optional)
|
||||
```bash
|
||||
code-review-graph watch
|
||||
```
|
||||
Auto-updates the graph on every file save. Zero manual work.
|
||||
|
||||
### 5. Visualize the graph (optional)
|
||||
```bash
|
||||
code-review-graph visualize
|
||||
open .code-review-graph/graph.html
|
||||
```
|
||||
Interactive D3.js force-directed graph. Starts collapsed (File nodes only) — click a file to expand its children. Use the search bar to filter, and click legend edge types to toggle visibility.
|
||||
|
||||
### 6. Semantic search (optional)
|
||||
```bash
|
||||
pip install "code-review-graph[embeddings]"
|
||||
```
|
||||
Then use `embed_graph_tool` to compute vectors. `semantic_search_nodes_tool` automatically uses vector similarity when matching embeddings are available and falls back to keyword/FTS search otherwise.
|
||||
|
||||
Embedding providers are local sentence-transformers, OpenAI-compatible endpoints, Google Gemini, MiniMax, and Voyage. Local embeddings use `CRG_EMBEDDING_MODEL`; OpenAI-compatible providers use `CRG_OPENAI_BASE_URL`, `CRG_OPENAI_API_KEY`, and `CRG_OPENAI_MODEL`; Voyage uses `VOYAGE_API_KEY` and optionally `CRG_VOYAGE_MODEL`. Cloud providers are opt-in and print an egress warning unless `CRG_ACCEPT_CLOUD_EMBEDDINGS=1` is set.
|
||||
|
||||
Function/class documentation summaries are included in embedding text. For a
|
||||
graph created by an older release, run a full build once before re-embedding so
|
||||
all files gain that metadata. Embedding refresh after build/update/watch is
|
||||
always default-off; opt in with an exact provider and model, for example:
|
||||
|
||||
```bash
|
||||
code-review-graph build \
|
||||
--embedding-provider local \
|
||||
--embedding-model all-MiniLM-L6-v2
|
||||
```
|
||||
|
||||
The same two options work with `update`, `postprocess`, and `watch`. They must be
|
||||
provided together. A refresh only updates a previously embedded graph, refuses
|
||||
to migrate vectors to a different provider/model/endpoint, purges deleted-node
|
||||
vectors, and degrades provider or transport failures to graph-build warnings.
|
||||
|
||||
### 7. Detect changes with risk scoring (v2)
|
||||
```
|
||||
Ask your MCP client: "Review my recent changes with risk scoring"
|
||||
```
|
||||
Uses `detect_changes_tool` to map diffs to affected functions, flows, communities, and test gaps.
|
||||
|
||||
### 8. Explore architecture (v2)
|
||||
```
|
||||
Ask your MCP client: "Show me the architecture of this project"
|
||||
```
|
||||
Uses `get_architecture_overview_tool` for community-based architecture map with coupling warnings.
|
||||
|
||||
### 9. Generate wiki (v2)
|
||||
```bash
|
||||
code-review-graph wiki
|
||||
```
|
||||
Creates markdown wiki pages for each detected community in `.code-review-graph/wiki/`.
|
||||
|
||||
### 10. Multi-repo search (v2)
|
||||
```bash
|
||||
code-review-graph register /path/to/other/repo --alias mylib
|
||||
```
|
||||
Then use `cross_repo_search_tool` to search across all registered repositories.
|
||||
|
||||
## Context Savings
|
||||
|
||||
CRG reduces review context by sending graph-derived structural context instead of broad file dumps. The exact reduction depends on the repository and change shape. The evaluation runner reports the current benchmark data used in the README:
|
||||
|
||||
```bash
|
||||
code-review-graph eval --all
|
||||
```
|
||||
|
||||
Since v2.3.4, review and impact tools include compact `context_savings` metadata. In v2.3.5 the CLI surfaces this as a boxed `Token Savings` panel on both `detect-changes --brief` and `update --brief`, with a per-category breakdown (Functions / Tests / Risk / Other) that sums exactly to the graph response size. Add `--verify` to cross-check the displayed numbers against OpenAI's `cl100k_base` tokenizer (requires `pip install tiktoken`). All numbers are labelled estimated because they use a conservative approximation rather than model-specific tokenisation; calibration shows the estimate stays within ~1% of real GPT-4 tokens in aggregate. Small single-file changes can occasionally use more context than the raw file because graph metadata has overhead.
|
||||
|
||||
## Supported Languages
|
||||
|
||||
The parser currently covers Python, JavaScript, TypeScript/TSX, Go, Rust, Java, C/C++, C#, VB.NET, Ruby, Kotlin, Swift, PHP, Scala, Solidity, Dart, R, Perl, Lua/Luau, Objective-C, shell scripts, Elixir, Zig, PowerShell, Julia, ReScript, GDScript, Nix, Verilog/SystemVerilog, SQL, Vue/Svelte single-file components, Astro files parsed through the TypeScript parser, Jupyter/Databricks notebooks (`.ipynb`), and Perl XS files (`.xs`).
|
||||
|
||||
Extension-less scripts are detected by shebang for common bash/sh/zsh/ksh/dash/ash, Python, Node, Ruby, Perl, Lua, Rscript, and PHP interpreters.
|
||||
|
||||
Languages not covered yet can be added without a fork via a `.code-review-graph/languages.toml` config — see [CUSTOM_LANGUAGES.md](CUSTOM_LANGUAGES.md).
|
||||
|
||||
## What Gets Indexed
|
||||
|
||||
- **Nodes**: Files, Classes, Functions/Methods, Types, Tests — plus Endpoints, Schedulers and ConfigProperties where framework enrichment applies
|
||||
- **Edges**: CALLS, IMPORTS_FROM, INHERITS, IMPLEMENTS, CONTAINS, TESTED_BY, DEPENDS_ON, REFERENCES — plus framework-specific kinds (INJECTS, HANDLES, TRIGGERS, PUBLISHES, CONSUMES/PRODUCES, DEPENDS_ON_CONFIG, TEMPORAL_STUB)
|
||||
|
||||
See [schema.md](schema.md) for full details.
|
||||
|
||||
## Ignore Patterns
|
||||
|
||||
By default, these paths are excluded from indexing:
|
||||
|
||||
```
|
||||
.code-review-graph/** node_modules/** .git/**
|
||||
__pycache__/** *.pyc .venv/**
|
||||
venv/** dist/** build/**
|
||||
.next/** target/** *.min.js
|
||||
*.min.css *.map *.lock
|
||||
package-lock.json yarn.lock *.db
|
||||
*.sqlite *.db-journal
|
||||
```
|
||||
|
||||
To add custom patterns, create a `.code-review-graphignore` file in your repo root (same syntax as `.gitignore`):
|
||||
|
||||
```
|
||||
generated/**
|
||||
vendor/**
|
||||
*.generated.ts
|
||||
```
|
||||
|
||||
In git repos, indexing is based on tracked files (`git ls-files`), so gitignored files are skipped automatically. Use `.code-review-graphignore` to exclude tracked files or when git isn't available.
|
||||
@@ -0,0 +1,128 @@
|
||||
# Architecture
|
||||
|
||||
## System Overview
|
||||
|
||||
`code-review-graph` is a local-first code intelligence graph exposed through a CLI and MCP server. It maintains a persistent, incrementally updated knowledge graph of a codebase so AI coding tools can review changes with structural context instead of reading broad file dumps. Claude Code is supported, but it is one client among several.
|
||||
|
||||
## Component Diagram
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ AI coding clients / CLI │
|
||||
│ │
|
||||
│ MCP clients Hooks / watch mode │
|
||||
│ ├── Codex └── incremental update │
|
||||
│ ├── Claude Code, CodeBuddy Code │
|
||||
│ ├── Cursor, Windsurf, Zed, Continue │
|
||||
│ └── Gemini CLI, Qwen, Qoder, Copilot, OpenCode │
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌────────────────────────────────────────────┐ │
|
||||
│ │ MCP Server (stdio or localhost HTTP) │ │
|
||||
│ │ │ │
|
||||
│ │ 31 MCP Tools + 7 MCP Prompts │ │
|
||||
│ │ ├── Core: build, impact, query, review, │ │
|
||||
│ │ │ search, traverse, embed, stats, docs │ │
|
||||
│ │ ├── Flows: list, get, affected │ │
|
||||
│ │ ├── Communities: list, get, architecture │ │
|
||||
│ │ ├── Analysis: detect_changes, refactor, │ │
|
||||
│ │ │ apply_refactor, hotspots, gaps │ │
|
||||
│ │ ├── Scoring: score_review, dedupe_ │ │
|
||||
│ │ │ findings, generate_report │ │
|
||||
│ │ ├── Wiki: generate, get_page │ │
|
||||
│ │ └── Multi-repo: list_repos, cross_search │ │
|
||||
│ └────────────────┬───────────────────────────┘ │
|
||||
└───────────────────┼──────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────┼───────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────┐ ┌─────────┐ ┌─────────────┐
|
||||
│ Parser │ │ Graph │ │ Incremental │
|
||||
│ │ │ Store │ │ Engine │
|
||||
└────┬────┘ └────┬────┘ └──────┬──────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
Tree-sitter SQLite DB git/svn diff
|
||||
grammars (.code-review- subprocess
|
||||
graph/
|
||||
graph.db)
|
||||
|
||||
Scoring module (code_review_graph/scoring.py) sits on top of the
|
||||
GraphStore and reads changed files + git history to compute the
|
||||
objective review metrics consumed by the unified-review skill and the
|
||||
score_review / dedupe_findings / generate_report MCP tools.
|
||||
```
|
||||
|
||||
## Data Flow
|
||||
|
||||
### Full Build
|
||||
1. `collect_all_files()` gathers tracked files (`git ls-files`) and applies `.code-review-graphignore` (gitignored files are skipped automatically when git is available)
|
||||
2. For each file, `CodeParser.parse_file()` uses Tree-sitter to extract AST
|
||||
3. AST walker identifies structural nodes (classes, functions, imports) and edges (calls, inheritance)
|
||||
4. `GraphStore.store_file_nodes_edges()` persists to SQLite with file hash for change detection
|
||||
5. Metadata updated with timestamp
|
||||
|
||||
### Incremental Update
|
||||
1. `get_changed_files()` uses VCS metadata to identify changed files (git diff by default, with SVN support in the incremental layer)
|
||||
2. `find_dependents()` queries the graph for files importing the changed files
|
||||
3. Changed + dependent files are re-parsed (others skipped via hash comparison)
|
||||
4. Only affected rows in SQLite are updated
|
||||
|
||||
### Review Context Generation
|
||||
1. Changed files identified (git diff or explicit list)
|
||||
2. `get_impact_radius()` walks outward from the changed nodes, relaxing a weighted
|
||||
best-path score per node (per-edge-kind weight × depth decay) rather than a plain BFS
|
||||
3. Source snippets extracted for changed areas only
|
||||
4. Review guidance generated (test coverage gaps, wide blast radius warnings)
|
||||
5. Assembled into a structured, token-efficient context for MCP clients and the CLI
|
||||
6. Where a cheap baseline can be estimated, compact `context_savings` metadata is attached as an estimate rather than an exact tokenisation
|
||||
|
||||
## Storage
|
||||
|
||||
### SQLite Schema
|
||||
- **nodes** table: id, kind, name, qualified_name, file_path, line_start/end, language, community_id, etc.
|
||||
- **edges** table: id, kind, source_qualified, target_qualified, file_path, line
|
||||
- **metadata** table: key-value pairs (last_updated, build_type, schema_version)
|
||||
- **flows** table: id, name, entry_point_id, depth, node_count, file_count, criticality, path_json
|
||||
- **flow_memberships** table: flow_id, node_id, position
|
||||
- **communities** table: id, name, level, parent_id, cohesion, size, dominant_language, description
|
||||
- **nodes_fts** (FTS5 virtual table): full-text search on name, qualified_name, file_path, signature
|
||||
- **community_summaries**, **flow_snapshots**, **risk_index** tables: compact precomputed summaries for token-efficient queries
|
||||
- **embeddings** table (separate DB): qualified_name, vector, text_hash, provider
|
||||
|
||||
Indexes on qualified_name, file_path, edge source/target, criticality, community_id, and cohesion for fast lookups.
|
||||
|
||||
WAL mode enabled for concurrent read access during updates.
|
||||
|
||||
### Qualified Names
|
||||
Nodes are uniquely identified by qualified names:
|
||||
- Files: absolute path (e.g., `/repo/src/auth.py`)
|
||||
- Functions: `file_path::function_name` (e.g., `/repo/src/auth.py::authenticate`)
|
||||
- Methods: `file_path::ClassName.method_name` (e.g., `/repo/src/auth.py::AuthService.login`)
|
||||
|
||||
## Parsing Strategy
|
||||
|
||||
Tree-sitter provides language-agnostic AST access. The parser:
|
||||
1. Walks the AST recursively
|
||||
2. Pattern-matches on node types (language-specific mappings in `_CLASS_TYPES`, `_FUNCTION_TYPES`, etc.)
|
||||
3. Extracts names, parameters, return types, base classes
|
||||
4. Identifies calls within function bodies
|
||||
5. Resolves imports to module paths
|
||||
|
||||
This approach is more robust than tree-sitter queries across grammar versions.
|
||||
|
||||
## Visualization
|
||||
|
||||
The `visualization.py` module generates an interactive D3.js force-directed graph as a self-contained HTML file. It reads all nodes and edges from the SQLite graph store and renders them in the browser, allowing developers to visually explore code relationships, filter by node kind, and inspect dependencies.
|
||||
|
||||
## Impact Analysis Algorithm
|
||||
|
||||
BFS from seed nodes (changed files' contents):
|
||||
1. Seed = all qualified names in changed files
|
||||
2. For each node in frontier:
|
||||
- Follow forward edges (what this node affects)
|
||||
- Follow reverse edges (what depends on this node)
|
||||
3. Expand up to `max_depth` hops (default: 2)
|
||||
4. Collect all reached nodes as "impacted"
|
||||
|
||||
This captures both downstream effects (things that call changed code) and upstream context (things that the changed code depends on).
|
||||
@@ -0,0 +1,299 @@
|
||||
# Knowledge Graph Schema
|
||||
|
||||
## Node Types
|
||||
|
||||
### File
|
||||
Represents a source code file.
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| name | string | Absolute file path |
|
||||
| file_path | string | Same as name for File nodes |
|
||||
| language | string | Detected language (python, typescript, go, etc.) |
|
||||
| line_start | int | Always 1 |
|
||||
| line_end | int | Total line count |
|
||||
| file_hash | string | SHA-256 of file contents (for change detection) |
|
||||
|
||||
### Class
|
||||
Represents a class, struct, interface, enum, or module definition.
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| name | string | Class name |
|
||||
| file_path | string | File containing the class |
|
||||
| line_start | int | Definition start line |
|
||||
| line_end | int | Definition end line |
|
||||
| language | string | Source language |
|
||||
| parent_name | string? | Enclosing class (for nested classes) |
|
||||
| modifiers | string? | Access modifiers (public, abstract, etc.) |
|
||||
|
||||
### Function
|
||||
Represents a function, method, or constructor definition.
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| name | string | Function name |
|
||||
| file_path | string | File containing the function |
|
||||
| line_start | int | Definition start line |
|
||||
| line_end | int | Definition end line |
|
||||
| language | string | Source language |
|
||||
| parent_name | string? | Enclosing class (for methods) |
|
||||
| params | string? | Parameter list as source text |
|
||||
| return_type | string? | Return type annotation |
|
||||
| is_test | bool | Whether this is a test function |
|
||||
|
||||
### Test
|
||||
Same schema as Function, but `kind = "Test"` and `is_test = true`. Identified by:
|
||||
- Name starts with `test_` or `Test`
|
||||
- Name ends with `_test` or `_spec`
|
||||
- File matches test file patterns (`test_*.py`, `*.test.ts`, `*_test.go`, etc.)
|
||||
- Language-specific test markers where supported, such as common Rust test attributes
|
||||
|
||||
### Type
|
||||
Represents a type alias, interface, enum, struct-like type, or parser-specific type construct where the language exposes one.
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| name | string | Type name |
|
||||
| file_path | string | File containing the type |
|
||||
| line_start | int | Definition start line |
|
||||
| line_end | int | Definition end line |
|
||||
|
||||
### Endpoint
|
||||
A synthesised node representing a routed entry point, emitted by the Spring enrichment for request mappings. Linked to the method that services it by a `HANDLES` edge.
|
||||
|
||||
### Scheduler
|
||||
A synthesised node representing a scheduled invocation, emitted for `@Scheduled` methods. Linked to the method it fires by a `TRIGGERS` edge.
|
||||
|
||||
### ConfigProperty
|
||||
An externalised configuration key parsed out of Spring `application.properties` / `application.yml` files. Values are deliberately discarded — only the key is stored. Linked to the code that binds it by a `DEPENDS_ON_CONFIG` edge.
|
||||
|
||||
## Edge Types
|
||||
|
||||
### CALLS
|
||||
A function calls another function.
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| source | string | Qualified name of the caller |
|
||||
| target | string | Name of the called function (may be unqualified) |
|
||||
| file_path | string | File where the call occurs |
|
||||
| line | int | Line number of the call |
|
||||
|
||||
### IMPORTS_FROM
|
||||
A file imports from another module or file.
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| source | string | Importing file path |
|
||||
| target | string | Imported module/path |
|
||||
| file_path | string | Same as source |
|
||||
| line | int | Line number of the import |
|
||||
|
||||
### INHERITS
|
||||
A class extends/inherits from another class.
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| source | string | Child class qualified name |
|
||||
| target | string | Parent class name |
|
||||
| file_path | string | File containing the child class |
|
||||
|
||||
### IMPLEMENTS
|
||||
A class implements an interface (Java, C#, TypeScript, Go).
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| source | string | Implementing class |
|
||||
| target | string | Interface name |
|
||||
|
||||
### CONTAINS
|
||||
Structural containment: a file contains a class, a class contains a method.
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| source | string | Container (file path or class qualified name) |
|
||||
| target | string | Contained node qualified name |
|
||||
|
||||
### TESTED_BY
|
||||
A function is tested by a test function.
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| source | string | Function being tested |
|
||||
| target | string | Test function qualified name |
|
||||
|
||||
### DEPENDS_ON
|
||||
General dependency relationship (used for non-specific dependencies).
|
||||
|
||||
### REFERENCES
|
||||
A value-level reference to another symbol, often used for function-as-value patterns such as callback maps, arrays, or assignment.
|
||||
|
||||
### INJECTS
|
||||
A dependency-injection relationship, currently used by Java/Spring enrichment for injected fields and constructor parameters.
|
||||
|
||||
### CONSUMES / PRODUCES
|
||||
Data or event flow relationships emitted by specialised parsers when a source consumes or produces a named resource.
|
||||
|
||||
### TEMPORAL_STUB
|
||||
Temporal dependency placeholder emitted by specialised parsers when a time/order relationship is detected but cannot be resolved to a stronger edge type.
|
||||
|
||||
### DEPENDS_ON_CONFIG
|
||||
A binding from code to externalised configuration, emitted by the Spring enrichment for `@ConfigurationProperties` classes and the `ConfigProperty` nodes parsed out of `application.properties` / `application.yml`.
|
||||
|
||||
### HANDLES
|
||||
A handler relationship between a dispatch point and the method that services it — Spring request mappings binding an `Endpoint` node to its controller method, and `@EventListener` methods binding to the event they consume.
|
||||
|
||||
### TRIGGERS
|
||||
A scheduled invocation, emitted for `@Scheduled` methods to link the synthesised `Scheduler` node to the method it fires.
|
||||
|
||||
### PUBLISHES
|
||||
An event-publication relationship, emitted where code publishes a Spring application event.
|
||||
|
||||
> `OVERRIDES` appears in the impact-scoring tables (`constants.py`) but is not emitted by any parser today.
|
||||
|
||||
## Qualified Name Format
|
||||
|
||||
Nodes are uniquely identified by qualified names:
|
||||
|
||||
```
|
||||
# File node
|
||||
/absolute/path/to/file.py
|
||||
|
||||
# Top-level function
|
||||
/absolute/path/to/file.py::function_name
|
||||
|
||||
# Method in a class
|
||||
/absolute/path/to/file.py::ClassName.method_name
|
||||
|
||||
# Nested class method
|
||||
/absolute/path/to/file.py::OuterClass.InnerClass.method_name
|
||||
```
|
||||
|
||||
## SQLite Tables
|
||||
|
||||
```sql
|
||||
-- Nodes table
|
||||
CREATE TABLE nodes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
kind TEXT NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
qualified_name TEXT NOT NULL UNIQUE,
|
||||
file_path TEXT NOT NULL,
|
||||
line_start INTEGER,
|
||||
line_end INTEGER,
|
||||
language TEXT,
|
||||
parent_name TEXT,
|
||||
params TEXT,
|
||||
return_type TEXT,
|
||||
modifiers TEXT,
|
||||
is_test INTEGER DEFAULT 0,
|
||||
file_hash TEXT,
|
||||
extra TEXT DEFAULT '{}',
|
||||
community_id INTEGER,
|
||||
updated_at REAL NOT NULL
|
||||
);
|
||||
|
||||
-- Edges table
|
||||
CREATE TABLE edges (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
kind TEXT NOT NULL,
|
||||
source_qualified TEXT NOT NULL,
|
||||
target_qualified TEXT NOT NULL,
|
||||
file_path TEXT NOT NULL,
|
||||
line INTEGER DEFAULT 0,
|
||||
extra TEXT DEFAULT '{}',
|
||||
confidence REAL DEFAULT 1.0,
|
||||
confidence_tier TEXT DEFAULT 'EXTRACTED',
|
||||
updated_at REAL NOT NULL
|
||||
);
|
||||
|
||||
-- Metadata table
|
||||
CREATE TABLE metadata (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- Flows table (v2.0)
|
||||
CREATE TABLE flows (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
entry_point_id INTEGER NOT NULL,
|
||||
depth INTEGER NOT NULL,
|
||||
node_count INTEGER NOT NULL,
|
||||
file_count INTEGER NOT NULL,
|
||||
criticality REAL NOT NULL DEFAULT 0.0,
|
||||
path_json TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Flow memberships table (v2.0)
|
||||
CREATE TABLE flow_memberships (
|
||||
flow_id INTEGER NOT NULL,
|
||||
node_id INTEGER NOT NULL,
|
||||
position INTEGER NOT NULL,
|
||||
PRIMARY KEY (flow_id, node_id)
|
||||
);
|
||||
|
||||
-- Communities table (v2.0)
|
||||
CREATE TABLE communities (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
level INTEGER NOT NULL DEFAULT 0,
|
||||
parent_id INTEGER,
|
||||
cohesion REAL NOT NULL DEFAULT 0.0,
|
||||
size INTEGER NOT NULL DEFAULT 0,
|
||||
dominant_language TEXT,
|
||||
description TEXT,
|
||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
);
|
||||
|
||||
-- Full-text search virtual table (v2.0)
|
||||
CREATE VIRTUAL TABLE nodes_fts USING fts5(
|
||||
name, qualified_name, file_path, signature,
|
||||
content='nodes', content_rowid='rowid',
|
||||
tokenize='porter unicode61'
|
||||
);
|
||||
|
||||
-- Token-efficient summary tables (v6)
|
||||
CREATE TABLE community_summaries (
|
||||
community_id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
purpose TEXT DEFAULT '',
|
||||
key_symbols TEXT DEFAULT '[]',
|
||||
risk TEXT DEFAULT 'unknown',
|
||||
size INTEGER DEFAULT 0,
|
||||
dominant_language TEXT DEFAULT ''
|
||||
);
|
||||
|
||||
CREATE TABLE flow_snapshots (
|
||||
flow_id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
entry_point TEXT NOT NULL,
|
||||
critical_path TEXT DEFAULT '[]',
|
||||
criticality REAL DEFAULT 0.0,
|
||||
node_count INTEGER DEFAULT 0,
|
||||
file_count INTEGER DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE risk_index (
|
||||
node_id INTEGER PRIMARY KEY,
|
||||
qualified_name TEXT NOT NULL,
|
||||
risk_score REAL DEFAULT 0.0,
|
||||
caller_count INTEGER DEFAULT 0,
|
||||
test_coverage TEXT DEFAULT 'unknown',
|
||||
security_relevant INTEGER DEFAULT 0,
|
||||
last_computed TEXT DEFAULT ''
|
||||
);
|
||||
|
||||
-- Embeddings table, stored in the embeddings database
|
||||
CREATE TABLE embeddings (
|
||||
qualified_name TEXT PRIMARY KEY,
|
||||
vector BLOB NOT NULL,
|
||||
text_hash TEXT NOT NULL,
|
||||
provider TEXT NOT NULL DEFAULT 'unknown'
|
||||
);
|
||||
```
|
||||
|
||||
Indexes include qualified-name, file-path, node-kind, edge source/target/kind, community, flow criticality, risk score, compound edge lookup indexes, and the composite edge upsert index.
|
||||
Reference in New Issue
Block a user