How to Parse and Index a Code Repository with Code-Graph-RAG: A Complete Guide
Code-Graph-RAG transforms multi-language source trees into queryable knowledge graphs through a three-stage pipeline of AST extraction, graph construction, and incremental indexing backed by Memgraph.
The vitali87/code-graph-rag toolkit enables you to parse and index a code repository with Code-Graph-RAG by orchestrating Tree-sitter parsers, multi-pass graph construction, and persistent storage layers. This system processes everything from Python and Rust to C# and PHP, creating a unified knowledge graph that supports both natural language queries and programmatic exploration.
The Three-Stage Parsing Pipeline
Stage 1: Repository Scanning and AST Extraction
In codebase_rag/graph_updater.py, the run method (lines 14-30) drives the initial parsing phase. The system walks every supported file in your repository and generates abstract syntax trees using Tree-sitter parsers or the fallback ast-grep tier for languages without native Tree-sitter specifications.
The parsing front-ends include language-specific implementations such as libclang for C/C++ and Roslyn for C#. For languages like Ruby that lack Tree-sitter support, the system falls back to codebase_rag/parsers/ast_grep_tier.py, which provides pattern-based structural parsing.
Stage 2: Graph Construction and Multi-Pass Ingestion
The GraphUpdater builds the knowledge graph through three distinct passes, as implemented in lines 49-82 of graph_updater.py:
- Pass 1 identifies module-level structure across the codebase.
- Pass 2 parses individual files and emits definitions with structural edges.
- Pass 3 resolves function calls and higher-level relationships such as inheritance and data-flow.
Nodes (functions, classes, modules) and relationships (calls, imports) are streamed to Memgraph via the IngestorProtocol. The MemgraphIngestor wrapper in codebase_rag/services/graph_service.py handles batch insertion with configurable batch sizes.
Stage 3: Incremental Indexing and Persistence
For incremental builds, the system compares file hashes stored in .hash_cache.json against current file states, skipping unchanged files to avoid redundant processing. When performing an incremental update, the function registry is re-hydrated from the existing Memgraph graph rather than rebuilt from scratch (lines 34-44 of graph_updater.py).
After ingestion, the graph can be exported as JSON for offline analysis. The GraphLoader class in codebase_rag/graph_loader.py (lines 15-33) reads these exports and builds in-memory indexes including nodes by label and property look-ups.
Command-Line Interface for Repository Indexing
The CLI entry point in codebase_rag/cli.py provides direct access to the parsing pipeline.
Full Repository Build
To parse an entire repository from scratch, deleting any existing graph data:
cgr start --repo-path /path/to/my/project --update-graph --clean
This command triggers the start entry point (lines 50-58 of cli.py), which validates the repository path and invokes GraphUpdater.run(force=True).
Incremental Updates
To update only changed files, omit the --clean flag:
cgr start --repo-path /path/to/my/project --update-graph
The system checks file hashes and only re-parses modified files, reusing cached ASTs for unchanged components.
Exporting to JSON
To export the constructed graph for offline use:
cgr start --repo-path /path/to/my/project --update-graph -o graph.json
The -o/--output flag invokes export_graph_to_file (lines 22-31 of cli.py), writing the complete graph structure to the specified JSON file.
Programmatic Access with the Python SDK
Loading and Exploring Exported Graphs
After exporting, use the Python SDK to analyze the graph without a running database:
from codebase_rag.graph_loader import load_graph
graph = load_graph("graph.json")
summary = graph.summary()
print(f"Nodes: {summary.total_nodes}, Relationships: {summary.total_relationships}")
# List the first five functions
for fn in graph.find_nodes_by_label("Function")[:5]:
print(f"{fn.properties['name']} ({fn.node_id})")
The load_graph function creates a GraphLoader instance (lines 48-61 of graph_loader.py) that builds searchable indexes from the JSON export, enabling efficient node retrieval via find_nodes_by_label and relationship traversal via get_relationships_for_node.
Querying Live Memgraph Instances
For natural language queries against a live database:
from codebase_rag.sdk.cypher_generator import CypherGenerator
from codebase_rag.services.graph_service import MemgraphIngestor
from codebase_rag.config import settings
ingestor = MemgraphIngestor(
host=settings.MEMGRAPH_HOST,
port=settings.MEMGRAPH_PORT,
batch_size=5000,
username=settings.MEMGRAPH_USERNAME,
password=settings.MEMGRAPH_PASSWORD,
)
cypher = CypherGenerator().generate(
"Show all classes that contain 'User' in their name"
)
results = ingestor.fetch_all(cypher, {})
for row in results:
print(row["name"])
The CypherGenerator translates natural language prompts into Cypher queries, executed by the MemgraphIngestor against the indexed graph.
Summary
- Code-Graph-RAG uses a three-stage pipeline in
graph_updater.pyto parse and index a code repository with Code-Graph-RAG: AST extraction, multi-pass graph construction, and persistence. - Tree-sitter and ast-grep handle language parsing across Python, TypeScript, Rust, Go, Java, C/C++, C#, PHP, Lua, Dart, and Ruby via
parser_loader.pyandast_grep_tier.py. - Incremental indexing skips unchanged files using hash caches stored in
.hash_cache.jsonand re-hydrates the function registry from Memgraph during updates. - CLI commands (
cgr startwith--update-graphand optional--clean) trigger full or incremental builds, with JSON export support via-o. - Python SDK provides
GraphLoaderfor offline JSON analysis andCypherGeneratorfor natural language querying of live Memgraph instances.
Frequently Asked Questions
What programming languages does Code-Graph-RAG support?
Code-Graph-RAG supports Python, TypeScript, Rust, Go, Java, C/C++, C#, PHP, Lua, and Dart through Tree-sitter parsers loaded via parser_loader.py. For languages without Tree-sitter specifications, such as Ruby, the system falls back to the ast-grep tier implemented in codebase_rag/parsers/ast_grep_tier.py, which uses pattern-based structural parsing to extract code relationships.
How does incremental indexing avoid re-parsing unchanged files?
The system stores file hashes in .hash_cache.json and compares them against current file states before each run. When you omit the --clean flag from CLI commands, GraphUpdater.run checks these hashes and skips files that haven't changed, while re-hydrating the function registry from the existing Memgraph graph to maintain reference integrity without redundant AST generation.
Can I use Code-Graph-RAG without running Memgraph?
Yes. While the live querying features require a Memgraph instance, you can export the graph to JSON using the -o flag and analyze it offline. The GraphLoader class in codebase_rag/graph_loader.py reads these JSON exports and builds in-memory indexes, providing methods like find_nodes_by_label and get_relationships_for_node without database connectivity.
What is the difference between the three parsing passes?
Pass 1 identifies module-level structures across the entire codebase to establish the graph skeleton. Pass 2 parses individual source files to emit concrete definitions (functions, classes) and structural edges. Pass 3 resolves dynamic relationships such as function calls, inheritance chains, and data-flow dependencies that require cross-file analysis, completing the knowledge graph construction before ingestion into Memgraph.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →