The foundation

Why graph beats RAG

Most AI coding tools lean on vector search (Copilot, Cursor, Codeium). But source code is a deterministic graph, not unstructured text, so chunking and embedding it can lose the structure that matters most.

Dimension Vector RAG
(Copilot, Cursor, Codeium)
Travsr graph
(BFS → PPR → PCST)
Structural accuracy Approximate: cosine similarity tends to surface similar chunks, which are not always the related code Exact: edges are real call graph, type graph, and import graph edges
Hallucination risk Higher: the model can fill gaps from training data when chunks don't fully cover the call site Only real edges: the graph is built from relationships found in your code, not inferred
Staleness Scheduled: embeddings are often rebuilt on a batch schedule, so they can lag behind HEAD Incremental: a git post-commit hook reindexes changed files on every commit
Token cost Higher: top-k chunks often include extra context to compensate for imprecision Fewer tokens: PCST aims to return the smallest subgraph that answers the query
Cross-file reasoning Indirect: depends on whether related files land in the same top-k results Native: graph edges cross file and package boundaries by design
Symbol identity Limited: embeddings have no explicit notion of a symbol's identity Stable: Kythe VNames identify a symbol consistently across re-indexing, repos, and languages
Local-first Often not: many setups send code to an embedding API and store vectors in a cloud DB Yes: graph lives on your machine; cloud opt-in only

See the difference

One question, two answers

Agent asks “Who calls PaymentService.charge?”
Vector RAG top-k similar chunks
  • checkout.ts: a local charge() helper that just reads similar
  • billing.test.ts: a test whose text mentions “charge”
  • docs/payments.md: prose about charging cards

Similar text, not actual callers. The real call site in another package never made the top-k cut.

Travsr graph get_callers traversal
  • api/orders.tsOrderController.submit
  • jobs/retry.tsRetryWorker.run
  • billing/refund.tsRefund.reverse

Every real call site across every package, walked from ref/call edges. Nothing invented.

How the graph is built

01

Tree-sitter: structural AST

Every file is parsed into an AST without a compilation step. Tree-sitter grammars produce import edges, class/function declarations, and call expressions as graph nodes and edges.

02

LSIF & SCIP: semantic edges

Language indexers resolve precise type references, call targets, and data-flow edges that Tree-sitter alone can't see: rust-analyzer for Rust, an LSIF emitter for TypeScript/JavaScript, and scip-* indexers for Python, Go, Java, and more, added on demand with travsr lang install.

03

Kythe VNames: stable identity

Every node gets a globally unique Kythe VName, so the same symbol keeps a consistent identity across re-indexing, repositories, and languages. Cross-repo edges are first-class: your app can reference your library by VName.

04

BFS → PPR → PCST retrieval

Queries are answered by graph traversal, not vector similarity. BFS finds direct neighbours. Personalized PageRank scores importance. Prize-Collecting Steiner Tree extracts the minimum connected subgraph that fits within a token budget.

Twelve tools, not a prompt

The graph is exposed over the Model Context Protocol, so any MCP client (Claude, Copilot, Cursor) traverses it directly over stdio or SSE. No re-prompting, no guessing: the client reads the graph directly.

  • get_dependencies
  • get_callers
  • get_blast_radius
  • search_symbol
  • get_repo_map
  • get_execution_path
  • get_context
  • get_graph_stats
  • get_graph_json
  • get_snippets
  • get_lang_status
  • repo_languages
Full tool reference →

Always fresh, by construction

A git post-commit hook re-indexes only what changed via a SHA-256 delta. There's no nightly embedding job to drift behind HEAD, so the context an agent reads aims to stay in step with the code you just wrote.

git commit SHA-256 delta graph updated

Ready to try it?