How the Interactive Querying Mode Works in the code-graph-rag CLI
The cgr CLI provides an interactive querying mode that lets you ask natural-language questions about a codebase and receive answers in real time through a persistent REPL loop.
This interactive mode powers the core user experience of code-graph-rag (vitali87/code-graph-rag), transforming natural language into Cypher queries executed against a Memgraph knowledge graph. Understanding how this mode works helps you optimize your workflow when exploring unfamiliar codebases or debugging complex dependencies.
Overview of the Interactive Flow
The interactive querying mode follows a four-stage pipeline controlled by the start command in codebase_rag/cli.py:
| Stage | Action | Source Location |
|---|---|---|
| Parse | cgr start processes --ask-agent and flags; routes to async entry point |
codebase_rag/cli.py |
| Initialize | main_async sets up the orchestrator and graph connection |
codebase_rag/main.py |
| Loop | _run_interactive_loop runs the persistent REPL |
codebase_rag/main.py |
| Execute | Cypher queries run against Memgraph; results render via Rich | codebase_rag/services/graph_service.py |
Each stage is implemented as an async function, ensuring the CLI remains responsive during LLM inference and graph queries.
Stage 1: CLI Entry Point and Argument Parsing
The start command in codebase_rag/cli.py defines the interactive querying interface. Key parameters include:
--ask-agent: Specifies the LLM that converts natural language to Cypher (or triggers the default REPL when omitted)--interactive-setup: Launches a TUI for selecting directories to index before querying--orchestrator: Configures the model that manages query planning
When --update-graph is absent and --ask-agent is present (or implied), the CLI invokes main_async rather than the batch indexing pipeline.
Stage 2: Asynchronous Initialization via main_async
The main_async function in codebase_rag/main.py coordinates the setup phase:
# Simplified flow based on source structure
async def main_async(
repo_path: str,
ask_agent: str | None,
orchestrator: str,
batch_size: int,
# ... additional kwargs
):
# 1. Load configuration via update_model_settings
# 2. Establish Memgraph connection
# 3. Delegate to interactive loop or single-shot handler
if ask_agent:
await _run_interactive_loop(...)
else:
await main_single_query(...)
This function resolves active projects, validates the target repository path, and prepares the Orchestrator model for Cypher generation.
Stage 3: The Interactive REPL in _run_interactive_loop
The core interactive querying mode lives in _run_interactive_loop within codebase_rag/main.py. This private async function implements a classic read-eval-print loop with graph-backed evaluation:
Loop Structure
async def _run_interactive_loop(
target_repo_path: str,
effective_batch_size: int,
active_projects: list[str],
ask_agent: str | None,
) -> None:
"""
Persistent REPL for natural-language codebase queries.
"""
# Initialize orchestrator and graph service
orchestrator = load_model(ask_agent)
graph = MemgraphIngestor.connect()
while True:
# 1. PROMPT: Capture user question
try:
question = typer.prompt("Ask about your codebase")
except (EOFError, KeyboardInterrupt):
print("\nExiting interactive mode.")
break
if not question.strip():
continue
# 2. GENERATE: Orchestrator produces Cypher
cypher_query = await orchestrator.to_cypher(question)
# 3. EXECUTE: Run against Memgraph
results = graph.fetch_all(cypher_query)
# 4. RENDER: Format as Rich table or JSON
render_results(results)
# 5. CONTINUE: Offer graceful exit
if not typer.confirm("Ask another question?"):
break
Key Design Decisions
typer.promptprovides cross-platform input handling with consistent styling- Graceful interruption:
EOFErrorandKeyboardInterrupt(Ctrl-C) exit cleanly without stack traces - Empty input handling: Blank lines restart the loop rather than submitting wasteful queries
- Confirmation prompts: The continuation check prevents accidental exits while maintaining discoverability
Stage 4: Query Execution and Result Rendering
The generated Cypher queries execute against Memgraph via the MemgraphIngestor.fetch_all method in codebase_rag/services/graph_service.py. Results flow through:
- Graph traversal: Node and relationship data retrieved from the knowledge graph
- Formatting layer:
Richtables for terminal display, or JSON for programmatic consumption (controlled by--output-format) - Streaming: Partial results appear as they arrive when supported by the underlying model
Practical Usage Examples
Launch Persistent Interactive Session
cgr start --repo-path . --interactive-setup
The --interactive-setup flag opens a TUI for selecting which directories to index, then drops into the REPL.
Single Question Without REPL
cgr start --repo-path . --ask-agent "Which functions call process_data?"
Returns one answer and exits—ideal for shell scripts or CI pipelines.
JSON Output for Integration
cgr start --repo-path . \
--ask-agent "List all classes inheriting from BaseModel" \
--output-format json
Produces machine-parseable output instead of formatted tables.
Configuration and Performance Tuning
The interactive querying mode respects several configuration layers in codebase_rag/config.py:
| Parameter | Effect on Interactive Mode |
|---|---|
effective_batch_size |
Controls chunking of large result sets in _run_interactive_loop |
orchestrator model |
Determines Cypher generation quality and latency |
active_projects |
Scopes the graph namespace for multi-repo workspaces |
Adjust these via flags or environment variables to optimize for latency versus accuracy.
Error Handling and Edge Cases
The _run_interactive_loop function implements defensive patterns:
- Invalid Cypher generation: The orchestrator may produce malformed queries; the loop catches execution errors and prompts for rephrasing
- Empty result sets: Clearly distinguished from execution failures
- Graph disconnection: Reconnection logic before query execution
- Token limits: Large responses trigger pagination warnings
Summary
- The
cgr startcommand incodebase_rag/cli.pyis the entry point for interactive querying mode, parsing--ask-agentand--interactive-setupflags main_asyncincodebase_rag/main.pyinitializes the async runtime and routes to either the REPL or single-shot handler_run_interactive_loopimplements the persistent query-evaluate-render cycle usingtyper.prompt, orchestrator-driven Cypher generation, andMemgraphIngestor.fetch_allexecution- Output formatting defaults to Rich tables but supports JSON via
--output-format - Graceful exit via Ctrl-C or negative confirmation prevents data loss or orphaned processes
Frequently Asked Questions
How do I exit the interactive querying mode without killing the terminal?
Press Ctrl-C or answer "no" to the "Ask another question?" prompt. The _run_interactive_loop function catches KeyboardInterrupt and EOFError exceptions to exit cleanly without stack traces.
Can I use the interactive mode without indexing the graph first?
No. The interactive querying mode requires a populated Memgraph knowledge graph. Run cgr start --update-graph first, or use --interactive-setup which combines directory selection with initial indexing.
What happens if the orchestrator generates invalid Cypher?
The loop catches execution errors from MemgraphIngestor.fetch_all, displays a descriptive message, and returns to the prompt. Rephrase your question or check the --orchestrator model configuration if errors persist.
Is the interactive mode available in non-async contexts?
All entry points in codebase_rag/main.py use asyncio.run() wrappers internally. The CLI surface is synchronous, but main_async and _run_interactive_loop are implemented as async functions to support concurrent LLM streaming and graph queries.
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 →