C+
For LLMs

Navigate C+ by its code graph, not grep

If you are a model reading or editing a C+ codebase, your first instinct is probably to grep for a name. Resist it. C+ ships a resolved, typed code graph through its compiler, and querying that graph answers the questions you actually have, where text search only guesses.

Why grep is the wrong tool here

Text search is lossy on three questions you ask constantly:

Every one of those is a resolution question, and resolution is exactly what grep throws away. The C+ compiler already computes it on every build. C+ keeps it and makes it queryable instead of discarding it.

The graph is the compiler's own resolution, kept

cpc query answers by symbol and type, not by text. Because the answers are resolved, math::area and a local area are distinguished, and a method call binds to the concrete Type::method it dispatches to.

cpc query def     math::area          # resolved definition site(s)
cpc query refs    Point::translate    # every use site
cpc query callers process_frame       # who calls it
cpc query callees render              # what it calls
cpc query type-at src/main.cplus:42:10 # type of a param/field/local at a position
cpc query members Vec                  # fields + methods of a type
cpc query symbols src/main.cplus       # outline of a file

Every result is JSON with clickable file:line:col locations, the same shape diagnostics emit, so you act on a result without parsing prose.

One call for a function's whole neighborhood

When you are about to edit a function, ask for its context pack in a single call:

cpc query context sum_range
{
  "kind": "context",
  "target": {
    "id": "src.geo::Shape::area",
    "kind": "method",
    "name": "area",
    "location": { "file": "src/geo.cplus", "line": 12, "col": 8 },
    "signature": "fn area(self) -> f64",
    "is_pub": true
  },
  "callers": [
    { "id": "src.main::render", "kind": "function", "name": "render",
      "location": { "file": "src/main.cplus", "line": 4, "col": 1 }, "is_pub": false }
  ],
  "callees": [
    { "id": "src.geo::Shape::perimeter", "kind": "method", "name": "perimeter",
      "location": { "file": "src/geo.cplus", "line": 18, "col": 8 }, "is_pub": true }
  ],
  "type_refs": [
    { "symbol": "src.geo::Shape", "kind": "type",
      "location": { "file": "src/geo.cplus", "line": 12, "col": 12 }, "in_context": "src.geo::Shape::area" }
  ],
  "unresolved": 0
}

That one call gives you the signature, who calls it, what it calls, the types it touches, and how many calls inside it the graph could not resolve. That is the edit pack you would otherwise assemble from several grep passes and a guess at which area matched. The symbol IDs use source names (src.geo::Shape::area), never a monomorphized area__Shape, so an ID pastes straight back into source.

In an agent loop, use the resident server

cpc query runs each lookup as a one-shot subprocess. For an agent loop, run cpc mcp instead: it builds the graph once, keeps it warm, and exposes the queries as tools over stdio (newline-delimited JSON-RPC). Point your MCP client at cpc mcp and call the tools directly:

The same index backs cpc lsp, so the editor and you share one resolved view of the code. There is no second source of truth to keep in sync.

Know where the graph ends

The graph is honest about its own coverage. Call and reference answers carry an explicit unresolved count and a scope field, so you know exactly where resolution stops and a grep fallback is genuinely needed (for example, a symbol reached only through a raw function pointer). Use that signal: query the graph first, read the unresolved field, and fall back to text search only for what it tells you it could not resolve.

The rule

For C+ navigation, query the graph before you reach for grep. It resolves the names text search cannot, it returns machine-readable locations you can act on without parsing, and it tells you where its own coverage ends. Grep is the fallback, not the default.

For the full reasoning behind why C+ is shaped this way, see C+ for LLMs; for the rest of the command surface, see the tooling reference.


‹ Back to all guides