Managing Multiple Projects in a Shared Graph with cgr CLI
The cgr CLI uses project namespaces to isolate multiple codebases within a single graph database while preserving cross-project query capabilities and enabling incremental updates.
Code-Graph-RAG (cgr) is an open-source tool that builds language-agnostic code graphs for retrieval-augmented generation. The cgr command-line interface allows you to capture, store, and query source code from multiple projects within a single shared graph without conflicts. This guide explains how the project namespace system works and how to manage multi-project workflows using commands defined in codebase_rag/main.py.
How Project Namespaces Work in cgr
Every node and edge in a cgr graph carries a project attribute that identifies its originating codebase. When you run cgr capture, the CLI extracts this value from the --project flag and attaches it to all captured entities via decorators in codebase_rag/decorators.py.
The architecture separates concerns across three layers:
-
Capture Layer (
codebase_rag/capture.py) — Parses source files using Tree-Sitter and produces normalizedNodeandEdgeobjects. -
Graph-Update Layer (
codebase_rag/graph_updater.py) — Persists captured data to the backing database (Memgraph by default), merges duplicates based on signature + project, and ensures idempotent updates. -
Query Layer (
codebase_rag/graph_loader.pyand CLI commands incodebase_rag/main.py) — Loads graph data for read operations with optional project filtering.
This design means projects coexist peacefully: same-named functions from different codebases remain distinct because their project attributes differ, yet you can still query across all projects when needed.
Adding Projects to a Shared Graph
Capturing Your First Project
Initialize the graph with any codebase by specifying a unique project name:
cgr capture ./my-api --project my-api
The capture sub-command in codebase_rag/main.py forwards the path to codebase_rag/capture.py, which walks the directory, parses supported files, and yields entities. The GraphUpdater class in codebase_rag/graph_updater.py then writes these to the database with project: "my-api" labels.
Adding Subsequent Projects
Capture additional codebases using different --project values:
cgr capture ./shared-lib --project shared-lib
cgr capture ./legacy-service --project legacy-service
Each invocation creates or updates only the nodes and edges belonging to that project. Existing data from other projects remains untouched.
Incremental Updates for Existing Projects
Re-running capture on the same project updates changed entities without duplicating unchanged ones:
cgr capture ./my-api --project my-api
The deduplication logic in codebase_rag/graph_updater.py matches nodes by their composite key (symbol signature + project) and performs MERGE operations rather than blind inserts.
Querying Across and Within Projects
Project-Agnostic Queries (All Projects)
Omit project constraints to search the entire graph:
cgr query "MATCH (f:Function) RETURN f.name, f.project ORDER BY f.project"
This executes through the query handler in codebase_rag/main.py, which passes the Cypher statement to codebase_rag/graph_loader.py for execution against Memgraph.
Project-Specific Isolation
Filter results to a single project using Cypher WHERE clauses:
cgr query "MATCH (f:Function) WHERE f.project = 'my-api' RETURN f.name, f.file_path"
Or combine with other predicates:
cgr query "
MATCH (f:Function)
WHERE f.project = 'shared-lib' AND f.name CONTAINS 'validate'
RETURN f.name, f.docstring
"
Cross-Project Relationship Analysis
The project attribute on edges enables powerful cross-project queries. Find calls that span project boundaries:
cgr query "
MATCH (caller:Function)-[c:CALLS]->(callee:Function)
WHERE caller.project <> callee.project
RETURN caller.name, caller.project, callee.name, callee.project
"
Or locate which projects depend on shared-lib functions:
cgr query "
MATCH (consumer:Function)-[:CALLS]->(lib:Function {project: 'shared-lib'})
RETURN DISTINCT consumer.project AS dependent_project, count(*) AS call_count
ORDER BY call_count DESC
"
Core Implementation Files
Understanding these source files helps troubleshoot multi-project issues and extend cgr functionality:
-
codebase_rag/main.py— Defines CLI entry points includingcapture,query, andservesub-commands. Parses--projectarguments and orchestrates calls to capture and update layers. -
codebase_rag/graph_updater.py— ContainsGraphUpdaterclass withupdate_graph()method that handles node/edge persistence, project attribute injection, and merge-on-conflict logic. -
codebase_rag/graph_loader.py— ProvidesGraphLoaderclass for read-only graph access with helper methods for project-scoped queries. -
codebase_rag/capture.py— Implementscapture_directory()and language-specific capture functions that produce intermediate representations before graph persistence. -
codebase_rag/decorators.py— Supplies@with_projectdecorator that attaches project metadata to captured entities during the capture pipeline.
Best Practices for Multi-Project Graphs
-
Use consistent project naming — Choose stable identifiers (repository names or package names) since the project attribute becomes part of node identities. Avoid spaces and special characters that complicate Cypher queries.
-
Document project relationships — The cross-project query patterns above help visualize architecture. Consider scheduling periodic queries to detect unwanted coupling between systems.
-
Separate staging and production captures — Use project names like
my-api-stagingandmy-api-prodif you need to compare code graphs across environments. -
Monitor graph growth — Each project adds nodes and edges. Use
cgr query "MATCH (n) RETURN count(n)"periodically to track size and plan database capacity.
Summary
-
The
cgr capture --project <name>command isolates each codebase using a project namespace attribute on every node and edge. -
Multiple projects coexist in one shared graph without collision, enabling both isolated per-project queries and cross-project relationship analysis.
-
Incremental updates are automatic—re-capturing a project merges changes without duplicating existing data, handled by
codebase_rag/graph_updater.py. -
The
cgr querycommand accepts any Cypher, withWHERE f.project = '...'clauses providing fine-grained isolation when needed. -
Cross-project edges (CALLS, IMPORTS, etc.) retain source and target project attributes, making boundary-spanning analysis straightforward.
Frequently Asked Questions
How does cgr prevent name collisions between functions in different projects?
Every captured entity receives a project attribute via decorators in codebase_rag/decorators.py. The GraphUpdater in codebase_rag/graph_updater.py uses this attribute as part of the merge key, so my-api/utils.py:parse() and shared-lib/utils.py:parse() remain distinct nodes despite identical local names.
Can I remove a single project without affecting others?
As of the current implementation, cgr does not include a dedicated remove sub-command. You can delete project-specific nodes manually using Cypher: cgr query "MATCH (n {project: 'old-project'}) DETACH DELETE n". Full project lifecycle management may be added in future releases.
What happens if I capture the same directory with two different project names?
cgr treats these as separate projects—nodes and edges are duplicated with different project values. Query both with WHERE n.project IN ['name-a', 'name-b'] or deduplicate downstream. Avoid this pattern unless you intentionally need multiple views of identical code.
Does the project attribute affect query performance?
No significant impact. The project property is indexed by default in codebase_rag/graph_updater.py setup routines, making WHERE f.project = '...' filters efficient even in graphs containing millions of nodes across dozens of projects.
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 →