# How the Interactive Querying Mode Works in the code-graph-rag CLI

> Explore the interactive querying mode in the code-graph-rag CLI. Ask natural language questions about your codebase and get instant answers via a REPL loop.

- Repository: [Vitali Avagyan/code-graph-rag](https://github.com/vitali87/code-graph-rag)
- Tags: how-to-guide
- Published: 2026-08-16

---

**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`](https://github.com/vitali87/code-graph-rag/blob/main/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`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/cli.py) |
| **Initialize** | `main_async` sets up the orchestrator and graph connection | [`codebase_rag/main.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/main.py) |
| **Loop** | `_run_interactive_loop` runs the persistent REPL | [`codebase_rag/main.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/main.py) |
| **Execute** | Cypher queries run against Memgraph; results render via Rich | [`codebase_rag/services/graph_service.py`](https://github.com/vitali87/code-graph-rag/blob/main/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`](https://github.com/vitali87/code-graph-rag/blob/main/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`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/main.py) coordinates the setup phase:

```python

# 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`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/main.py). This private async function implements a classic read-eval-print loop with graph-backed evaluation:

### Loop Structure

```python
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.prompt`** provides cross-platform input handling with consistent styling
- **Graceful interruption**: `EOFError` and `KeyboardInterrupt` (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`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/services/graph_service.py). Results flow through:

1. **Graph traversal**: Node and relationship data retrieved from the knowledge graph
2. **Formatting layer**: `Rich` tables for terminal display, or JSON for programmatic consumption (controlled by `--output-format`)
3. **Streaming**: Partial results appear as they arrive when supported by the underlying model

---

## Practical Usage Examples

### Launch Persistent Interactive Session

```bash
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

```bash
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

```bash
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`](https://github.com/vitali87/code-graph-rag/blob/main/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 start` command** in [`codebase_rag/cli.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/cli.py) is the entry point for **interactive querying mode**, parsing `--ask-agent` and `--interactive-setup` flags
- **`main_async`** in [`codebase_rag/main.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/main.py) initializes the async runtime and routes to either the REPL or single-shot handler
- **`_run_interactive_loop`** implements the persistent query-evaluate-render cycle using `typer.prompt`, orchestrator-driven Cypher generation, and `MemgraphIngestor.fetch_all` execution
- **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`](https://github.com/vitali87/code-graph-rag/blob/main/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.