Skip to content

Code graph

Instead of reading files “blind”, Context Code builds a knowledge graph of your project: functions, classes, symbols, imports and the calls between them, across several languages (TypeScript/JavaScript, Python, Rust, C/C++, C# and others), plus the internal links in your Markdown documentation.

  • Code communities: modules that relate to each other, detected automatically — the project’s real structure, not the folder layout.
  • Central nodes (“god nodes”): pieces with too many dependencies, prime refactor candidates.
  • Unexpected connections: relationships that cross different communities or folders and don’t look related at first glance.

The graph does not come out of a text search: files are actually parsed (syntax tree, tree-sitter), so a name inside a string or a comment never invents a relationship.

  • TypeScript and JavaScript imports through the AST: import … from, export … from, require(…) and dynamic import(…). Resolving the target takes into account the aliases in tsconfig.json/jsconfig.json (compilerOptions.paths and baseUrl, following extends), the barrels (index.ts, index.js…) and the re-exports: if a.ts does export * from './b', the graph also draws the edge to b. An import { X } from "@/core" stops being an “external” dependency and points at the real file.
  • Inheritance: class A extends B and implements in TypeScript, class A(B) in Python, impl Trait for Type in Rust, and the base classes of C++ and C#. They show up as edges of their own — Inheritance and Implements — separate from calls.
  • C, C++ and C# through the AST too: their functions, classes, structs and methods are extracted with tree-sitter just like TS/JS, Rust and Python. Their #include and using are still resolved by pattern, and namespaces are ignored on purpose (they are not navigable pieces).

A TypeScript call never “jumps” to a Rust or a Python function: they are different languages and the graph does not make those edges up. But the two seams that matter most in a client/server app are detected and drawn.

Tauri commands. Every invoke("my_command") in the front end is joined to the fn my_command marked #[tauri::command] in Rust, with a Tauri invoke edge. And if the command does not exist — an invoke to a name nobody implements any more, in other words a button that does nothing — the graph creates a broken node and counts it separately in the statistics.

HTTP routes. Routes declared by Express, NestJS, FastAPI, Flask, ASP.NET or axum become route nodes (GET /api/x) with a Route → handler edge to the function that serves them.

All of it is summed up in the .context/grafo/GRAPH_REPORT.md report, in a Front↔back bridges section with two very concrete lists:

  • Invokes with no Rust command — each line is a button that does nothing, with the file it is called from.
  • Routes with no identified handler — declared routes no function could be attached to.

If the project has neither invokes nor routes, the section does not appear.

The graph is explored in a native 3D view: handy for grasping a large project’s architecture at a glance, seeing what connects to what and examining each node’s relationships.

The graph's 3D view: code communities, central nodes and their relationships.

Besides the usual types, the legend now has HTTP route among the nodes and four new relationships among the edges: Inheritance, Implements, Tauri invoke and Route → handler. Each entry toggles on and off with a click, and the second button isolates it (leaves only that type).

Keyboard shortcuts inside the view:

KeyWhat it does
1 … 6Toggle Markdown, Folder, Tag, Broken, Code and MD+Code
7Toggles the HTTP route nodes
0Show all the types again
Ctrl+K / ⌘KJumps to the search box (filter by name or path)

The agent does not read the graph’s JSON: it uses the GraphQuery tool, which returns text already summarised and trimmed to a token cap. On top of the long-standing queries (summary, neighbors, subgraph, path, tag, orphans), 3.6.9 brings four that answer exactly the questions you ask when touching someone else’s code:

QueryWhat it is for
exploreThe most useful one: in a single call it returns the symbol’s literal source code, who calls it, what it calls and its impact radius.
symbolSearches symbols by name or substring and lists file, line, kind and how many calls come in and out of each one.
callers / calleesWho calls a symbol, or what it calls, grouped by file. Takes a depth from 1 to 3.
impactWhat breaks if you touch it: how many nodes depend on it, across how many files, broken down by depth and with the list of affected files. depth from 1 to 4, 2 by default.

When a result does not fit, the agent knows: impact says so if the walk hit the node cap, and explore says so if it had to trim the source.

You need to know none of this to benefit from it. Just ask in plain language:

explain handle_livekit_join to me and what breaks if I change it

The agent settles both things with a single explore query — it brings back the whole function, who calls it, what it calls and the files that depend on it — instead of chaining half a dozen searches and whole-file reads.

A graph cannot grow forever, so there are caps: 20,000 symbols, 20,000 calls and 12,000 imports. What matters is how they are handed out: your project’s own code first, and only then the vendored folders — those ending in -master, the runtime* ones, third_party, external, include and the like — and within each group by depth and path.

The order is deterministic: two consecutive regenerations give the same graph. Before, on a repo with large vendored dependencies, they ate the cap and your own code never made it in.

The graph is updated when you open the Graph view, with the /grafo command in the chat or with the view’s Regenerate button. Opening the view paints the graph that was already there first and only reprocesses if some file changed since last time; the Regenerate button always rebuilds the whole thing.

The agent navigates the graph to pinpoint exactly the snippet of code it needs, instead of loading whole files into the model. Less context sent means fewer tokens — cheaper and faster answers, especially on large projects.