CodeGraph
Brain app
First committed by colbymchenry
Published in colbymchenry/codegraph. Publisher page
77,372 GitHub stars
What it is
Supercharge AI coding agents with semantic code intelligence — surgical context, fewer tool calls, faster answers. 100% local.
AutoGineer overview, from the brain’s own files
CodeGraph is a local-first code intelligence platform that transforms how AI coding agents interact with large codebases. By parsing code into a structured knowledge graph using tree-sitter, it enables agents to perform precise, semantic queries rather than relying on inefficient file-crawling or grep-based searches. It stores symbol definitions, call edges, and dependencies in a local SQLite database, allowing agents to retrieve exact, relevant code snippets and blast-radius summaries in a single round-trip. Designed for seamless integration, it acts as an MCP server that connects to various coding agents, significantly reducing token usage and tool calls while improving the accuracy of agentic code navigation.
How it works
Memory model: Hybrid
AutoGineer overview, from the brain’s own files
CodeGraph operates as a local-first service that indexes a project by parsing source code into a graph of symbols and relationships. It uses tree-sitter for deterministic extraction and stores the resulting data in a local SQLite database. When an agent needs information, it queries this graph via an MCP server to retrieve specific code segments and call paths. The system includes an auto-sync feature that monitors file changes to keep the index updated without manual intervention.
| Folder | Role | Files |
|---|---|---|
| .claude | agent-config | 3 |
| .cursor | agent-config | 1 |
| .github | agent-config | 2 |
| __tests__ | tests | 1045 |
| assets | attachments | 40 |
| docs | docs | 76 |
| scripts | scripts | 75 |
| src | source-code | 359 |
| codegraph-kernel | folder | 59 |
| site | folder | 33 |
| telemetry-dashboard | folder | 22 |
| telemetry-worker | folder | 18 |
| ui | folder | 97 |
The agent reads
- docs/design/framework-coverage.md evidence: AGENTS.md
- AGENTS.md evidence: CLAUDE.md
- .gitignore evidence: README.md
The agent writes
- CLAUDE.md evidence: AGENTS.md
- docs/benchmarks/call-sequence-analysis.md evidence: AGENTS.md
- scripts/prepare-release.mjs evidence: AGENTS.md
- src/mcp/server-instructions.ts evidence: AGENTS.md
- AGENTS.md evidence: README.md
- .gitignore evidence: README.md
Requirements
Runtimes: Node.js >=20.0.0 <25.0.0
Databases: Redis, SQLite
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Google Antigravity, Kiro, OpenCode, Amp, Roo Code, Cline, JetBrains Junie, Devin (ex-Windsurf)
Set it up
This brain runs code automatically: 5 scripts. Review README.md, install.ps1, install.sh before opening the folder in Claude Code. No step below turns a hook on for you.
README.md:curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | shREADME.md:irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iexREADME.md:- **You're on an old (pre-0.9) install.** Reinstall to get the bundled runtime — `curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh` (macOS/Linux), `irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex` (Windows), or …[+42 chars]install.ps1:pwsh install.ps1install.sh:sh install.sh
- Not scanned by AutoGineerIts dependencies are installed by npm when you run it and were not scanned by AutoGineer (6 declared). https://www.npmjs.com/package/@colbymchenry/codegraph
First committed by colbymchenry. Published in colbymchenry/codegraph. Licence: MIT License.
Usage
AutoGineer overview, from the brain’s own files
- Install the CLI tool to manage project indexing and agent configuration.
- Initialize a project to build the local knowledge graph.
- Connect the MCP server to supported AI coding agents.
- Query the graph using natural language for structural code questions.
- “How does the authentication flow work in this project?”
- The agent uses codegraph_explore to retrieve the relevant call paths and source code for authentication-related symbols, providing a complete structural overview.
- “What is the blast radius of changing this function signature?”
- The agent queries the graph to identify all callers and dependent modules, returning a summary of affected files and logic.
Documentation by colbymchenry
<div align="center">
CodeGraph
Already installed? Run codegraph upgrade
Follow @getcodegraph on X for updates.
Supercharge Claude Code, Cursor, Codex, OpenCode, Hermes Agent, Gemini, Antigravity, Kiro, GitHub Copilot, and more with Semantic Code Intelligence
The fastest complete code graph · surgical context · built for how agents actually work · 100% local
<picture> <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/rust-logo-dark.svg?v=1"> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/rust-logo.svg?v=1" height="30" alt="Rust" align="center"> </picture> **Kernel powered by Rust**
Documentation & Website →
Image: npm version Image: License: MIT Image: Self-contained Image: npm provenance Image: Attested builds
Image: Windows Image: macOS Image: Linux
Image: Claude Code Image: Cursor Image: Codex Image: opencode Image: Hermes Agent Image: Gemini Image: Antigravity Image: Kiro Image: GitHub Copilot Image: IBM Bob Image: Factory Droid Image: Windsurf Image: Qwen Code Image: DeepSeek Harness Image: Grok Build Image: Kimi Code Image: Qoder Image: Pi
<br>
The CodeGraph platform is coming — for every PR, know exactly what to test, what could break, which flows are affected, and whether business logic is compromised.
<a href="https://getcodegraph.com"><img alt="Join the waitlist for early beta access" src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/waitlist.svg?v=2" height="52"></a>
<sub>Get <b>early beta access</b> to the hosted product · <a href="https://getcodegraph.com">getcodegraph.com</a></sub>
</div>
Contents
Get Started
Language Support
Why CodeGraph?
Key Features
Framework-aware Routes
Mixed iOS / React Native / Expo bridging
Quick Start
How It Works
CLI Reference
MCP Tools
Library Usage
Configuration
Telemetry
Verified releases
Supported Platforms
Supported Agents
Supported Languages
Measured cross-file coverage
Troubleshooting
Contributing
License
Get Started
1. Install the CLI
No Node.js required — one command grabs the right build for your OS:
# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh
# Windows (PowerShell)
irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex<details> <summary><b>Already have Node? Use npm instead (works on any version)</b></summary>
npm i -g @colbymchenry/codegraph<sub>CodeGraph bundles its own runtime — nothing to compile, no native build, works the same everywhere. The installer puts codegraph on your PATH but doesn't change your current shell — open a new terminal before the next step so the command resolves.</sub>
<sub>Upgrade any time with codegraph upgrade — it detects how you installed (bundle, npm, or npx) and updates in place. Add --check to see if an update is available, or codegraph upgrade <version> to pin one.</sub>
</details>
2. Wire up your agent(s)
In a new terminal, run the installer to connect CodeGraph to the agents you use:
codegraph install<sub>Detects and auto-configures Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, GitHub Copilot (VS Code, Copilot CLI, JetBrains IDEs), IBM Bob, Factory Droid, Windsurf, Qwen Code, DeepSeek Harness, Grok Build, Kimi Code, Qoder CLI, and Pi — wiring the CodeGraph MCP server into each. This is the step that connects CodeGraph to your agent; installing the CLI in step 1 does not do it on its own. It only wires up your agent — it does not index any code; building each project's graph is the separate codegraph init in step 3. (Shortcut: npx @colbymchenry/codegraph downloads and runs this in one go.)</sub>
3. Initialize each project
cd your-project
codegraph init<sub>codegraph init creates the local .codegraph/ directory and builds the full graph in the same step — one command, done.</sub>
<div align="center">
Image: 1_C_VYnhpys0UHrOuOgpgoyw
</div>
4. No more syncing!
Auto-sync is enabled by default. CodeGraph watches the project and updates the graph on every file change — while your agent edits code, or you add, modify, or delete files. The index is never stale, and there is nothing to re-run.
Uninstall
Changed your mind? One command removes CodeGraph from every agent it configured and the CLI itself — every install it finds (standalone bundle, npm global package, launcher link), shown to you before anything is deleted:
codegraph uninstallPass --keep-cli to remove only the agent configurations and keep the CLI installed.
<sub>Reverses the installer — strips CodeGraph's MCP server config, instructions, and permissions from each configured agent. Your project indexes (.codegraph/) are left untouched; remove those per-project with codegraph uninit. Use --target to remove from specific agents, or --yes to run non-interactively.</sub>
Language Support
Every language below gets the same treatment — full structural extraction and cross-file resolution into one graph, no per-language setup:
<p align="center"> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/typescript.svg?v=1" width="104" height="104" alt="TypeScript" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/javascript.svg?v=1" width="104" height="104" alt="JavaScript" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/arkts.svg?v=1" width="104" height="104" alt="ArkTS" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/python.svg?v=1" width="104" height="104" alt="Python" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/go.svg?v=1" width="104" height="104" alt="Go" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/rust.svg?v=1" width="104" height="104" alt="Rust" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/java.svg?v=1" width="104" height="104" alt="Java" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/csharp.svg?v=1" width="104" height="104" alt="C#" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/php.svg?v=1" width="104" height="104" alt="PHP" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/ruby.svg?v=1" width="104" height="104" alt="Ruby" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/c.svg?v=1" width="104" height="104" alt="C" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/cpp.svg?v=1" width="104" height="104" alt="C++" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/objective-c.svg?v=1" width="104" height="104" alt="Objective-C" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/metal.svg?v=1" width="104" height="104" alt="Metal" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/cuda.svg?v=1" width="104" height="104" alt="CUDA" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/swift.svg?v=1" width="104" height="104" alt="Swift" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/kotlin.svg?v=1" width="104" height="104" alt="Kotlin" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/scala.svg?v=1" width="104" height="104" alt="Scala" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/dart.svg?v=1" width="104" height="104" alt="Dart" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/svelte.svg?v=1" width="104" height="104" alt="Svelte" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/vue.svg?v=1" width="104" height="104" alt="Vue" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/astro.svg?v=1" width="104" height="104" alt="Astro" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/liquid.svg?v=1" width="104" height="104" alt="Liquid" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/delphi.svg?v=1" width="104" height="104" alt="Pascal / Delphi" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/lua.svg?v=1" width="104" height="104" alt="Lua" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/r.svg?v=1" width="104" height="104" alt="R" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/luau.svg?v=1" width="104" height="104" alt="Luau" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/cfml.svg?v=1" width="104" height="104" alt="CFML" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/cobol.svg?v=1" width="104" height="104" alt="COBOL" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/vbnet.svg?v=1" width="104" height="104" alt="Visual Basic .NET" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/erlang.svg?v=1" width="104" height="104" alt="Erlang" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/solidity.svg?v=1" width="104" height="104" alt="Solidity" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/terraform.svg?v=1" width="104" height="104" alt="Terraform / OpenTofu" /> <img src="https://raw.githubusercontent.com/colbymchenry/codegraph/main/assets/languages/nix.svg?v=1" width="104" height="104" alt="Nix" /> </p>
<sub>Per-language details — extensions, frameworks, and what exactly gets extracted — in Supported Languages.</sub>
Why CodeGraph?
When an AI agent needs to understand code — to answer a question or make a change — it discovers structure the slow way: grep, glob, and Read, one file at a time, rebuilding call paths and dependencies by hand. That's a pile of tool calls and round-trips before it even starts the real work.
CodeGraph hands the agent the exact code it needs in one call. It's a pre-built knowledge graph of every symbol, call edge, and dependency in your codebase — so instead of crawling files, the agent asks one question and gets back the relevant source, the call paths between those symbols (including dynamic-dispatch hops grep can't follow), and the blast radius of a change. Surgical context, not a file-by-file search — which means fewer tool calls and faster answers on every codebase, large or small.
<img width="1536" height="1024" alt="token-cost-savings-scale" src="https://github.com/user-attachments/assets/eb74a11a-a3ab-4b01-80a6-19f78352ae8e" />
A note on cost: CodeGraph's win on every codebase is precision — the agent stops crawling files and answers from the graph. On current models that precision is also a large direct saving: the 2026-08 re-measurement, on a harness that blocks the CLI in both arms, put it at 44% lower cost and 62% fewer tokens on average across the seven benchmark repos, because a strong model without the graph burns its budget re-deriving structure. Cost tracks how much discovery a question demands more than raw repo size: 57–78% on questions the file-reading agent needed 28–43 tool calls to answer, near-even where it got there in 7.
A note on context: the numbers above measure throughput — tokens processed, tools called, dollars spent to reach one answer. They don't measure what is still sitting in your context window afterward, and on that axis CodeGraph costs more, not less. Across the same seven repos in multi-turn sessions, CodeGraph's responses leave about 80% more retrieval context resident at the end of a session than a file-reading agent's do — on VS Code, 67k tokens against 18k. The mechanism is the same one that makes it fast: CodeGraph returns one dense, verbatim payload that answers the question and then stays in the window, where a grep-and-read agent churns through many small results that get evicted. Fewer tokens processed and a larger persistent footprint are both real at once. If you run long sessions in a small window, budget for it. Measured per-repo:
docs/benchmarks/residual-context-occupancy.md.
Benchmark Results
Tested across 7 real-world open-source codebases spanning 7 languages, comparing an agent (Claude Code, headless) answering one architecture question with and without CodeGraph, at the median of 4 runs per arm. Re-measured 2026-08-05 on Claude Opus 4.8 against the current build, on a harness that blocks the codegraph CLI in both arms — contamination row: 0 of 28 without-arm runs.
The universal win — every repo, every size: 88% fewer tool calls · 53% faster · 62% fewer tokens · 44% cheaper · file reads cut to zero on all seven repos.
With the index available, the agent answers from one to four codegraph_explore calls and stops. Without it, the agent burns its budget on discovery — up to 43 tool calls and 19 file reads re-deriving what the graph already knew. Every repo was faster with CodeGraph in this measurement — by 35% on the narrowest question, by 3.6× on the widest.
| Codebase | Language | Tool calls | Time | File reads | Tokens | Cost |
|---|---|---|---|---|---|---|
| VS Code | TypeScript · ~11k files | 2 vs 28 | 2.2× faster (58s vs 2m 10s) | 0 vs 12 | 77% fewer | 71% cheaper |
| Excalidraw | TypeScript · ~640 | 2 vs 43 | 3.6× faster (45s vs 2m 42s) | 0 vs 18 | 84% fewer | 78% cheaper |
| Django | Python · ~3k | 3 vs 14 | 35% faster (54s vs 1m 23s) | 0 vs 8.5 | 41% fewer | 13% cheaper¹ |
| Tokio | Rust · ~790 | 3 vs 29 | 2.6× faster (1m 3s vs 2m 43s) | 0 vs 19 | 65% fewer | 64% cheaper |
| OkHttp | Java · ~645 | 1 vs 6 | 43% faster (33s vs 58s) | 0 vs 2 | 54% fewer | 21% cheaper |
| Gin | Go · ~110 | 1 vs 7 | 39% faster (28s vs 46s) | 0 vs 4 | 52% fewer | ~even¹ |
| Alamofire | Swift · ~110 | 4 vs 33 | 2.6× faster (54s vs 2m 22s) | 0 vs 16.5 | 59% fewer | 57% cheaper |
<sub>¹ Cost tracks how much discovery the question demanded, which is why it varies far more than the other columns: 57–78% on repos where the file-reading arm needed 28–43 tool calls, but only 13% on Django and even on Gin, where it got there in 14 and 7. The with-arm still answered in 3 and 1 calls with zero file reads. File reads = median files opened — the surgical-context win in one column: the agent never reads a file on any of the seven repos when CodeGraph is present.</sub>
<details> <summary><strong>Per-repo breakdown — WITH vs WITHOUT (median of 4)</strong></summary>
| Codebase | Metric | WITH cg | WITHOUT cg |
|---|---|---|---|
| VS Code | Time / Tools / Tokens / Cost | 58s / 2 / 155k / $0.53 | 2m 10s / 28 / 670k / $1.80 |
| Excalidraw | Time / Tools / Tokens / Cost | 45s / 2 / 156k / $0.54 | 2m 42s / 43 / 991k / $2.43 |
| Django | Time / Tools / Tokens / Cost | 54s / 3 / 183k / $0.55 | 1m 23s / 14 / 309k / $0.63 |
| Tokio | Time / Tools / Tokens / Cost | 1m 3s / 3 / 201k / $0.66 | 2m 43s / 29 / 573k / $1.83 |
| OkHttp | Time / Tools / Tokens / Cost | 33s / 1 / 107k / $0.39 | 58s / 6 / 230k / $0.50 |
| Gin | Time / Tools / Tokens / Cost | 28s / 1 / 87k / $0.31 | 46s / 7 / 180k / $0.31 |
| Alamofire | Time / Tools / Tokens / Cost | 54s / 4 / 209k / $0.54 | 2m 22s / 33 / 505k / $1.27 |
</details>
<details> <summary><strong>Full benchmark details</strong></summary>
Methodology. Each arm is claude -p (Claude Opus 4.8, claude-opus-4-8) run headlessly against the repo with --strict-mcp-config: WITH = CodeGraph's MCP server enabled, WITHOUT = an empty MCP config. Built-in Read/Grep/Bash stay available to both. Same question per repo, 4 runs per arm, median reported. Cost = the run's total_cost_usd; Tokens = total tokens processed, summed per assistant turn (input incl. cache reads + cache creation + output); Time = wall-clock; Tool calls = every tool invocation, including those inside any sub-agents the model spawns. Repos cloned at --depth 1 and indexed by the same CodeGraph build that served them. Re-measured 2026-08-05 on the current build.
The codegraph CLI is blocked in both arms. A sanitized PATH plus a PreToolUse hook denies any Bash invocation of the CLI, in the WITHOUT arm as well as the WITH arm. This matters: without that block the control arm is not a control. On an unblocked harness we measured the WITHOUT agent finding the CLI on PATH and reaching CodeGraph through Bash in 26 of 28 runs — which distorts the comparison in both directions, since a CLI call is not counted as a tool call and its output still enters the window. Earlier published figures were produced without this block. In the run reported above, all 28 WITHOUT runs attempted the CLI and all 28 were blocked — 0 contaminated.
Queries:
| Codebase | Query |
|---|---|
| VS Code | "How does the extension host communicate with the main process?" |
| Excalidraw | "How does Excalidraw render and update canvas elements?" |
| Django | "How does Django's ORM build and execute a query from a QuerySet?" |
| Tokio | "How does tokio schedule and run async tasks on its runtime?" |
| OkHttp | "How does OkHttp process a request through its interceptor chain?" |
| Gin | "How does gin route requests through its middleware chain?" |
| Alamofire | "How does Alamofire build, send, and validate a request?" |
Why CodeGraph wins: with the index available, the agent answers directly — usually one codegraph_explore returns the relevant source — and stops, with zero file reads on every benchmark repo. Without it, the agent spends most of its budget on discovery (find/ls/grep) before reading the right code. CodeGraph only helps when queried directly, so its instructions steer agents to answer directly rather than delegate exploration to file-reading sub-agents — otherwise a sub-agent reads files regardless and CodeGraph becomes overhead.
</details>
Built for speed — the Rust kernel
CodeGraph's parsing engine is a native Rust kernel: 20 languages — TypeScript, JavaScript, Java, Python, Go, C, C++, Rust, C#, Ruby, PHP, Swift, Kotlin, Scala, Dart, R, Lua, Luau (Metal and CUDA ride the C++ path) — parse in compiled code with one boundary crossing per file. Every language shipped only after its graphs proved byte-for-byte identical to the reference engine on real repositories, from small libraries up to the Linux kernel; platforms without a prebuilt binary and files with syntax errors fall back per-file automatically, same graph either way.
And it scales itself to the machine it's on. Worker pools, parallel resolution, and analysis caches are sized from what the system actually has — real core counts (container/cgroup-aware, so a VPS that grants 2 cores gets sized for 2, not the host's 64), honestly-measured available RAM on macOS and Linux, and the measured cost of your project's resolution work:
On a workstation: the full parallel pipeline — native parse workers, a multi-worker resolver pool that engages the moment it pays for itself, memory-gated analysis caches. The Swift compiler repository (27k files of Swift and C++) fresh-indexes in about 100 seconds; a one-file edit re-syncs in ~4.
On a 2-core / 6GB VPS: the same graph, from a pipeline tuned to finish — the Linux kernel (70k files, 2M symbols, 6.4M relationships) indexes to completion in under 12 minutes where RAM-first designs run out of memory before reaching 1%.
Every day after day one: saving a file updates the graph in well under a second — the watcher fires 300ms after a lone save and syncs exactly what changed (~0.3s of work on a 4,400-file project, ~0.4s on the 27,000-file Swift compiler repo), never re-scanning the tree. Measured against the fastest competing indexer's re-index-on-change: 2–7× faster on medium and larger repos across a 31-repo, 30-language benchmark — and the gap widens with repo size, because their cost grows with the repository and ours grows with the change.
Key Features
| Native Rust Kernel | Parsing and extraction run in a compiled Rust engine for 20 languages — with graphs verified byte-for-byte identical to the reference engine, and automatic per-file fallback so nothing ever breaks |
| Adapts to Your Machine | Sizes its worker pools and caches from what the system actually has — real core counts (container-aware), honest available RAM, measured per-project cost. A workstation gets the full parallel pipeline; a 2-core VPS gets one tuned to finish reliably |
| Surgical Context | One tool call returns entry points, related symbols, and code snippets — no slow file-by-file exploration |
| Full-Text Search | Find code by name instantly across your entire codebase, powered by FTS5 |
| Impact Analysis | Trace callers, callees, and the full impact radius of any symbol before making changes |
| Always Fresh | File watcher uses native OS events (FSEvents/inotify/ReadDirectoryChangesW) with debounced auto-sync — the graph stays current as you code, zero config |
| 20+ Languages | TypeScript, JavaScript, ArkTS, Python, Go, Rust, Java, C#, VB.NET, PHP, Ruby, C, C++, CUDA, Objective-C, Metal, Swift, Kotlin, Scala, Dart, Lua, Luau, R, Nix, Erlang, CFML, COBOL, Solidity, Terraform/OpenTofu, Svelte, Vue, Astro, Liquid, Pascal/Delphi |
| Framework-aware Routes | Recognizes web-framework routing files and links URL patterns to their handlers across 17 frameworks |
| Mixed iOS / React Native / Expo | Closes cross-language flows that static parsing misses: Swift ↔ ObjC bridging, React Native legacy bridge + TurboModules + Fabric view components, native → JS event emitters, Expo Modules |
| Tauri | Follows a desktop app across its web frontend and Rust core: invoke('save_file'), tauri-specta's typed commands.saveFile(), your own wrappers around invoke, and events in both directions |
| 100% Local | No data leaves your machine. No API keys. No external services. SQLite database only |
<details> <summary><strong>How auto-syncing works — and why you don't need to run <code>codegraph sync</code> manually</strong></summary>
When your agent (Claude Code, Cursor, Codex, opencode) launches codegraph serve --mcp, three layers keep the index in step with your code — and make sure the agent never gets a silent wrong answer in the brief window between an edit and the next sync:
File watcher with debounced auto-sync. A native FSEvents / inotify / ReadDirectoryChangesW watcher captures every source-file create / modify / delete and triggers a re-index after a debounce window (default
2000ms, tunable viaCODEGRAPH_WATCH_DEBOUNCE_MS, clamped to[100ms, 60s]). Bursts of edits collapse into a single sync. On a large TypeScript or Vue repo nearly every edit also asks for a repo-wide rebuild of the synthesized callback and event edges;CODEGRAPH_SYNTH_MIN_INTERVAL_MS(1s to 24h) bounds how often that runs, and a rebuild a sync puts off runs when the interval is up. Until then those edges can be that far behind the code, and tool output doesn't say so.Per-file staleness banner. During the brief debounce window, MCP tool responses that would reference a still-pending file prepend a
⚠️banner naming it and telling the agent toReadit directly. Pending files NOT referenced by the response surface as a small footer instead. Either way, the agent gets an explicit signal — validated with Claude Code, where the agent literally says "Reading the file directly for the live content" before opening it.Connect-time catch-up. When the MCP server (re)connects, codegraph runs a fast
(size, mtime)+ content-hash reconciliation against the working tree before answering the first query — so edits made while no MCP server was running (agit pullfrom the terminal, edits from another editor, a previous agent session that exited) get absorbed on the next session's first tool call.
agent writes src/Widget.ts
→ watcher fires (<100ms)
→ debounce (default 2s)
→ sync; Widget.ts is in the index
→ next agent query sees itVerify any time with codegraph status (CLI). If anything is pending, you'll see a ### Pending sync: section naming the files and their edit age.
The handful of cases where manual codegraph sync makes sense: the watcher is disabled (sandboxed environments, or CODEGRAPH_NO_DAEMON=1), or you're scripting against the index outside an agent session and want a pre-flight sync at the start of your script.
→ Full deep-dive in Guides → Indexing a Project.
</details>
Framework-aware Routes
CodeGraph detects web-framework routing files and emits route nodes linked by references edges to their handler classes or functions. Querying callers of a view/controller now surfaces the URL pattern that binds it.
| Framework | Shapes recognized |
|---|---|
| Django | path(), re_path(), url(), include() in urls.py (CBV .as_view(), dotted paths) |
| Flask | @app.route('/path', methods=[...]), blueprint routes |
| FastAPI | @app.get(...), @router.post(...), all standard methods |
| Express | app.get(...), router.post(...) with middleware chains |
| NestJS | @Controller + @Get/@Post/... (with RouterModule prefixes, setGlobalPrefix and URI versioning), GraphQL @Resolver + @Query/@Mutation, @MessagePattern/@EventPattern, @SubscribeMessage |
| Laravel | Route::get(), Route::resource(), Controller@action, tuple syntax |
| Drupal | *.routing.yml routes (_controller, _form, entity handlers); hook_* implementations in .module/.theme/.install/.inc and #[Hook] methods; *.services.yml services, and calls through \Drupal::service(); plugin definitions, as attributes or annotations |
| CodeIgniter 3 | Every public controller method by convention, $route[...] in routes.php, HMVC modules; $this->load models, libraries and views |
| Rails | get '/x', to: 'users#index', hash-rocket => syntax |
| Spring | @GetMapping, @PostMapping, @RequestMapping on methods |
| Play | GET/POST/… verb routes in conf/routes → Controller.method actions (Scala + Java) |
| Gin / chi / gorilla / mux | r.GET(...), router.HandleFunc(...) |
| Axum / actix / Rocket | .route("/x", get(handler)) |
| ASP.NET | [HttpGet("/x")] attributes on action methods |
| Vapor | app.get("x", use: handler) |
| Astro | src/pages/ file-based routes (.astro pages + .ts endpoints, [param]/[...rest] syntax) |
Routers — routes and the navigation between them
These frameworks additionally emit navigates edges: the function that sends a user somewhere is linked to the screen it names, so "where does tapping this go" is one hop in the graph rather than a search. Each reads a literal destination — a computed one, or a path no route serves, is left unresolved rather than guessed — and a link written in markup is marked as inferred.
| Router | Routes from | Navigation from |
|---|---|---|
| Expo Router | Every screen file under app/ (app/item/[id].tsx → /item/[id], groups stripped), bound to its default-export component; +api files are endpoints (GET /hello) bound to their handlers | router.push / replace / navigate, template hrefs, { pathname } objects, and a helper's returned href |
| Next.js | App Router app/**/page.tsx and Pages Router pages ((group) stripped, [slug] → :slug); app/api/**/route.ts exports and pages/api/* are endpoints, not screens | router.push / replace / prefetch, redirect() / permanentRedirect() in a server action or page, NextResponse.redirect(new URL(…)) in middleware, <Link href> and internal <a href> |
| React Router | <Route path component/element> (v5 and v6) and createBrowserRouter([{ path, element }]) | history.push / replace, useNavigate's navigate, a loader's redirect, <Link to> / <NavLink to> / <Navigate to> / v5's <Redirect to> / react-router-bootstrap's <LinkContainer to>, and a styled(Link) wrapper |
| TanStack Router | createFileRoute('/posts/$postId') (file-based) and createRoute({ path, getParentRoute }) composed up its parent chain (code-based); _pathless segments, (group) folders, __root and <Outlet/> layouts are not addresses | navigate({ to }), a thrown redirect({ to }), <Link to> / <Navigate to> — where to is the route PATTERN and the values ride beside it in params |
| Vue Router / Nuxt | createRouter({ routes: [...] }) / new Router(...) and the route tables it's given (export const constantRoutes = [...], per-module route files), with the view each entry names — a lazy () => import(…) bound to its file — and children joined onto their parent's path, the parent being the layout around them; plus Nuxt pages/ file-based routes, server/api/ endpoints and route middleware | router.push / replace, this.$router.push, Nuxt's navigateTo, <router-link> / <RouterLink> / <NuxtLink> — by route name (push({ name: 'profile' })) as well as by path |
| SvelteKit | src/routes/**/+page.svelte ([slug] → :slug, [[opt]] → :opt?, [id=matcher] → :id, (group) folders stripped), joined to the +page.server.js beside it so a loader's guard belongs to its page | goto('/x'), redirect(status, '/x') from a load or form action, and the plain <a href> that is a link in a SvelteKit app |
| Angular | Routes arrays (RouterModule.forRoot / forChild, provideRouter, a routes file's default export) with component or a lazy loadComponent; children and lazy loadChildren (an NgModule's through its routing module) joined into full paths; paths written as route constants or $localize strings; a route with children is a layout around the screens inside it | router.navigate([...]), navigateByUrl, a guard's createUrlTree / parseUrl — a command array, a route constant, or a component property holding one — routerLink / [routerLink] in the component's template, and redirectTo. Each template's child components (<app-foo>) are linked to the component that renders them |
In a repository holding several apps, each app's routes are matched only against navigation written inside that app.
Mixed iOS / React Native / Expo bridging
Real iOS and React Native codebases live across multiple languages — a Swift caller invokes an Objective-C selector that's been auto-bridged, a JS file calls into a native module via the React Native bridge, a JSX component delegates to a native view manager. Static tree-sitter extraction stops at each language boundary. CodeGraph bridges them so codegraph_explore connects the flow end-to-end across the gap — call paths and blast radius cross the boundary instead of stopping at it.
| Boundary | JS / Swift side | Native side | How |
|---|---|---|---|
| Swift → ObjC | Swift obj.foo(bar:) | ObjC selector -fooWithBar: | @objc auto-bridging rules (including init/property/protocol forms) + Cocoa preposition prefixes (With/For/By/In/On/At/…) |
| ObjC → Swift | ObjC [obj fooWithBar:] | Swift @objc func foo(bar:) | Reverse-bridge name candidates; verifies @objc exposure from source |
| React Native legacy bridge | JS NativeModules.X.fn(...) | ObjC RCT_EXPORT_METHOD / RCT_REMAP_METHOD · Java/Kotlin @ReactMethod | Parses macro/annotation declarations to build a JS-name → native-method map |
| React Native TurboModules | JS import M from './NativeM'; M.fn(...) | Native impl matching the Codegen spec | Treats the Native<X>.ts spec interface as ground truth |
| RN native → JS events | JS new NativeEventEmitter(...).addListener('e', cb) | ObjC [self sendEventWithName:@"e" body:...] · Swift sendEvent(withName: "e", ...) · Java/Kotlin .emit("e", ...) | Synthesized cross-language event channel keyed by literal event name |
| Expo Modules | JS requireNativeModule('X').fn(...), directly or through a binding (export default requireNativeModule<T>('X')) | Swift / Kotlin Module { Name("X"); AsyncFunction("fn") { ... } } | Parses the Expo DSL literals into method nodes; a call on a binding resolves to module X's fn on both platforms, else to the method on the binding's declared type |
| Fabric view components | JSX <MyView prop={v}/> | TS Codegen spec + native impl class | Spec → component node; convention-based name+suffix lookup (View/ComponentView/Manager/ViewManager) bridges to native |
| Legacy Paper view managers | JSX <MyView prop={v}/>, through a requireNativeComponent('X') module | ObjC RCT_EXPORT_VIEW_PROPERTY · Java/Kotlin @ReactProp | Same as Fabric — requireNativeComponent('X') is a JS component node, and Paper-era declarations also produce component + property nodes |
Validated on real codebases (small + medium + large for each bridge):
| Bridge | Small | Medium | Large |
|---|---|---|---|
| Swift ↔ ObjC | Charts | realm-swift | Wikipedia-iOS |
| RN legacy bridge | AsyncStorage | react-native-svg | react-native-firebase |
| RN native → JS events | RNGeolocation | — | react-native-firebase |
| Expo Modules | expo-haptics | expo-camera | expo SDK sweep (7 packages) |
| Fabric / Paper views | react-native-segmented-control | react-native-screens | react-native-skia |
Every bridge hop says how it got into the graph. A hop matched by a bridge resolver carries metadata.resolvedBy: 'framework' and metadata.framework naming the resolver (swift-objc-bridge, react-native-bridge, expo-modules-js, fabric-view). A synthesized channel is tagged provenance:'heuristic' with metadata.synthesizedBy (rn-event-channel, fabric-native-impl).
Quick Start
1. Run the Installer
npx @colbymchenry/codegraphThe installer will:
Ask which agent(s) to configure — auto-detects installed ones from: Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, GitHub Copilot (VS Code, Copilot CLI, JetBrains IDEs), IBM Bob, Factory Droid, Windsurf, Qwen Code, DeepSeek Harness, Grok Build, Kimi Code, Qoder CLI, and Pi
Prompt to install
codegraphon your PATH (so agents can launch the MCP server)Ask whether configs apply to all your projects or just this one
Write each chosen agent's MCP server config, plus a small marker-fenced CodeGraph section in the agent's instructions file (
CLAUDE.md/AGENTS.md/GEMINI.md/QWEN.md) — that's how subagents and non-MCP agents learn thecodegraph explorecommand, since the MCP server's own guidance only reaches the main agent. Removed cleanly bycodegraph uninstall.Set up auto-allow permissions when Claude Code or Qwen Code is one of the targets
The installer wires up your agents only — it does not index your code. After it finishes, build each project's graph yourself with codegraph init (step 3). One global codegraph install covers every project; you run codegraph init once per project.
Non-interactive (scripting / CI):
codegraph install --yes # auto-detect agents, install global
codegraph install --yes --init # same, then build the current project's index (one-shot bootstrap)
codegraph install --target=cursor,claude --yes # explicit target list
codegraph install --target=auto --location=local # detected agents, project-local
codegraph install --target=copilot-vscode,copilot-cli,copilot-jetbrains --yes # GitHub Copilot everywhere
codegraph install --print-config codex # print snippet, no file writes
codegraph install --print-config copilot-vscode # same, for Copilot in VS Code| Flag | Values | Default |
|---|---|---|
--target | auto, all, none, or csv (claude,cursor,...) | prompt |
--location | global, local | prompt |
--yes | (boolean) | prompt every step |
--init | (boolean) run codegraph init in the current directory after wiring agents | — |
--no-permissions | (boolean) skip the auto-allow list (Claude Code, Qwen Code) | permissions on |
--print-config <id> | dump snippet for one agent and exit | — |
2. Restart Your Agent
Restart your agent (Claude Code / Cursor / Codex CLI / opencode / Hermes Agent / Gemini CLI / Antigravity IDE / Kiro / VS Code, the Copilot CLI, or your JetBrains IDE for GitHub Copilot / IBM Bob / Factory Droid / Windsurf / Qwen Code / DeepSeek Harness / Grok Build / Kimi Code / Qoder CLI / Pi) for the MCP server to load.
3. Initialize Projects
cd your-project
codegraph initBuilds the per-project knowledge graph index, which then auto-syncs on every file change. A single global codegraph install works in every project you open — no need to re-run the installer per project. Add --yes to skip every prompt (scripts / CI / container bootstraps).
That's it — your agent will use CodeGraph tools automatically when a .codegraph/ directory exists.
<details> <summary><strong>Manual Setup (Alternative)</strong></summary>
Install globally:
npm install -g @colbymchenry/codegraphAdd to ~/.claude.json:
{
"mcpServers": {
"codegraph": {
"type": "stdio",
"command": "codegraph",
"args": ["serve", "--mcp"],
"alwaysLoad": true
}
}
}alwaysLoad keeps codegraph_explore loaded from the first prompt. Claude Code otherwise defers every MCP tool behind a tool-search step, so a fresh session sees only the tool's name until the model searches for it.
Add to ~/.claude/settings.json (optional, for auto-allow):
{
"permissions": {
"allow": [
"mcp__codegraph__*"
]
}
}<sub>One wildcard auto-approves every CodeGraph tool — codegraph_explore is the only one listed by default, but if you re-enable others via CODEGRAPH_MCP_TOOLS they're already permitted, no prompt.</sub>
</details>
<details> <summary><strong>Agent Tool Guidance</strong></summary>
CodeGraph's MCP server delivers its usage guidance to your agent automatically, in the MCP initialize response. In short, it tells the agent to:
Answer structural questions directly with CodeGraph — it is the pre-built index, so a grep/read loop just repeats work it already did. Treat the returned source as already read.
Reach for
codegraph_explorefor almost anything — "how does X work", a flow/"how does X reach Y", or surveying an area. One call returns the relevant symbols' verbatim source grouped by file, the call paths between them (dynamic-dispatch hops included), and a blast-radius summary. Name a file or symbol in the query to read its current line-numbered source.Trust the results — don't re-verify with grep, and check the staleness banner after edits.
Works per project: query any project that has a
.codegraph/index by passingprojectPath— so a monorepo where only some services are indexed, or a second repo, works in one session. A path with no index returns clean guidance to use built-in tools; indexing stays your decision.
The exact text is src/mcp/server-instructions.ts — the single source of truth for the main agent. Because subagents and non-MCP harnesses never see the MCP guidance, the installer also writes a short marker-fenced section into the agent's instructions file pointing at the codegraph explore CLI equivalent.
</details>
How It Works
┌───────────────────────────────────────────────────────────────────┐
│ Claude Code │
│ │
│ "How does a request reach the database?" │
│ calls CodeGraph tools directly — no Explore sub-agent │
│ │ │
└─────────────────────────────────┬─────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ CodeGraph MCP Server │
│ │
│ explore · one call → verbatim source + call flow + blast radius │
│ │ │
│ ▼ │
│ SQLite knowledge graph │
│ symbols · edges · files · FTS5 full-text search │
└───────────────────────────────────────────────────────────────────┘Extraction — a native Rust kernel parses source with tree-sitter grammars compiled into it, extracting nodes (functions, classes, methods) and edges (calls, imports, extends, implements) for 20 languages; remaining languages and per-file fallbacks use the same extraction logic on the portable engine, producing identical graphs.
Storage — Everything goes into a local SQLite database (
.codegraph/codegraph.db) with FTS5 full-text search.Resolution — After extraction, references are resolved: function calls → definitions, imports → source files, class inheritance, and framework-specific patterns.
Auto-Sync — The MCP server watches your project using native OS file events. Changes are debounced (2-second quiet window), filtered to source files only, and incrementally synced. The graph stays fresh as you code — no configuration needed.
CLI Reference
codegraph # Run interactive installer
codegraph install # Run installer (explicit)
codegraph uninstall # Remove CodeGraph from your agents AND the CLI (--keep-cli for configs only)
codegraph init [path] # Initialize a project + build its graph (one step; --local-only keeps .codegraph/ out of git)
codegraph uninit [path] # Remove CodeGraph from a project (--force to skip prompt)
codegraph index [path] # Full index (--force to re-index, --quiet for less output)
codegraph sync [path] # Incremental update
codegraph status [path] # Show statistics (--json, --strict)
codegraph unlock [path] # Remove a stale lock file that's blocking indexing
codegraph query <search> # Search symbols (--kind, --limit, --json)
codegraph explore <query> # Relevant symbols' source + call paths in one shot (same output as the codegraph_explore MCP tool)
codegraph node <symbol|file> # One symbol's source + callers, or read a file with line numbers (same output as codegraph_node)
codegraph files [path] # Show file structure (--format, --filter, --max-depth, --tests, --json)
codegraph callers <symbol> # Find what calls a function/method (--limit, --json)
codegraph callees <symbol> # Find what a function/method calls (--limit, --json)
codegraph impact <symbol> # Analyze what code is affected by changing a symbol (--depth, --json)
codegraph implementations <symbol> # List the types that implement or extend a type (--depth, --json)
codegraph affected [files...] # Find test files affected by changes (see below)
codegraph cycles [path] # List circular imports between files (--json); exits 1 when there are any
codegraph daemon # Manage background daemons — pick one to stop (alias: daemons)
codegraph telemetry [on|off] # Show or change anonymous usage telemetry
codegraph upgrade [version] # Update to the latest release (--check, --force)
codegraph version # Print the installed version (also -v, --version)
codegraph help [command] # Show help, optionally for one commandcodegraph affected
Traces import dependencies transitively to find which test files are affected by changed source files.
codegraph affected src/utils.ts src/api.ts # Pass files as arguments
git diff --name-only | codegraph affected --stdin # Pipe from git diff
codegraph affected src/auth.ts --filter "e2e/*" # Custom test file pattern| Option | Description | Default |
|---|---|---|
--stdin | Read file list from stdin | false |
-d, --depth <n> | Max dependency traversal depth | 5 |
-f, --filter <glob> | Custom glob to identify test files | auto-detect |
-j, --json | Output as JSON | false |
-q, --quiet | Output file paths only | false |
CI/hook example:
#!/usr/bin/env bash
AFFECTED=$(git diff --name-only HEAD | codegraph affected --stdin --quiet)
if [ -n "$AFFECTED" ]; then
npx vitest run $AFFECTED
fiMCP Tools
When running as an MCP server, CodeGraph exposes a single tool — codegraph_explore. Measured agent behavior showed that one strong tool steers agents better than a menu of narrower ones — fewer mis-picks, and it saves context every session:
| Tool | Purpose |
|---|---|
codegraph_explore | Answer almost any question in one call — "how does X work", a flow ("how does X reach Y"), or surveying an area — returning the relevant symbols' verbatim source grouped by file, plus the call paths between them and a blast-radius summary. Surfaces dynamic-dispatch hops (callbacks, React re-render, interface→impl) grep can't follow. Name a file or symbol in the query to read its current line-numbered source, the same shape the Read tool gives you. |
The other tools (codegraph_node, codegraph_search, codegraph_callers, codegraph_callees, codegraph_impact, codegraph_files, codegraph_status) stay fully functional but unlisted by default — everything they return already arrives inline on codegraph_explore (its blast-radius section, the relationship map, a symbol's body as its callee list). Re-enable any of them for the MCP surface with the CODEGRAPH_MCP_TOOLS environment variable (e.g. CODEGRAPH_MCP_TOOLS=explore,node,search,callers), or use their CLI equivalents (codegraph node / query / callers / callees / impact / files / status).
Even when the server's own root has no .codegraph/ index, the tools stay available: pass projectPath to query any indexed project — a sub-service in a monorepo, or a second repo — in the same session. A path that has no index returns clean guidance to use built-in tools instead, so nothing fails loudly, and indexing stays your decision.
Library Usage
CodeGraph can be embedded directly. The npm package re-exports its programmatic
API, so both import and require resolve the CodeGraph class in your own
process — handy for embedding it in an app (e.g. an Electron main process).
import CodeGraph from '@colbymchenry/codegraph';
// CommonJS works too:
// const { CodeGraph } = require('@colbymchenry/codegraph');
const cg = await CodeGraph.init('/path/to/project');
// Or: const cg = await CodeGraph.open('/path/to/project');
await cg.indexAll({
onProgress: (p) => console.log(`${p.phase}: ${p.current}/${p.total}`)
});
const results = cg.searchNodes('UserService');
const callers = cg.getCallers(results[0].node.id);
const context = await cg.buildContext('fix login bug', { maxNodes: 20, includeCode: true, format: 'markdown' });
const impact = cg.getImpactRadius(results[0].node.id, 2);
cg.watch(); // auto-sync on file changes
cg.unwatch(); // stop watching
cg.close();Lower-level building blocks are exported from the same entry point for callers
that drive the graph directly: DatabaseConnection, QueryBuilder,
getDatabasePath, initGrammars / loadGrammarsForLanguages, and FileLock.
Embedding requirements
Install from npm (
npm i @colbymchenry/codegraph) so the matching per-platform package — which carries the compiled library and its dependencies — is fetched alongside the shim.The API runs on your runtime, so it needs Node 22.5+ for the built-in
node:sqlite(Electron qualifies when its bundled Node is 22.5+). The CLI and MCP server are unaffected — they run on the self-contained bundled runtime.TypeScript types ship with the package. As with any Node-targeting library, keep
@types/nodeavailable andskipLibCheck: true(the common default).
Configuration
Next to none — CodeGraph is zero-config by default, with nothing to write or keep in sync to get started. Language support is automatic from the file extension; there's nothing to wire up per language. The one optional file is for mapping custom file extensions.
What it skips out of the box:
Dependency, build, and cache directories —
node_modules,vendor,dist,build,target,.venv,Pods,.next, and the like across every supported stack — so the graph is your code, not third-party noise. This holds even with no.gitignore. Abuildfolder insidesrc/is the exception: no build step writes TypeScript, JSX, Vue, Svelte, Astro, Go, C# or Rust files into one, so those are indexed from it, and a Java, Kotlin or Scala package namedbuildis indexed whole.Anything in your
.gitignore— honored in git repos via git, and in non-git projects by reading.gitignoredirectly (root and nested).Files larger than 1 MB — generated bundles, minified JS, vendored blobs.
To keep something else out, add it to .gitignore. To pull a default-excluded
directory back in (say you really do want a vendored dependency indexed),
add a negation — !vendor/. The defaults apply uniformly, so committing a
dependency or build directory doesn't force it into the graph; the .gitignore
negation is the explicit opt-in.
.gitignore can't drop a directory you've committed, though. For a vendored
theme or SDK that's checked into the repo (e.g. a Metronic theme under
static/), list it under exclude in codegraph.json — gitignore-style
patterns, matched against repo-root-relative paths, honored on index, sync, and
watch:
{
"exclude": ["static/", "**/vendor/**"]
}Conversely, when real source is gitignored on purpose — a project under a second
VCS (SVN, Perforce) that .gitignores its own source so it stays out of Git —
force it back in with include (the opposite of exclude; includeIgnored
only revives embedded git repos, not plain source):
{
"include": ["Tools/", "Local/typescript/"]
}CodeGraph discovers those files off disk, overriding .gitignore, on index,
sync, and watch. An explicit exclude still wins, and built-in skips
(node_modules, dist, .git) are never re-included.
Sometimes a directory shouldn't leave the index — you still want to find things
in it — it just shouldn't outrank your real code. A scripts/ or
optional-skills/ tree whose helpers use generic names (usage, status,
run) can win on an exact name match and crowd out the product code that
actually answers the query. Name those trees under deprioritize:
{
"deprioritize": ["optional-skills/", "scripts/"]
}This is the ranking counterpart to exclude: those paths stay indexed and
findable — searching for them directly still works — they just stop winning
against first-party code. It applies to query / search and to explore's
ranking. It is not a filter: unlike the built-in example/, sample/,
fixture/, benchmark/ and demo/ handling — which also drops those files
from some result sets outright — deprioritize only ever changes rank. Reach
for exclude when you want something gone.
Custom file extensions
If your project uses a non-standard extension for a supported
language — say .dota_lua for Lua, or .tpl for PHP —
those files are skipped by default, because the extension isn't one CodeGraph
recognizes. Map them with an optional codegraph.json at your project root:
{
"extensions": {
".dota_lua": "lua",
".tpl": "php"
}
}Each value is a supported language id. The mappings merge on top of the built-in
defaults and win on conflict, so you can also re-point a built-in (e.g.
".h": "cpp"). Commit the file to share the mapping with your team. A typo'd
language or a malformed file is warned about and skipped — it never breaks
indexing — and a project with no codegraph.json behaves exactly as before.
Re-index (codegraph index) after adding or changing mappings.
Telemetry
CodeGraph collects anonymous usage statistics — which tools and commands get used, which languages get indexed — to guide where language and agent support work goes. Never any code, paths, file or symbol names, queries, or IP addresses; usage is aggregated locally into daily totals before anything is sent, and the ingest endpoint is public code in this repo that enforces the documented field list. The installer asks up front; turn it off any time:
codegraph telemetry off # or: CODEGRAPH_TELEMETRY=0, or DO_NOT_TRACK=1TELEMETRY.md lists every field, with the off-switches and the
full data-handling story.
Verified releases
Every artifact is built and published by the public Release workflow — never from a laptop — and carries cryptographic proof of it:
npm packages are published via trusted publishing (OIDC — no long-lived npm tokens exist that could be stolen) with provenance attestations linking every version to the exact commit and workflow run that built it. Verify what's installed:
npm audit signaturesGitHub Release bundles (and
SHA256SUMS) carry signed build attestations (SLSA v1.0 Build Level 2). Verify any downloaded bundle:gh attestation verify codegraph-darwin-arm64.tar.gz -R colbymchenry/codegraph
Releases published before July 2026 predate this pipeline and don't carry attestations.
Supported Platforms
Every release ships a self-contained build (bundled Node runtime — nothing to compile) for all three desktop OSes, on both Intel/AMD (x64) and ARM (arm64):
| Platform | Architectures | Install |
|---|---|---|
| Windows | x64, arm64 | PowerShell installer or npm |
| macOS | x64, arm64 | shell installer or npm |
| Linux | x64, arm64 | shell installer or npm |
See Get Started for the one-line install commands.
Supported Agents
The interactive installer auto-detects and configures each of these — wiring up
the MCP server, which delivers its own usage guidance. For Claude Code, Codex
CLI, Gemini CLI, opencode, Factory Droid, Qwen Code, DeepSeek Harness, Grok
Build, Kimi Code and Pi it also writes a short marker-fenced CodeGraph section
into the agent's instructions file (CLAUDE.md / AGENTS.md / GEMINI.md /
QWEN.md, or rules/codegraph.md for Grok Build), so subagents and non-MCP
agents learn the codegraph explore command:
Claude Code
Cursor
Codex CLI — the Codex IDE extension reads the same config: a global install writes
$CODEX_HOME/config.toml(default~/.codex/config.toml), and a local install writes.codex/config.toml, which Codex uses only in a project you've marked as trustedopencode — MCP entry is OpenCode 2's
mcp.servers.codegraphwithcodemode: false(keepscodegraph_exploreon the native tool list;codegraph installmigrates the oldermcp.codegraphshape)Hermes Agent
Gemini CLI
Antigravity IDE
Kiro
GitHub Copilot — Copilot Chat in VS Code (
copilot-vscode), the Copilot CLI (copilot-cli), and the Copilot plugin in JetBrains IDEs (copilot-jetbrains)IBM Bob (
bob) — Bob IDE and Bob Shell:~/.bob/settings/mcp.json, or.bob/mcp.jsonin a projectFactory Droid (
droid) —~/.factory/mcp.json, or.factory/mcp.json, which is meant to be committed; Droid reloads the file on its ownWindsurf (
windsurf) — global only:~/.codeium/windsurf/mcp_config.json, which Devin, Windsurf's new name, imports by defaultQwen Code (
qwen) —settings.jsonin$QWEN_HOME(default~/.qwen) or.qwen/, keeping any comments in itDeepSeek Harness (
dsh) — global only: one entry in$DSH_HOME/cordis.patch.yml(default~/.dsh) covers every profileGrok Build (
grok) —$GROK_HOME/config.toml(default~/.grok), or.grok/config.toml, which Grok uses only in a trusted folderKimi Code (
kimi) —$KIMI_CODE_HOME/mcp.json(default~/.kimi-code), or.kimi-code/mcp.json, which Kimi Code uses only in a trusted folderQoder CLI (
qoder) —$QODER_CONFIG_DIR/settings.json(default~/.qoder), or.qoder/settings.local.json; in the Qoder IDE, add the server under Settings > MCPPi (
pi) —mcp.jsonin Pi's agent directory (default~/.pi/agent), or.pi/mcp.json, which Pi reads once you trust the project;exposure: "direct"keepscodegraph_exploreon the model's tool list
Supported Languages
| Language | Extension | Status |
|---|---|---|
| TypeScript | .ts, .tsx | Full support |
| JavaScript | .js, .jsx, .mjs | Full support |
| ArkTS (HarmonyOS) | .ets | Full support (everything TypeScript has, plus @Component/@ComponentV2 structs with their ArkUI decorators (@State/@Prop/@Link/@Local/@Builder/…), build() view trees — parent→child component edges, chained-attribute links to @Extend/@Styles functions, .onClick(this.handler) event bindings — dynamic-dispatch bridges for state→build() re-renders, @ohos.events.emitter emit→subscriber pairs (static event keys only), and router.pushUrl literal urls → the target page struct; ohpm workspace modules resolve bare import { X } from "data" through oh-package.json5 file: dependencies, honoring each module's main entry) |
| Python | .py | Full support |
| Go | .go | Full support |
| Rust | .rs | Full support |
| Java | .java | Full support |
| C# | .cs | Full support |
| PHP | .php | Full support |
| Ruby | .rb | Full support |
| C | .c, .h | Full support |
| C++ | .cpp, .hpp, .cc | Full support |
| Objective-C | .m, .mm, .h | Partial support (classes, protocols, methods, @property, #import, message sends; .mm ObjC++ may parse incompletely) |
| Metal | .metal | Full support (vertex/fragment/kernel functions, structs, type aliases, call edges — MSL parses as C++, with [[attribute]] annotations handled) |
| CUDA | .cu, .cuh | Full support (kernels and device/host functions, structs, classes, host→kernel call edges through <<<grid, block>>> launch syntax — templated launches, function-pointer launches (auto kernel = &fn<...>), dim3{...} configs, and macro-defined kernels included; __global__/__device__/__launch_bounds__ specifiers handled; CUDA in plain .h/.hpp headers recognized by content) |
| Swift | .swift | Full support |
| Kotlin | .kt, .kts | Full support |
| Scala | .scala, .sc | Full support (classes, traits, methods, type aliases, Scala 3 enums) |
| Dart | .dart | Full support |
| Svelte | .svelte | Full support (script extraction, Svelte 5 runes, SvelteKit routes) |
| Vue | .vue | Full support (script + script-setup extraction, Nuxt page/API/middleware routes) |
| Astro | .astro | Full support (frontmatter + script extraction, template component/call references, src/pages/ routes) |
| Liquid | .liquid | Full support |
| Pascal / Delphi | .pas, .dpr, .dpk, .lpr | Full support (classes, records, interfaces, enums, DFM/FMX form files) |
| Lua | .lua | Full support (functions, methods with receivers, local variables, require imports, call edges) |
| R | .R .r | Full support (functions in every assignment form, S4/R5/R6 classes with methods, library/require imports, source() file references, call edges) |
| Luau | .luau | Full support (everything in Lua, plus type/export type aliases, typed signatures, and Roblox instance-path require) |
| CFML | .cfc, .cfm, .cfs | Full support (tag-based <cfcomponent>/<cffunction> and bare-script component { ... } styles, extends/implements, embedded <cfscript> delegation, call edges) |
| COBOL | .cbl, .cob, .cpy | Full support (programs, sections/paragraphs with PERFORM/GO TO call edges, CALL 'literal' cross-program calls, COPY copybook imports — including standalone .cpy files — DATA DIVISION records/fields/88-levels, EXEC CICS LINK/XCTL and EXEC SQL INCLUDE targets; fixed and free format) |
| Visual Basic .NET | .vb | Full support (classes, Modules, interfaces, structures, enums, properties, events, Declare P/Invoke, Handles/WithEvents, Inherits/Implements edges, call edges through VB's call/index paren ambiguity, As New instantiation, interpolated strings, LINQ, Unicode identifiers) |
| Erlang | .erl, .hrl, .escript, .app.src, .app | Full support (functions with multi-clause/multi-arity grouping, -spec signatures, records with fields, -type/-opaque aliases, -define macros, -include/-include_lib/-import edges, local and mod:fn remote call edges, fun name/arity references, spawn/apply/proc_lib/timer/rpc MFA-argument call edges, gen_server:call/cast(?MODULE) → own handle_call/handle_cast links, -behaviour links, -export-based visibility) |
| Solidity | .sol | Full support (contracts, libraries, interfaces, structs, enums, modifiers, events, errors, state variables, import/using directives, emit/revert calls) |
| Terraform / OpenTofu | .tf, .tfvars, .tofu | Full support (resources, data sources, modules, variables, outputs, providers incl. aliases, locals; var./local./module./resource references with Terraform's per-directory scoping enforced; module calls bridged across the boundary — inputs to the child module's variables, module.M.out to the child's output, source to the module's files; cloudposse/atmos remote-state cross-component wiring when the component is statically named; provider = aws.east selections resolved up the module tree; moved/import/removed/check block references; .tfvars assignments linked to the variables they set) |
| Nix | .nix | Full support (functions with simple/destructured/curried params, let/attrset bindings, inherit, import ./path file edges — ./dir resolving through default.nix — plus NixOS module imports = [ ./x.nix ] lists and callPackage ./pkg.nix file edges; call edges; module-system option wiring — a config write like launchd.user.agents.x = { ... } links to the module declaring options.launchd.user.agents, so option flows trace across modules) |
Measured cross-file coverage
Impact and blast-radius queries are only as good as the dependency graph behind them, so coverage is measured rather than asserted. Fair coverage = the share of symbol-bearing source files that have at least one resolved cross-file dependent — something that imports, calls, references, or (through a framework convention) routes to them — on a real-world benchmark repo per language. The residual is always a genuine static-analysis frontier (runtime dynamic dispatch, reflection / DI containers, framework-convention entry points, vendored third-party code), never hidden by gaming the denominator.
| Language | Benchmark repo | Coverage |
|---|---|---|
| TypeScript / JavaScript | this repo | 95.8% |
| Python | psf/requests | 100% |
| Go | gin-gonic/gin | 96.6% |
| Rust | BurntSushi/ripgrep | 86.7% |
| Java | google/gson | 93.3% |
| C# | jbogard/MediatR | 85.2% |
| PHP | guzzle/guzzle | 100% |
| Ruby | sidekiq/sidekiq | 100% |
| C | redis/redis | 92.2% |
| C++ | google/leveldb | 94.8% |
| Objective-C | SDWebImage | 91.6% |
| Swift | Alamofire | 95.3% |
| Kotlin | square/okhttp | 96.2% |
| Scala | gatling/gatling | 91.2% |
| Dart | flutter/packages | 92.4% |
| Svelte / SvelteKit | sveltejs/realworld | 100% |
| Vue / Nuxt | nuxt/movies | 93.5% |
| Astro | xingwangzhe/stalux | 93.0% |
| Lua | nvim-telescope/telescope.nvim | 84.2% |
| Luau | dphfox/Fusion | 92.2% |
| Liquid | Shopify/dawn | 73.8% |
| Pascal / Delphi | PascalCoin | 77.4% |
Framework routing is validated the same way, on a canonical app per framework: Express 100%, FastAPI 98%, Flask 100%, NestJS 96.8%, Gin 96.5%, Axum 100%, Rocket 93.8%, Vapor 100%, Laravel 92%, Rails 89.6%, React Router 100% — and the convention/reflection-heavy ones at their honest static-analysis ceiling: ASP.NET 83.9%, Spring 83.3%, Drupal 78.9%, Play 76.3%, Django 74.1%. SvelteKit, Vue/Nuxt, and Astro use file-based routing, so their page/endpoint coverage is the Svelte/SvelteKit (100%), Vue/Nuxt (93.5%), and Astro (93.0% — every src/pages/ file maps to a route node on the two validation repos) figures in the table above.
Troubleshooting
"CodeGraph not initialized" — Run codegraph init in your project directory first.
Indexing is slow — Check that node_modules and other large directories are excluded. Use --quiet to reduce output overhead.
MCP hits database is locked — current builds shouldn't: CodeGraph bundles its own Node runtime and uses Node's built-in node:sqlite in WAL mode, where concurrent reads never block on a writer. If you still see it:
You're on an old (pre-0.9) install. Reinstall to get the bundled runtime —
curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh(macOS/Linux),irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex(Windows), ornpm i -g @colbymchenry/codegraph@latest.codegraph statusshowsJournal:other thanwal— WAL couldn't be enabled on this filesystem (common on network shares and WSL2/mnt), so reads can block on writes. Move the project (with its.codegraph/folder) onto a local disk.
MCP server not connecting — Your agent starts the server itself, so you don't launch it by hand. Make sure the project is initialized and indexed (codegraph status) and that the path in your MCP config is correct. If it still won't connect, re-run codegraph install to rewrite the config.
Two codegraph serve --mcp on one project fight over the index / auto-sync stops — CodeGraph allows one live MCP writer per project (the shared background daemon, or a single direct-mode process). Extra clients should proxy to that daemon. If you set CODEGRAPH_NO_DAEMON=1, run only one serve --mcp for that project; a second instance exits with a clear writer-lock error (see writer.pid under .codegraph/). Prefer leaving the daemon enabled so multiple MCP hosts share one watcher.
MCP tool calls fail with Transport closed while codegraph status/sync are healthy — almost always WSL2 with the project on a Windows drive (a /mnt/c or /mnt/d path), where the local socket CodeGraph uses to share one background server across sessions is unreliable. CodeGraph works around this itself now: when the project's filesystem can't host that socket, the shared server puts it in the system temp directory instead, and when the shared server still can't be reached, or drops, the session is served in-process instead of losing the connection. If you still hit it, set CODEGRAPH_NO_DAEMON=1 in your MCP server's environment to skip the shared server entirely (each session runs in its own process). Moving the project onto the Linux-native filesystem (e.g. under ~/ instead of /mnt/) restores the shared server.
Missing symbols — The MCP server auto-syncs on save (wait a couple seconds). Run codegraph sync manually if needed. Check that the file's language is supported and isn't inside a .gitignored or default-excluded directory (e.g. node_modules, dist).
Sharing one checkout between Windows and WSL — Don't point both at the same .codegraph/: the background-server lock and the SQLite index are tied to the OS that wrote them, and SQLite locking across the WSL2/Windows filesystem boundary is unreliable (WSL reports it as a disk I/O error). For a project on a Windows drive (a /mnt/c/… path), WSL keeps its own index automatically: an index first built from WSL goes in .codegraph-wsl/, leaving .codegraph/ to Windows. An index already in .codegraph/ stays where it is, so if Windows built that one, give WSL its own by setting CODEGRAPH_DIR=.codegraph-wsl in WSL and running codegraph init there. CODEGRAPH_DIR always picks the name when set, on either side. CodeGraph skips any sibling .codegraph-* directory when indexing and watching, so the two never trip over each other.
Very large repositories (hundreds of thousands of files), or a large .codegraph/codegraph.db-wal file — The -wal file is SQLite's write-ahead log: writes waiting to be folded into codegraph.db. While a big index is being built, CodeGraph lets it grow in proportion to the index (soft threshold = the larger of 256 MB and a quarter of the index size, up to 2 GB) before folding it back, because folding too often is what made large indexes slow on ordinary disks. At rest it is trimmed to 64 MB, and a leftover from a killed session is folded and trimmed the next time the project opens — the index itself has no size limit. Two environment variables tune this: CODEGRAPH_WAL_VALVE_MB (the soft threshold during indexing) and CODEGRAPH_WAL_HEAL_MB (the resting size and the trim threshold). CODEGRAPH_WAL_VALVE_DEBUG=1 prints every decision to stderr.
Contributing
See CONTRIBUTING.md to build from source, test a change and prepare a pull request. The documentation index links to the project's guides and design notes.
License
MIT
<div align="center">
Made for AI coding agents — Claude Code, Cursor, Codex CLI, opencode, Hermes Agent, Gemini CLI, Antigravity IDE, Kiro, GitHub Copilot, IBM Bob, Factory Droid, Windsurf, Qwen Code, DeepSeek Harness, Grok Build, Kimi Code, Qoder CLI, and Pi
</div>
docs/viewer-launch-changelog.md
Changelog entries held for the viewer launch
These describe codegraph ui and its screens. They were taken out of ## [Unreleased] when a fixes release shipped with the viewer withheld behind CODEGRAPH_UI=1 (src/bin/viewer-gate.ts). At launch, move them back under ## [Unreleased] in CHANGELOG.md, bring back the Highlights below, and delete this file.
Highlights, as written for the viewer launch
codegraph ui— your graph in a browser. A local, read-only viewer for the project you already indexed: your code with its callers and callees in the margin, a map of the whole repository, and a strip that shows how one symbol reaches another.See your app the way its users meet it. A Screens tab draws every screen and the navigation between them, for Expo Router, React Router, Next.js, TanStack Router, Vue Router / Nuxt, SvelteKit and Angular apps.
See what happens from a screen or an endpoint. A Steps tab draws what one action sets in motion — the handlers it fires, the state it writes, the calls that leave your code, and every way it can answer — with the condition on each arrow.
APIs too, and across tiers. Endpoints in Express, NestJS, Fastify, Koa, Hono, FastAPI, Flask, Django, Spring, ASP.NET, Vapor and Gin, with a page's
fetchfollowing through to the route that serves it, a queued job to its consumer, an event to its handler.Read a handler in the order its code runs. The same picture laid out by when things happen rather than by distance, so a reply sits below the token it carries. Where the code chooses, the condition is said once and each arrow answers it.
Find what nothing reaches, and where everything starts. Dead code and entry points as screens of their own, a type hierarchy showing what is built on what, and trails you can name, save and come back to after the code has changed.
Conditions and arguments for more languages. What a call passes, and the
if/switch/tryit runs under, are now read for Python, Java, Kotlin, C#, Go and C as well as JavaScript, TypeScript and Swift.Upgrading: re-index your projects after this release — several of the new readings rest on edges that are written while indexing.
New Features
The Steps tab draws what an Angular screen does. Each handler a template binds is drawn with the event that fires it, like
submitFormon the form's(ngSubmit)ortoggleFavoriteon a button's(click), followed by the requests it sends and the screen it opens. That includes handlers in child components the screen renders. Re-index Angular projects after upgrading.The Screens tab draws Angular apps. Every screen in an Angular app's routes, with the navigation between them:
router.navigate(…), a guard's redirect and each template'srouterLink, under the condition the code checks first. A button in a child component, like an article's favorite button, is drawn from the screen that renders it. A layout's tabs and buttons are drawn from every screen inside that layout. Re-index Angular projects after upgrading.A big screen's picture stops wrapping into a column. How wide a screen's lines run before they wrap was worked out with a formula, and the formula was wrong for the way these pictures are actually drawn: a part of a screen spends lines on its own structure — a step that fires things gets a line to itself, and what it fires starts another — so estimating the lines from the boxes alone badly undercounted them, and one screen's 98 boxes wrapped into a 4,356px column. Laying a picture out is cheap and exact, so the widths are now simply tried and the one that comes out closest to the shape of a window is kept. Across one app's 51 screens the tallest picture went from 4,356px to 3,796px, total height fell 8%, and — because a shorter picture is also a picture whose lines have less far to go — lines running over other boxes fell by a third and lines crossing each other went from 13 to 5.
A screen's picture stops being mostly empty. The parts of a screen were tiled a row at a time, each row as tall as its tallest member — so one short region beside a tall one left the whole rest of that row blank. Measured across one app: the median screen's canvas was only 55% picture and 45% nothing, and its busiest screen was 44% — 4,860px tall to hold about 2,160px of content, all of which a reader has to scroll through. Each part now goes as high as it can and then as far left as it can, so a short one tucks under another short one instead of waiting for the tall one beside it. Reading order is unchanged: parts are still placed in the screen's own source order, and an earlier one is never pushed below a later one. That screen is now 3,584px instead of 4,860px, and close to square rather than a ribbon.
The screen's own line into each part of itself stops sweeping across the picture. A screen's picture holds back the screen's whole fan-out and draws one line into each region instead — but on a screen with ten regions those regions tile into bands, so the line into a region two bands down had to travel the height of the whole picture to get there. Measured across one app's 51 screens: those lines were 17% of everything drawn and caused 79% of every remaining crossing. Two fixes. The line now lands on the box nearest the region's top-left that the screen actually leads to, rather than on whichever box the walk happened to meet first — which, once a screen is drawn in clusters, could be lines down inside the region. And when the region is still too far for a line to be followed, the link is said in words like any other, with no exception for the screen itself. Across those 51 screens, lines crossing each other went from 47 to 10, with no screen above 10; the busiest screen went from 19 to 2.
A busy screen's picture no longer draws itself as a web. On a screen whose whole body is one component, the region grouping had nothing to divide — 98 of 100 boxes landed in a single column — and the picture came back as a 5,600px ribbon whose 113 lines crossed each other 652 times, each one running over five other boxes' names. Three things are fixed. A screen is now laid out in clusters: a step, then the steps it sets in motion on the line under it, stepped in — so a box and the thing it fires are one line apart instead of seven, which is what the crossings were made of. A region's lines run to a width its size earns rather than a fixed one, so a big screen comes out about as wide as it is tall instead of a narrow ribbon. And a link whose two boxes end up too far apart to follow is now said in words at both ends —
→ resumeInferenceunder the box that leads there,← CaptureViewunder the box it arrives at — rather than drawn as a line across the whole picture; select either box and every one of its real lines draws, exactly as before. On the screen that prompted this, the lines drawn at rest cross each other once, down from 652, with half of them still drawn as real lines. Steps that fire nothing still share a line, so a flat screen is unchanged. Nothing needs a re-index.A region that holds a cycle no longer stretches the picture. Rows inside a region were settled by relaxation, which never converges on a graph that has a loop in it: one real screen's 65 boxes were pushed to rows 294-301 while the rest sat at rows 0-2, and the order they were drawn in had nothing to do with what leads to what. Cycle-closing links are now set aside before the rows are settled, as the in-order reading already did — the loop is still drawn, it just cannot stretch the picture.
A busy screen's picture is laid out by the parts of the screen. A screen is a set of handlers with no order between them, so on a hub screen the old rows-by-distance collapsed into one enormous row — the main screen of one app put 89 boxes side by side on a canvas over 28,000px wide, every line a near-horizontal sweep across all of it. The Steps tab now groups a screen's picture by region — the component that owns each handler, named in a small caption over its boxes — with each region a column where a step sits above what it sets in motion, tiled in the screen's own source order. At rest the picture hides only two things: the screen's own fan-out — one line into each region stands in for it — and lines that point back up; every other line draws where it leads, between two regions included, and selecting a step brings out its whole story in the side panel, link by link. A box nothing points at is the screen's own doing — run on render or mount, or from a binding written inline — the key says so, and selecting it lights its line from the screen with what fires it. The same app's widest screen now lays out under 3,500px with every line local, and the whole picture fits on screen when it opens. Endpoints, handlers and the in-order reading are untouched, and nothing needs a re-index: the regions come from the same walk that draws the steps.
Where the code chooses, the picture says so once. A helper that ends
return (await hasSeenWelcome(id)) ? '/home/' : '/welcome/'sends the app to one of two screens, but the Steps picture drew that as two separate arrows, each carrying the whole condition with one of them negated and both cut off at the same forty characters — and before you clicked anything, neither arrow was labelled at all, so nothing said it was a choice. Now sibling arrows out of one box that are the arms of oneif,switchor ternary are drawn as the choice they are: the condition is written once under the box that decides it, and each arrow out says only which way it is —yes,no, or a case's own value. They are the only arrows labelled before you select anything, so the picture reads at a glance without becoming a wall of text. A one-sided guard — an early exit, anifwith only one side drawn — still carries its condition on the arrow, and an arrow that is reached whether or not the condition holds never claims a side. Nothing needs a re-index: the decision is read from the source at request time.A dialog's buttons fire what they run.
Alert.prompt('Add Folder', …, [{ onPress: (name) => createBackgroundFolder(name) }])is two facts: the prompt is a call that leaves the index, and its button fires the handler. The handler's line now arrives from the dialog's own box — with the condition on it — instead of from the screen, so the confirm-then-act chains a mobile app is full of read as chains: the delete alert leads to the delete, which leads to the request it sends. The same holds for anything bound inside the arguments of a call that leaves the index. Nothing needs a re-index: it is read from the source at request time.The Steps tab draws a handler in the order its code runs. The picture of what an endpoint sets in motion put the lookup, the token signing, the 200 and the 401 side by side, because each is one step from the anchor — true, and not how the code reads. Now a handler, an endpoint or any function opens as the same picture laid out by when things happen: a line means and then, so the 200 sits below the token signing it is built from and the 401 branches off the check that chose it. Where the code forks both ways, a small box asks the condition once and each line out of it answers —
yes,no, a case's value — so the two arms of oneifread as one decision instead of two lines that each carry the whole predicate, one of them negated; selecting a step next to the decision lights the whole fork through it. A lone guard — an early exit, anifwith one drawn side — keeps its condition on the line, and an arm that answers the request, returns or throws simply has nothing leaving it. A call written inside another call's arguments happens first, so the token is signed before the reply that carries it. A helper is drawn where it is called (via generateToken), a body that repeats says so (for each item of items), and work registered to run later (later · then) or started at once (together · Promise.all) says that rather than pretending to be a sequence. A screen still opens as before — its handlers fire on events and have no order between them — and either reading is one click, or one&view=order/&view=treein the link, away. Nothing to re-index: it is read from the source at request time, and where the conditions cannot be read the picture is a plain sequence rather than an invented structure.Double-click a box to go there. On the Steps tab a double-click on any step starts the picture from it — the same as the panel's Start here — so an endpoint the page calls, or another screen drawn as a boundary, opens as its own chapter in one gesture; on the Screens tab a double-click on a screen opens what happens from it. A boundary's panel now says it is not entered rather than that nothing leaves it.
The Steps tab now draws an API as well as an app. Anchor on an endpoint —
POST /usersin Express, NestJS, Fastify, FastAPI, Flask, Django, Spring (Java or Kotlin), ASP.NET, Vapor or Gin — and the viewer starts at the handler the route runs (or at the route itself when the handler is an inline arrow), says what fires it (FIRES FROM POST /users · after authenticate, validate(…)— the middleware arguments at the registration, or the guard decorators on the method and its class, or a FastAPIdependencies=[…]), and draws what the request sets in motion: the database calls with the model and whether they read or write (prisma.user.create({ data })·database · user · write), jobs put on a queue, emails, payments, cache reads, token checks, calls to other services, files and processes — and the responses, one box per status a handler can send (201,404) whose panel rows are the endpoint's contract as the code has it:WHEN NOT user → 404 · NotFoundException('no such user'),always → 201 · res.status(201).json(user). A queue consumer or a scheduled job anchored by name says the decorator that fires it (@Process('email')). The legend, the panel and the chooser use the project's own words — endpoint, data call, another tier — and the bare Steps tab lists an API's endpoints by router file when there are no screens. Re-index is not needed: everything new is read from the source at request time.Calls are read as written, so the database is the database. The index keeps only the last segment of a deep member call (
createforprisma.user.create), and a bare name matches by name alone — often to the wrongcreate. The Steps walk now reads each call from the source as written, classifiesprisma.user.create,this.usersRepository.save,session.commit,owners.saveand_context.TodoItems.Addby the whole chain and by the receiver's declared type (OwnerRepository owners,private readonly usersService: UsersService,val owners: OwnerRepository, read from the class body), and followsthis.usersService.findByEmail(…)into the class the type names instead of the name-only guess. A hop resolved this way says so in the panel.A Steps tab in
codegraph ui— what happens from here. Pick a screen (or search any symbol and choose What happens from here) and the viewer draws everything it sets in motion as typed steps: the handlers wired to its taps and listeners, the calls that cross into native code, the native events that come back, the store actions it writes, and the calls that leave the app into the network, storage, the device or telemetry — one box per step, an arrow for every way one leads to the next, and on each arrow the condition under which it happens. The plumbing between two steps (hooks, helpers, the components in between) is folded into the arrow and listed in the side panel, exactly as the Screens tab folds a tap's chain into one transition — and every call the panel lists says what it passes, read from the source as written (SecureStore.setItemAsync('userEmail', values.email),axios.post('/auth/login', { email, password })), so a step is not just that something was stored or sent but what. And each handler says what fires it — the JSX prop and its element (onPress · <Button>), the option it is written under (onSubmit · useFormik(…)), the listener or effect it runs from — read from the source at the call site, soonPress={() => handleLogin(values)}and Formik'sonSubmitmakehandleLogina step of its own with the event on the arrow into it. Any step is the next anchor, any link opens as a Flow strip, a cap the walk hit is announced on the step it hit it at, and the picture travels in the URL. React Native + Expo apps get the full picture today; any project gets handlers, stores and calls that leave the index.A Screens tab in
codegraph ui— the app the way its user meets it. One box per screen, an arrow for every way of getting from one to another, and on each arrow the condition under which it happens. Click a screen and each of its transitions is labelled beside the screen at the other end of the line with the last condition checked before it happens —→ isCollectedabove/object-detail— laid out so that no two labels overlap and none sits under a line; hover a label, a line, or its row in the side panel for the whole condition and the chain the tap travels through (HomeSearchResults → ItemCard → openObjectDetail), with a link to each navigation call. A screen that returns to where it came from is drawn around the boxes rather than through them, shared chrome (a top bar rendered on ten screens) is one node a row above what it opens rather than the same arrows from every box, a screen that opens many others is wide enough to follow each line back to it and its lines take separate paths through the gap so they fan out instead of stacking, hovering picks the line nearest the pointer, and a helper that chooses the destination after login shows its fork. Projects whose graph holds screen navigation land on this tab. Expo Router apps today.Read your graph in a browser:
codegraph ui. Point it at a project you've already indexed and it opens a viewer for it on your own machine. Pick a symbol and you see who calls it on the left, its real source in the middle with a marker on every line that calls something, and what it calls on the right, each one drawn level with the line that calls it. Hover either end and both light up; click anything to step into it. Test callers fold into a single line so real callers stay in view, edges CodeGraph isn't confident about are folded away as "uncertain" rather than shown as fact, and a symbol no test reaches within three caller hops says so on a badge. A blast-radius strip counts what a change would reach. Search with/or Cmd-K across every symbol and file, start from suggested entry points (routes, hubs, files that run code when imported), and follow a trail of the path you walked that lives in the URL, so you can send someone the exact route you took. Click any file path for that file's outline in source order between everything it depends on and everything that depends on it.Run
codegraph uiin an indexed project, orcodegraph ui /path/to/projectfor one indexed elsewhere (codegraph webis an alias). It takes port 4747, or the next free one;--port <n>pins a specific port and--no-openjust prints the URL for a headless box or an SSH session. SetCODEGRAPH_BROWSER=<command>to choose the browser, orCODEGRAPH_BROWSER=noneto never open one.The viewer listens on
127.0.0.1only, so nothing on your network can reach it, and requests claiming to come from any other host are refused. It opens an index that already exists, never creates one, and never changes your graph or a line of your code — the one thing it writes is a trail you asked it to save (see below), and--read-onlyturns even that off. It sends nothing anywhere.A map of the whole project, in
codegraph ui. The Map tab draws your repository at module granularity — one box per directory — with dependencies pointing down, so the top of the picture is what runs first and the bottom is what everything else stands on. Nothing is placed by hand and nothing floats: a module sits one layer above whatever it depends on, line weight is how many calls, imports and type references cross the link, and the same project always draws the same picture. Hover a link for what crosses it, including the busiest symbol pairs behind the weight; click a module to isolate its links, list its dependencies and dependents with counts, and jump straight into one of its files.It says what it leaves out. Links carrying only a handful of references stay hidden until you select a module they touch, references CodeGraph isn't confident about are excluded from every count on the screen and the number is printed, and mutual dependencies, module loops and circular imports between files are listed rather than straightened away. The vertical order rests on the dependencies your code actually writes down — imports, qualified names, inheritance, typed receivers — because a method name shared by two unrelated folders should not be able to move a box.
It opens on your project's source directory; a picker switches to any other top-level folder or the whole repository, a checkbox brings tests in, and
depthsplits a large folder into its sub-folders — useful on a monorepo. What you're looking at lives in the address, so the view is shareable.Ask how one symbol reaches another, in
codegraph ui. Type "how does execute reach getFile" into the search box — orexecute -> getFile— and the Flow strip draws the call path between them, left to right, one card per hop. Each card is opened at the exact line that makes the next call rather than at the top of the function, so reading the strip is reading the handful of lines that actually carry the work; the identifier being called is a link, and clicking a card opens it in the symbol screen with the trail already set to the path you've read so far.A dashed link is a hop nobody can see in the source — a callback, an interface dispatch, a React re-render, a JSX child — and it names the mechanism and, where CodeGraph knows it, the exact line the handler was wired at. When a name means several definitions, the strip says so and names the one the path runs through, offers the alternatives in a picker, and can draw them together as one branching diagram.
The "Read as flow" button on the trail turns a walk you did by hand into the same strip. It is the same path finder
codegraph_exploreleads its answers with, so the picture and what your agent tells you can't disagree.When a path runs out, the Flow strip says where — and why. A flow that doesn't reach what you asked about now ends in a small block: "Where the graph stops." It names the kind of dispatch that ended it — a computed member call, a
getattr, a reflective invoke, a typed message bus — and the line it's on, and the card beside it opens at that exact line so you can read the code the block is talking about. Where the key is written in the source (handlers['save']) it shows the key and shortlists the symbols that could be on the other side, marking any you already named; where the key is a runtime value it says so rather than guessing.It also lists what CodeGraph chose not to follow: name-only matches it wasn't confident enough about, with their confidence, and a count of the other calls the symbol makes that this path didn't need. Nothing is invented — no edge is guessed and none is added to your graph — and a flow that does reach what you asked for never shows the block at all. It's the same finding
codegraph_exploreannounces to your agent when a flow breaks, so the screen and the answer agree.Read a whole file, with its call graph in the margin, in
codegraph ui. The file screen gained a Source tab: the file itself, top to bottom, with the same gutter markers as the symbol view and the same right-hand list of what each line calls, positioned level with the line that calls it. A 6,800-line file scrolls at full speed — only the lines on screen are ever drawn, and the text pages in behind you while the markers are there from the first frame.In the left margin is an arc for every call that stays inside the file, drawn from the calling line to the line the callee is defined on. Nothing is laid out by an algorithm — the author already put the symbols in order, so source order does the work, and this is the one place a file's internal call structure is legible at a glance. Hover a line to light the arcs the function under your cursor takes part in, and click an arc to jump to the other end. On a file with more than forty of them the picture narrows to the symbol you're reading instead of drawing a wash of overlapping sweeps, with the total in the header. A rail on the far left lists the file's symbols and follows you as you scroll, when the window is wide enough for it.
The viewer keeps up with your project while it's open. Save a file and
codegraph uisays so within about a third of a second: a banner on the file's screen explaining that the index hasn't caught up yet, and the file's current source in place of a body sliced at line numbers it no longer has — the call arcs, gutter markers and call list go away with the old numbering rather than pointing at the wrong lines. It's the same answercodegraph_nodegives your agent about a file that changed after its last sync.When anything re-indexes the project — your agent's background sync,
codegraph sync, a git hook — whatever is on screen re-reads the graph and a small "Index updated · reloaded" note appears. A symbol that moved because you added a line above it is followed to its new place, with your trail intact, instead of turning into a dead link.Nothing polls: the viewer watches for these two things and is told about them. If it loses touch with the server it retries a few times with a growing delay, then stops and says "Not live" in the top bar rather than hammering a port that isn't answering.
Take a flow or a map with you: copy it as an image, or save it as an SVG. The Flow strip and the Map both gained Copy image and Download SVG. Copy image puts a PNG on your clipboard, ready to paste into a pull-request comment or a chat — the fastest way to say "here is what your change actually touches" without asking anyone to install anything. Download SVG saves a file for a README: it is real text rather than a bitmap, so it stays sharp at any size and the symbol names in it are selectable.
Both render the light theme whichever one you are reading in, because the image is going to be read on somebody else's screen, and both carry a caption saying what the picture is. What comes out is exactly what is on screen — the same hops, the same dashed dynamic-dispatch links with their wiring sites, the same "where the graph stops" block, the same modules dimmed or brought forward by your selection — because the image is drawn from the same measurements the screen is, not photographed off it. An eight-hop strip comes out around half a megabyte, well inside what GitHub takes inline.
"Where does anything start?" has a screen now. The Entry points tab in
codegraph ui(or presse) is the first thing worth opening on a codebase you have never seen. Every route with the symbol that serves it and thefile:lineyou will find it at, grouped by the file the URL is registered in — your router, not your handlers — and headed with the framework CodeGraph detected it from. Under that, the files that actually do something when they load (a CLI, a worker entry, a build script), the tests ranked by how much of the project each one exercises, and the symbols the most code depends on.None of it is guessed from a filename: a file "runs something" because the graph recorded a call from the file itself, and a project with fewer than three routes simply has no Routes section rather than an empty one. Every list says how much of itself it is showing, and says "at least" wherever the real total can only be a floor.
Any row that names a symbol can start a flow: press
Flow ›, then type a second symbol or press→ hereon another row, and you get the path between them — so "how doesPOST /v1/payroll/cycles/{cycleID}/runreach the database" is two clicks. Typing into the search box now finds entry points too, under their own heading below the symbol matches, so a URL comes back with its handler attached instead of on its own.Syntax colouring in
codegraph uinow comes from CodeGraph's own reading of your code. The viewer used to run a second syntax highlighter over source CodeGraph had already parsed, with its own separate set of grammars. It doesn't any more: the colouring is taken straight from the parse that built your graph, so a file is coloured by exactly the grammar that decided what its symbols are. Three things you will notice — the name a definition declares now stands out on the line that declares it, wherever it appears; calls written inside a string (${user.name()},#{...},$"{...}") are read as code and are now clickable links like every other call site; and built-in type words such asstring,intandvoidlook the same in every language instead of one way in Go and another in TypeScript. A big file paints far faster, most visibly in TypeScript, which was by a wide margin the slowest before.Two formats change for the worse and it is worth saying so: Liquid, Razor, YAML, Twig, XML and
.propertiesfiles are shown without colouring now, and in.svelte,.vueand.astrofiles the<script>block is coloured but the surrounding markup is not. Nothing about navigation changes there — call sites in those files still link, exactly as before.This also takes about 3 MB of grammar files and two dependencies out of the install.
The viewer's screens are now a component library other tools can render. The Symbol view, the Flow strip and the Map are packaged as
@colbymchenry/codegraph-ui— the same componentscodegraph uidraws, not a copy of them — so another application can show you a symbol's callers, a call path or your architecture over its own copy of the graph. Everything a screen knows arrives through one small interface it is handed, so the tool doing the rendering decides where the data comes from and where a click goes; a design-token stylesheet ships with it so the screens can be themed to match whatever they are embedded in. It is versioned with the engine, so the reader and the graph it reads always match.Nothing changes for
codegraph uiitself — it is the same viewer, now the library's first user. The package is prepared, not yet on npm.See what a type is built on, and what is built on it, in
codegraph ui. Open a class, interface, struct, trait or enum and a small tree now sits above its members: what it extends and implements, going all the way up rather than stopping at the direct parent, and everything that extends or implements it, going down. Inheritance is drawn with a solid line and implementation with a dashed one, so the two never read as the same relationship, and every row opens the type it names.For an interface, the list below it is the answer to a question source code cannot give you: a call through that interface can land on any of them, and where there are enough of them to make a static answer meaningless the block says so in a sentence. Go's implicit interface satisfaction is included — a struct that satisfies an interface without either file mentioning the other appears in the fan, marked as matched by CodeGraph rather than written down, along with the line it was matched at. Long fans fold behind a "+N more implementations" button rather than being cut off, and if there is more below than was walked the tree says that too.
Members that redeclare something from a type above are marked in the outline ("overrides Base", or "satisfies Clock" for an interface), so a 40-member class shows at a glance which parts are its own and which are a contract it is filling. The number of implementations shown here is the same number
codegraph_explorereports to your agent when it announces an interface dispatch.Find the code nothing reaches, in
codegraph ui. A new Dead code tab lists the symbols no import, call or reference anywhere in your project reaches — biggest first, grouped by the file they live in, with the number of lines each one would take with it. A class nobody uses brings its methods along as a single finding rather than eleven. Every row opens the code.The screen is built to be believed rather than to look impressive. A line above the list says, and keeps saying, that this means "no static reference in the index" and not "unused" — reflection, a framework registry and a template can all reach code a graph cannot follow. Under the list, every reason a candidate was left off is printed with its count, so you can see the list is twenty findings out of two and a half thousand candidates rather than twenty out of twenty-one.
Those exclusions are the feature. Anything exported, or declared in a header, is off the list by default, because something outside your repository can import it — one switch adds them back, with a warning band. So are test and generated files, abstract and interface declarations, anything a decorator registers, members that override something further up, names the language calls for you (
constructor,__enter__,main), vendored directories, and files nothing in your project reaches at all. Two more rules catch what the graph itself missed: a name CodeGraph failed to resolve somewhere is never called unreferenced, and neither is a name shared with a symbol that is used — the twin may simply have been picked instead. Last, before any row is shown, the files that could reach it are read and the identifier counted: written down twice, something uses it and we did not see it.On the Map, a module nothing depends on now says so in its own count line instead of counting itself — usually your entry points, sometimes something you forgot to delete. Tool-generated files and modules are dimmed wherever they appear: on the map, in its file list, in search results and on the file screen.
Keep a walk you want to come back to: saved trails in
codegraph ui. Follow a path through the code, press Save trail on the trail bar, give it a name, and it's kept. Saved trails are listed on the empty screen and on the Entry points tab, above the suggestions — a walk somebody named beats any ranking — and opening one puts you back at the symbol you left with the whole path in the trail bar. Explaining "how a request is served" to a new teammate is now a link and a name rather than a paragraph.A saved trail survives your project changing. Each step is remembered by what it is — its qualified name, its kind, the file it was in — rather than by where it sat, so editing the file above a function doesn't lose it. When something does move, the trail says so on its own row: which step moved to another file, which one was renamed or deleted, and which part of the walk still opens. It never quietly stitches over a missing step, because a trail is a path and a step that skips one would show a call that doesn't exist.
Trails are plain JSON, one file per trail, under
.codegraph/ui/trails/— already ignored by git, so they stay yours by default. Export hands you the file if you'd rather commit one for the team. This is the only thing the viewer writes: it still never indexes, never changes your graph, and never touches a line of your code. Start it withcodegraph ui --read-onlyand it won't write even that — saved trails can still be opened, just not saved or deleted.
Fixes
Steps diagrams retain database operations made through external client chains without inventing internal dependencies.
Fixes — Screens, links and navigation
Routes served by a component are listed. Entry points and the Steps picker listed a route only when a function, method or class served it. A Vue Router, Nuxt, Svelte or Astro screen is served by a component, so those apps' routes weren't listed at all. They are now, each with the component that serves it.
One busy inline handler no longer hides the other routes. Entry points and the Steps picker filled their list by the symbols each route reaches rather than by routes, so an inline handler with dozens of calls could use up the list by itself, and the routes after it went unlisted. They now count routes. Thanks @danusha2345. (#2370)
Where the app goes after login is a fork, not two always-es. A navigation whose destination comes back from a helper —
router.replace(await resolvePostLoginRoute())overreturn (await hasSeenWelcome(…)) ? '/home/' : '/welcome/'— drew both screens with no condition, reading as if the welcome screen always shows. The two arms share a line, and only a column can tell them apart; each synthesized edge now carries its literal's own position, so the guard reader says which arm it is:WHEN await hasSeenWelcome(…)→ home, and its negation → welcome. And the scan starts at the helper's body, so a literal-union return type —Promise<'/welcome/' | '/home/'>, whose routes are string literals too, written first — no longer stands in for the navigation itself. Re-index after upgrading to pick the positions up.A screen that talks to native code keeps its own navigations. In a React Native or Expo app, a
router.pushwritten inside a listener for a native event was credited to whichever screen had started that round trip, not to the screen the push is written on. In one app that moved seven transitions off the capture screen and onto the review screen it opens — leaving the review screen looking as though nothing in the app could reach it, stranded in the "no transition reaches this" band at the bottom of the Screens tab, and printing Swift conditions likeThread.isMainThreadon a JavaScript navigation. A navigation now belongs to the screen whose file it is written in; an event arriving from native code, from an HTTP call or off a queue is no longer read backwards as if it were a caller.A link written under a condition says so on the Screens tab. A checkout stepper whose tabs are each enabled by their own prop, and a navbar whose admin links only render for an admin, both read as always — every transition written in markup was drawn with no condition at all, while the ones written as calls carried theirs. They are read the same way now: a store's checkout tabs say
step1…step4, its navbar saysuserInfo && userInfo.isAdminfor the admin links and!userInfofor sign-in, and 59 of that store's 74 transitions carry the condition they actually run under, up from 20. A template language with no condition rules of its own still says nothing rather than guessing.A link in markup no longer reads as a helper's return value.
<Link to='/shipping'>was labelledreturn /shipping, which in this picture means the destination came back from somewhere else and was inferred. It is written right there, so it now readslink /shipping— and an internal<a href>readsa. Only a destination that genuinely arrives from elsewhere still saysreturn.API endpoints are no longer drawn on the Screens tab. A store's thirty Express endpoints sat beside its nineteen pages as boxes nothing navigates to and nothing leaves — in a picture that is only about navigation — and they stretched the row of unreachable pages hundreds of boxes wide. A screen is now a route named by its path alone; a route named with the method that reaches it (
GET /api/orders,POST /api/users/login,ANY /api/users) is a request, not somewhere a user can be. Every route still appears on Entry points, which is the list of everything a request or a user can arrive at.A screen you could reach but never leave. Three separate things left a page's own navigation off the map, and a store's home and sign-in pages showed nothing leaving them. One component, several addresses: a screen rendered at more than one route — a listing page that is also the search and the paginated results — handed all of its navigation to whichever route happened to be written first, and the rest were drawn as dead ends; every address it serves now gets it. A link written as a choice:
<Link to={redirect ? \/register?redirect=${redirect}` : '/register'}>is how a link that carries state is written, and markup was read by a weaker reader than calls were, so it saw nothing; both now use the same one. **A destination whose other half is unknowable:**const redirect = location.search ? location.search.split('=')[1] : '/'followed byhistory.push(redirect)is how every app sends a user on after signing in — the/` is where it lands by default, and reading neither half lost the whole transition. Where both halves ARE readable and disagree, it is still a fork and still nothing.A server action written through a wrapper starts its transition on the right page.
export const signIn = validatedAction(schema, async (data) => { … redirect('/dashboard') })— the arrow inside is no symbol of its own — now belongs tosignInon the Screens tab, andsignInis attributed to the page whose component hands it touseActionState(signIn, …), read from the source when the graph holds no such edge.React Router apps that keep their routes in a table get a Screens picture. The ASP.NET Core React template's
AppRoutesarray, aroutesarray handed touseRoutesorcreateBrowserRouterfrom another file, and admin kits'[MainRoutes, LoginRoutes]route objects drew no screens, because their routes were never read. They are now, with the links andnavigate(…)calls between them. A parent route object's component is drawn as the layout around the screens inside it: its own links count from each of those screens, and when anindex: truechild claims its address, that child is the screen there. Re-index React projects after upgrading.A React Router layout written as JSX is drawn around every screen inside it. In
<Route path="/" element={<Layout />}><Route index element={<Home />} />…</Route>, the screen at/is Home rather than Layout, and the links in Layout's header count from each screen nested in it instead of only from/. A guard with no path of its own, like<Route element={<PrivateRoute />}>, does the same for its redirect to the login page. Re-index React projects after upgrading.
Fixes — Steps — what a call does, and when
A Steps picture with nothing past its anchor says why. Starting Steps from a helper that only computes drew one box and no explanation. The panel now says that nothing it sets in motion is a step the picture draws, explains that calls between plain functions fold into the lines, and links to the symbol's callers and callees.
Steps no longer goes blank on a function full of checks. Reading a function in its code's order doubled the ways forward at every
ifwith noelse. A body with dozens of checks that draw nothing, like jsoup'sparse, never finished, and the page died with a stack overflow. Ways that arrive at the same place are now one way, carrying only the conditions they all share.A Swift app's Steps picture labels what a call leaves for correctly.
rawValue.data(using: .utf8)was drawn as a network call, Foundation'sTimeras telemetry,Calendarand a SiriKitintentas device calls, andviewModel.votes.firstIndex(of:)as a database read. Now only a session'sdata,uploadanddownloadare network,Timer,Calendarandintentare no effect at all, a view model is not a table, and a keychain is storage. A call through a type the app declares itself, like IceCubes'Notificationsendpoint enum, is no longer read as the library of the same name.A server-rendered endpoint's Steps picture shows how it answers. A Spring MVC handler answers by what it returns: a view name, a constant holding one,
"redirect:/owners/" + id, aModelAndViewor aRedirectView. Each is now a reply, a render (200) or a redirect (302), under the condition it's returned in. petclinic'sPOST /owners/newdraws the form re-renderedWHEN result.hasErrors()and the redirect otherwise. A Laravel controller'sview(…),redirect(…),redirect()->back(),back(),to_route(…)andresponse()->json(…)are replies too, so BookStack's book page draws the page it renders beside its404and its old-slug redirect. PHP's own programming-error exceptions (InvalidArgumentException,LogicException, …) thrown deep in a helper are no longer drawn as one of the endpoint's answers.A handler called from under a binding says what it passes. A press that runs
tryCatchSync(onClosePress)drew a box for the wrapper and stopped — leaving the one thing a reader asks ("what is being wrapped?") unsaid, even though every other call-shaped site already prints its arguments. The panel and tooltip now saytryCatchSync(onClosePress)— the argument is the answer.A step the walk stopped at keeps its whole name. A boundary — another screen, or a cap the walk hit — ends its name with an ellipsis by design, but the box was not sized for it, so a longer name lost its last letters instead (
/scan-to-verif…for/scan-to-verify …). The anchor's start mark clipped a long path the same way (/sheets/forgot-passw…). The box now makes room for both.A server action, or any handler written inside a wrapper, draws what it really does.
const signIn = validatedAction(schema, async (data) => { … })showed one call out of nine, because the constant held a reference to its schema and that counted as having a body of its own. Its picture is now whole — the lookup, the early returns, thePromise.all, the redirect.A call is no longer followed to a same-named method of its own class.
crypto.createHash('sha256').update(…)inside a service that happens to have anupdatemethod was followed into that method, so a login endpoint read as though it updated the user, extra replies and all. A method of your own class is writtenthis.update(…); a receiver that is notthisnow ends the walk instead of guessing.A step is ordered by the call it was actually reached at. An endpoint whose handler is written inline at the registration (
router.post('/users/login', async (req, res) => { … })) had every call in the handler read as part of the registration itself, which put its reply before the work that produces it.A row reads in the code's order. The boxes one step away from a handler now sit left to right as the code runs them — the database read, the token signed while the reply is built, the reply, the error reply — instead of in an arbitrary order; a call written inside another call's arguments (
generateToken(…)inres.json({ token: generateToken(…) })) comes before it, and its link saysinside res.json(…)in the panel and the tooltip.Each way an endpoint answers is its own box. A handler that replies
200or401now draws two reply boxes instead of one200 · 401, so each line from the handler carries its own condition on the picture —→ user && (await user.matchPassword(password))into the 200,→ NOT (…)into the 401 — the way a screen's transitions do; replies whose status the code does not spell out share one box labelled by the call.A reply that sets no status is a 200.
res.json(user),res.send(…),reply.send(…),NextResponse.json(…), aJSONResponseorjsonify(…)with no status in the chain now count as200, so an endpoint's success reply has a box of its own beside the401's and its row carries its code; a status set by the statement just before (res.status(202); res.json(user)) is that reply's. An Express handler written inline at the registration keeps its own replies too — they were filtered out with the framework noise.The Steps walk stays out of test doubles. A production handler that calls an interface method is no longer followed into
TestUserDataRepository(or any implementation in a test folder); the counts in the summary no longer say "outside the indexs".Conditions read as words, and each scenario gets its own row. In the Screens and Steps tabs a condition is now written the way you would say it —
WHEN NOT (isUploadInProgress || elapsed < 5000) AND user?.organization_id, the joining words set apart from the code — with the code inside each guard left as written, and a transition or step with several call sites (four early returns that each go home) is listed as four rows, the clauses they all share said once above them, instead of one string joined with||. A guard that is itself an either/or keeps its parentheses everywhere conditions are shown, includingcodegraph_explore's Flow section.Every call now says when it happens. In
codegraph ui, a symbol's callee and caller rails and the Flow strip's connectors carry the branch conditions the call site sits under —when !isUploading && isCollected— andcodegraph_explore's Flow section prints the same on each hop (↓ calls (when isCollected)). The conditions come from theif/else/ ternary /switch/&&branches around the call, the early returns before it (if (busy) returnreads as!busy), and Swift'sguard; an inline callback inherits the conditions of the place it is defined. Read from the source as it is now, never stored: nothing about your index changes. TypeScript, JavaScript and Swift today.
Fixes — Symbols, tests and the viewer
Dead code no longer lists methods a framework calls for you. A method with a framework's decorator on it — Angular's
@HostListeneror@HostBinding, NestJS's@Cron,@OnEventor@Process, TypeORM's@BeforeInsert, Spring's@InitBinder— was listed as unused whenever the decorator came from a package outside your project. So was a lifecycle hook like Angular'sngOnInitor NestJS'sonModuleIniton a class that skips theimplementsclause, though the framework calls it by name all the same. Both are now left off and counted under the list. What counts as a hook is learned from your own classes that do write the interface out, not from a built-in list, so an unused helper on the same component is still listed. Nothing needs a re-index.Dead code no longer lists the types you add to a library's or the browser's. An interface written inside
declare module 'chart.js' { … }ordeclare global { … }, like chart.js'sPluginOptionsByType, TanStack Router'sRegister, the browser'sWindow, or a custom element's entry inHTMLElementTagNameMap, merges into a type declared outside your project. The library or the browser reads it from there, so nothing in your code names it, and every one was listed as unused. TypeScript exports what these blocks declare without anexportkeyword, and CodeGraph now records them the same way, so they are left off and counted with the exported symbols. Adeclare namespacein an ordinary module file still belongs to that file and is checked as before. Re-index TypeScript projects after upgrading.One slow file can no longer freeze the viewer. Code is highlighted with the same grammars the index uses. The COBOL grammar never finishes a line like
., which free-format COBOL paragraphs end with, so opening a Flow through cobolcraft frozecodegraph ui, and every page after it waited forever. Highlighting now runs where it can be stopped: a piece of code that takes too long is shown unhighlighted, and everything else keeps working.The Map opens on a picture, not a single box. A project whose code sits in one flat folder, like Express's
lib/, an R package'sR/, an Erlang app'ssrc/or fmt'sinclude/fmt/, opened as one box with nothing to say. It now opens on the whole repository when that draws more. Files at the repository's top level also count when the Map picks where the program lives: git's hundreds of top-level.cfiles had madebuiltin/look like most of the code.A Flow between two symbols finds names of any shape. A Flow opened from a Steps link, an entry point or the search box names its two ends by the symbols' own names, and names that don't look like plain identifiers were dropped: an Objective-C selector like
initWithFrame:, a Ruby method likesave!orvalid?, and any name shorter than three letters, likeok. The strip then said the other end "names nothing in this index", even though it was there. Both ends are now looked up exactly as written.Entry points and the Symbol view no longer go blank. The Symbol view of every Vue, Svelte and Astro component stayed on "Loading…", because its member list held the component's script symbols twice, along with the component and its file. Entry points went blank on apps with inline route handlers like
app.get('/x', async (c) => { … }), which came back once per call in the handler, each call listed as if it were the handler. Each member is now listed once, and each route is one row: the handler it is bound to, or "inline handler" when the handler is written at the registration.Entry points' most-depended-on list shows your code, not a vendored bundle's. A minified library or bundled docs script (
n,t,Bufferwith thousands of callers, all from inside the bundle) could top the list. Symbols in generated files are now left out of it, like test files.Stopping
codegraph uistops the server. Killing the command by its process id, as a process manager, an editor task orkilldoes, left the server running on its port with no way to reach it, until the machine restarted. The server now notices it has been left behind and shuts down, closing the index first. Ctrl+C was never affected.A SwiftUI view with a preview is no longer listed as a file that runs something. A
#Preview { … }sits at the top level of a view's file, so every view with one showed up under entry points as if it ran code. A preview is Xcode's canvas, not code the app runs: its calls no longer count, and a file whose only top-level code is previews leaves the list.The Map opens a Maven or Gradle project on its packages. A Java project keeps every file under
src/main/java/org/<company>/<app>/, and those folders hold nothing but the next one, so the Map drew the whole program as onesrc/main/java/orgbox and no grouping option reached further. A folder with one subfolder and no files of its own no longer counts as a level: petclinic opens onowner,vet,modelandsystem, each labelledsrc/main/java/…/petclinic/owner, with the full path on hover.In
codegraph ui, routes whose handlers live in more than 60 different files are all linked to their handler, instead of the later ones showing "not in the index". (#1975)The viewer continues to count module-level initializer calls as top-level file activity in entry points and file screens.
The Map groups a repository the way that repository is shaped. It always drew top-level directories, so a project whose whole program lives under one
src/opened as a picture of four boxes —src,ios,.github,(root files)— with two thirds of the code inside one of them and nothing to say about it. The Map now picks its own grouping: the shallowest one that is not a single box holding the program, so a mobile app opens onsrc/app,src/components,src/api,ios/CaptureViewand the rest, and a project packaged asfrontend/src/…opens on the screens, components and reducers instead of on the wordfrontend. A repository whose top-level directories really are its modules is left exactly where it was. A new Grouping control on the right says which one was chosen and lets you take it a level in or out, and a leaf directory is now named for itself rather than as…/(root files). Each box now also says how much leans on it — how many files elsewhere reference straight into it — with a bar along its bottom edge scaled against the most depended-on box on screen, so the folder you have to be careful with is the one you can see at a glance rather than the one with the longest name. The Map also has a Key now, like the Screens and Steps tabs — including what the dashed maroon lines mean, which only appear once you select a module: that module reaching back UP into something that depends on it.The Symbol tab opens the Symbol tab. With no symbol open and no trail to return to, clicking Symbol in the top bar took you to the landing page — which, on any project that has screens, is the Screens tab. So the button said Symbol and gave you somebody else's view. It now has an address of its own (
#/s) that opens the "nothing selected" screen: the search prompt and the where-to-start list of routes, entry files and the symbols the most code depends on.The Map covers a multi-root project. A React Native app's
ios/beside itssrc/— or any second root holding a fifth of the code — is now on the picture, one level deeper, instead of the map silently drawing only the larger root.Fixed a long-running
codegraph uisession serving a symbol that a sync had already deleted. The viewer keeps one connection to your index open, and its in-memory lookup didn't notice when another process — your agent's sync, orcodegraph sync— rewrote the file underneath it, so a symbol screen could keep showing a body with no callers while search correctly reported it had moved. Because a symbol's identity includes the line it starts on, this happened after almost any edit above it.A viewer trail now holds up to 64 hops everywhere — the trail bar, saved trails and "Read as flow". Thanks @inth3shadows for the report and @danusha2345. (#1976)
Saved trails stay inside your project. When
.codegraph/uiwas a link to a folder somewhere else, saving a trail wrote it into that folder, even though the save then reported an error, and a trail file that was itself a link to a file outside the project was listed and opened like one of yours. Both are now refused, and links that stay inside the project keep working. Thanks @danusha2345.codegraph uiwon't open a sensitive folder by another name. A link or junction to a folder like~/.sshor~/.config, a differently capitalized name on Windows and macOS, or a\\?\spelling on Windows got past the check that keeps such a folder from being opened, and the viewer then showed the files inside. They are now refused like the folder itself. Thanks @mixxer for finding it.A file with
%in its name opens as itself. The viewer decoded a file's name a second time before reading it, so100%.tswas refused, anda%20b.tswas shown as changed on disk, with no lines, becausea b.tswas read in its place. Every file now opens as the file it names. Thanks @shrankr.Saved trails record the right author when one process serves several projects. An app embedding the viewer for more than one project wrote the first project's git user name on every trail it saved; each project now uses its own. Thanks @danusha2345.
codegraph uion Windows moves on from a port another program holds. Windows refuses a port that another program keeps for itself, or one inside a range it reserves, with a different error than a port that is simply in use, so the viewer gave up at the first such port instead of trying the next one, and blamed ports below 1024 needing elevated privileges — a rule Windows doesn't have. It now moves on to the next free port, and when none is left, or the port you pinned with--portis refused, it says what Windows did and how to list the reserved ranges. Thanks @ijbranch for the report and @sx4im. (#2299)A file's outside imports are listed in more languages. The file screen lists the imports that name nothing in your project, like a third-party package or a system header. In C and C++, Objective-C, JavaScript and TypeScript, Python, C#, Lua, R, Solidity, Svelte and Vue, an import that named no file had been linked to its own import statement, so it was listed nowhere. Re-index after upgrading.
What an Angular template uses is no longer dead code. A signal,
computedvalue, getter or method that a component's template calls from a property binding, an interpolation, a structural directive or a control-flow block —[name]="icon()",{{ label() }},*matRowDef="let row; columns: displayedColumns()",@if (loading())— or hands to a child, like[displayWith]="displayFn", now counts as reached, so it leaves the Dead code tab. Steps draws it as part of what the screen renders, not as something the user does. Re-index Angular projects after upgrading.
Entries whose graph and codegraph_explore half already shipped
The release notes carry a reworded version of each of these. Keep only the viewer half at launch.
A Next.js app lands on the Screens tab like a mobile app. App Router pages (
app/(group)/blog/[slug]/page.tsx→/blog/:slug) and Pages Router pages are screens bound to the component they export;<Link href>, an internal<a href>,router.push/router.replace(next/navigationandnext/router),redirect()/permanentRedirect()in a server action or a page, and the middleware'sNextResponse.redirect(new URL('/login', req.url))are the transitions between them — each attributed back to the page it starts on with the plumbing folded and the condition on the arrow, a link written in markup drawn dashed as an inferred hop.app/api/**/route.tsexports (GET,POST, …) are endpoints bound to their functions,pages/api/*handlers areANY /api/…, and a page's Steps picture fires from its load (FIRES FROM page load · /users), draws the data it reads, the handlers it wires, the server actions it crosses to and the pages it leads to as boundaries. A response's status written as{ status: 201 }is read too. Re-index after upgrading.TanStack Router apps land on the Screens tab too. Routes are read both ways a TanStack app declares them: file-based, where
createFileRoute('/posts/$postId')carries the whole path as a literal, and code-based, where eachcreateRoute({ path, getParentRoute })names a fragment that is composed through its parent into/posts/$postId.navigate({ to })fromuseNavigate, a thrownredirect({ to })from a loader orbeforeLoad, and<Link to>/<Navigate to>are the transitions between them. TanStack is the one router here whose destination is the route PATTERN rather than a filled address —<Link to="/posts/$postId" params={{ postId }}>names the route and passes the values beside it — so a destination is read as a pattern and matched against the route it names. Addresses that are not pages are left off the map: a_authsegment is a pathless layout and never appears in the URL, a(group)folder is invisible, a__rootroute wraps everything without being a page, and a file that renders an<Outlet />is the layout around an address while the index route beside it is the page at it. A computedto, a pattern no route serves, and anavigate({ search })that only changes the query are left out rather than guessed. Re-index after upgrading.Vue Router and SvelteKit apps land on the Screens tab too. Both drew their screens as islands with no transitions, so the tab stayed hidden; now the navigation between pages is read for each. Vue: the routes are read out of
createRouter({ routes: [...] })— path, name, and the view each entry names, including a lazycomponent: () => import('@/views/Login')— androuter.push/router.replace/$router.push, Nuxt'snavigateTo, and<router-link>/<RouterLink>/<NuxtLink>are the transitions. Vue apps usually navigate by route NAME rather than by path, sorouter.push({ name: 'profile' })and:to="{ name: 'profile' }"resolve by name, androuter.push({ path: '/', query })by path. SvelteKit:goto('/login'),redirect(303, '/article/' + slug)from a load or a form action — whose destination is its second argument, after the status — and the plain<a href="/login">that is a link in a SvelteKit app. A SvelteKit page also opens with a body now — it is joined to the page file that serves it and to the+page.server.jsbeside it — so its Steps picture draws its loader's work, its form actions, and the auth guard the loader performs (redirect(302, '/login')underif (!locals.user)) as a transition to the sign-in page, with the condition on the arrow. A computed destination, a path or name nothing declares, a relative path in a nested route, and a conditional whose two arms go to different pages are left out rather than guessed. Re-index after upgrading.A React Router app lands on the Screens tab too.
<Route path='/payment' component={PaymentScreen}>(v5),<Route path='/payment' element={<PaymentScreen/>}>(v6) andcreateBrowserRouter([{ path, element }])already named a project's screens; now the navigation between them is drawn as well.history.push('/placeorder')andhistory.replace,navigate('/placeorder')fromuseNavigate,redirect()in a loader or an action, and<Link to>/<NavLink to>/<Navigate to>/ react-router-bootstrap's<LinkContainer to>each become a transition — attributed back to the screen it starts on, with the plumbing folded and the condition on the arrow, a link written in markup drawn dashed as an inferred hop. A route with an optional parameter (/cart/:id?) is reached by both/cartand/cart/5. Until now a React Router project's screens were drawn as islands with no transitions at all, and a screen's Steps picture left out every page it sends you to — a checkout step showed its saved payment method but not that it goes on to place the order. A computed destination (history.push(redirect)), a path no route serves, and a relative path inside a nested route are left out rather than guessed, and an ordinarypaths.push('/x')on an array is never mistaken for navigation. Re-index after upgrading.The Steps tab follows a web app across its tiers. A page's
fetch('/api/users', { method: 'POST' })(or anaxios/ky/got/$fetchcall, including one through a project instance made withaxios.create({ baseURL })) now reaches the route that serves it in the same index — drawn as a crossing to the server (⇢ POST /api/users) with the handler named on the box and the registration site in the panel, a boundary by default and entered with Continue through, so the picture reads page → handler → the endpoint → its database write → its response. A job put on a BullMQ / Bull queue lands on the@Processmethod,Workerorqueue.processhandler that consumes it; a NestJSEventEmitter2event lands on its@OnEventlisteners (globs included); a socket message crosses from a client'ssocket.emitto the gateway's@SubscribeMessageand back from the server'semitto the component that registeredsocket.on; and a Next.js server action called from a client file is a crossing to the server by its'use server'directive. Each of these is a synthesized hop — dashed, with where it was wired up — andcodegraph_explore's Flow section names them too. Only a literal path or event name pairs: a variable url, a path no route serves, or one two routes serve alike produce nothing. Re-index to pick the new edges up.Conditions and arguments for Python, Java, Kotlin, C#, Go and C. The
whenon a call —if/elif/else,switch/when/match, the ternary and Kotlin'sifexpression,try/except/catch,and/or, and the early exits before it (if err != nil { return }reads aserr == nil,if not item.title: raiseasitem.title) — and what each call passes (HTTPException(status_code=422, detail="bad price"),c.JSON(http.StatusCreated, gin.H{…})) are now read for those languages too, incodegraph ui's rails, Flow strip and Steps tab and incodegraph_explore's Flow section. As before: read from the source as it stands, never stored, and a language without rules yields nothing rather than a wrong label.A
<Card/>is the Card that file imports, not the first one in the repo. When two components share a name —Section,Picker,FrameCard, one per feature folder — the render link out of a component picked whichever happened to be indexed first, ignoring what the file actually declares or imports. That cost twice over: a component gained a link to something it never renders, and the one it does render was left with nothing pointing at it, so callers, impact and the Screens tab all dead-ended there. In one Expo app it left a folder sheet's navigation standing alone on Screens with no screen behind it, while the link pointed at an unrelated card in another sheet; in Excalidraw every<Excalidraw/>in the app pointed at a documentation preview file instead of the component itself. A tag now resolves to the component that file declares, then to the one it imports — through a@/path alias as readily as a relative path — and a JavaScript tag prefers a JavaScript component over a same-named class in a mobile app's native half. No links are added or removed, only pointed at the right place. Re-index after upgrading.A link that goes to one of several places now draws all of them. A destination written as a choice —
!isAdmin ? keyword ? \/search/${keyword}/page/${x}` : `/page/${x}` : `/admin/productlist/${x}`, which is how a paginator shared between a storefront and an admin list is written — used to draw nothing at all, because one edge carried one destination and picking an arm would have been a guess. Every arm is now its own transition, labelled with the path THAT arm takes, so a store's paginated addresses are on the map instead of sitting there unreachable. The same goes forredirect(307, user ? \/profile/@${user.username}` : '/login')in a loader, and forrouter.push(cond ? '/a' : '/b')`. Arms that name the same route still make one transition, and an arm nothing can read still contributes nothing.A client call with a type argument binds too.
useSWR<Team>('/api/team', fetcher)andky.get<User>('/api/users/1')reach their routes like the untyped forms.A SvelteKit layout is no longer a second screen at a page's address.
+layout.svelteand+error.sveltesit at the same path as the+page.sveltebeside them and were each indexed as a route, so one address appeared in the index two and three times over. Only a page is a route now.An Express handler written through a wrapper is the endpoint's handler.
const authUser = asyncHandler(async (req, res) => { … })— theexpress-async-handleridiom — now names the handler in Entry points and starts the Steps walk at it, with the database reads, the token check and the401row read from the arrow's body.Files under an
e2e/directory count as tests. Their calls no longer appear as production callers in Steps, dead-code and test badges.
512 files · 493 not served here
- .claude/skills/add-lang/SKILL.md (not served here)
- .claude/skills/agent-eval/SKILL.md (not served here)
- .claude/skills/agent-eval/corpus.json (not served here)
- .cursor/rules/codegraph.mdc
- .dockerignore (not served here)
- .gitattributes (not served here)
- .github/workflows/deploy-site.yml (not served here)
- .github/workflows/release.yml (not served here)
- .gitignore (not served here)
- AGENTS.md
- ATTRIBUTION.md
- AUTOGINEER-RUNS.md
- BUNDLING.md
- CHANGELOG.md
- CLAUDE.md
- CONTRIBUTING.md
- LICENSE
- README.md
- TELEMETRY.md
- __tests__/absent-member-calls.test.ts (not served here)
- __tests__/adaptive-explore-sizing.test.ts (not served here)
- __tests__/add-lang-check-grammar.test.ts (not served here)
- __tests__/add-lang-verify-extraction.test.ts (not served here)
- __tests__/alias-binding-resolution.test.ts (not served here)
- __tests__/ambient-module-imports.test.ts (not served here)
- __tests__/android-res-exclusion.test.ts (not served here)
- __tests__/angular-router.test.ts (not served here)
- __tests__/arkts-resolution.test.ts (not served here)
- __tests__/astro-component-markup-only.test.ts (not served here)
- __tests__/awaited-receiver.test.ts (not served here)
- __tests__/bare-call-no-method.test.ts (not served here)
- __tests__/batched-ref-cleanup.test.ts (not served here)
- __tests__/beta-signup.test.ts (not served here)
- __tests__/bounded-source.test.ts (not served here)
- __tests__/branch-guards-languages.test.ts (not served here)
- __tests__/branch-guards.test.ts (not served here)
- __tests__/browser-host-globals.test.ts (not served here)
- __tests__/bundle-launcher.test.ts (not served here)
- __tests__/c-calls-never-methods.test.ts (not served here)
- __tests__/c-cpp-declaration-types.test.ts (not served here)
- __tests__/c-cpp-member-supertypes.test.ts (not served here)
- __tests__/c-cpp-member-type-refs.test.ts (not served here)
- __tests__/c-feature-macro-variants.test.ts (not served here)
- __tests__/c-fnptr-kernel-sweep.test.ts (not served here)
- __tests__/c-fnptr-synthesizer.test.ts (not served here)
- __tests__/c-include-lone-macros.test.ts (not served here)
- __tests__/c-macro-renames.test.ts (not served here)
- __tests__/c-macro-return-types.test.ts (not served here)
- __tests__/c-statement-macro-names.test.ts (not served here)
- __tests__/c-static-call-rescue.test.ts (not served here)
- __tests__/call-receiver-no-fabrication.test.ts (not served here)
- __tests__/call-value-targets.test.ts (not served here)
- __tests__/callers-call-sites.test.ts (not served here)
- __tests__/catch-all-path-alias.test.ts (not served here)
- __tests__/celery-dispatch-synthesizer.test.ts (not served here)
- __tests__/cfml-call-scope.test.ts (not served here)
- __tests__/cfml-create-object.test.ts (not served here)
- __tests__/cfml-embedded-parse-warning.test.ts (not served here)
- __tests__/cfml-include.test.ts (not served here)
- __tests__/cfml-inheritance-resolution.test.ts (not served here)
- __tests__/cfml-receiver-inference.test.ts (not served here)
- __tests__/cfml-tag-calls.test.ts (not served here)
- __tests__/child-process-windows-hide.test.ts (not served here)
- __tests__/chinese-filename-retrieval.test.ts (not served here)
- __tests__/cjk-query-tokens.test.ts (not served here)
- __tests__/cli-affected-filter-globs.test.ts (not served here)
- __tests__/cli-affected-paths.test.ts (not served here)
- __tests__/cli-affected-test-conventions.test.ts (not served here)
- __tests__/cli-context-command.test.ts (not served here)
- __tests__/cli-cycles.test.ts (not served here)
- __tests__/cli-definition-grouping.test.ts (not served here)
- __tests__/cli-files-test-classification.test.ts (not served here)
- __tests__/cli-implementations.test.ts (not served here)
- __tests__/cli-index-explicit-path.test.ts (not served here)
- __tests__/cli-install-init.test.ts (not served here)
- __tests__/cli-no-color.test.ts (not served here)
- __tests__/cli-node-command.test.ts (not served here)
- __tests__/cli-parse-warning.test.ts (not served here)
- __tests__/cli-query-command.test.ts (not served here)
- __tests__/cli-sync-stale-index.test.ts (not served here)
- __tests__/cli-sync.test.ts (not served here)
- __tests__/cli-truncation.test.ts (not served here)
- __tests__/cli-ui-command.test.ts (not served here)
- __tests__/cli-unlock.test.ts (not served here)
- __tests__/cli-version.test.ts (not served here)
- __tests__/closure-collection-synthesizer.test.ts (not served here)
- __tests__/cobol-sequence-area-period.test.ts (not served here)
- __tests__/codeigniter.test.ts (not served here)
- __tests__/commonjs-exports.test.ts (not served here)
- __tests__/commonjs-require-calls.test.ts (not served here)
- __tests__/commonjs-require-imports.test.ts (not served here)
- __tests__/compile-cache-missing-drive.test.ts (not served here)
- __tests__/component-same-folder.test.ts (not served here)
- __tests__/concurrent-locking.test.ts (not served here)
- __tests__/config-secret-redaction.test.ts (not served here)
- __tests__/const-alias-flow.test.ts (not served here)
- __tests__/context-ranking.test.ts (not served here)
- __tests__/context.test.ts (not served here)
- __tests__/cooperative-yield.test.ts (not served here)
- __tests__/cpp-absl-holder-receiver.test.ts (not served here)
- __tests__/cpp-alias-inherited-member.test.ts (not served here)
- __tests__/cpp-anonymous-union-members.test.ts (not served here)
- __tests__/cpp-attribute-macros.test.ts (not served here)
- __tests__/cpp-auto-iterator-arrow.test.ts (not served here)
- __tests__/cpp-auto-local-receiver.test.ts (not served here)
- __tests__/cpp-auto-range-for-receiver.test.ts (not served here)
- __tests__/cpp-base-class-lookup.test.ts (not served here)
- __tests__/cpp-binding-and-wrapper-receivers.test.ts (not served here)
- __tests__/cpp-block-comment-lines.test.ts (not served here)
- __tests__/cpp-brace-scopes.test.ts (not served here)
- __tests__/cpp-class-name-receiver-members.test.ts (not served here)
- __tests__/cpp-comparisons-and-template-declarators.test.ts (not served here)
- __tests__/cpp-conditional-class.test.ts (not served here)
- __tests__/cpp-container-accessor-calls.test.ts (not served here)
- __tests__/cpp-declarator-clause-names.test.ts (not served here)
- __tests__/cpp-declared-type-receiver.test.ts (not served here)
- __tests__/cpp-dereferenced-iterator-calls.test.ts (not served here)
- __tests__/cpp-element-access-calls.test.ts (not served here)
- __tests__/cpp-free-function-overloads.test.ts (not served here)
- __tests__/cpp-global-alias-target.test.ts (not served here)
- __tests__/cpp-global-scope-names.test.ts (not served here)
- __tests__/cpp-gtest-fixture-scope.test.ts (not served here)
- __tests__/cpp-implicit-this-calls.test.ts (not served here)
- __tests__/cpp-included-test-suite.test.ts (not served here)
- __tests__/cpp-iterator-arrow.test.ts (not served here)
- __tests__/cpp-library-type-receiver.test.ts (not served here)
- __tests__/cpp-listed-declarator-receiver.test.ts (not served here)
- __tests__/cpp-local-callable-calls.test.ts (not served here)
- __tests__/cpp-lookup-memos.test.ts (not served here)
- __tests__/cpp-macro-and-constructor-calls.test.ts (not served here)
- __tests__/cpp-macro-defined-methods.test.ts (not served here)
- __tests__/cpp-macro-include-walk.test.ts (not served here)
- __tests__/cpp-macro-namespace.test.ts (not served here)
- __tests__/cpp-map-iterator-calls.test.ts (not served here)
- __tests__/cpp-member-chain-base-members.test.ts (not served here)
- __tests__/cpp-member-chain-outside-calls.test.ts (not served here)
- __tests__/cpp-member-chain-project-calls.test.ts (not served here)
- __tests__/cpp-member-receiver.test.ts (not served here)
- __tests__/cpp-member-template-returns.test.ts (not served here)
- __tests__/cpp-namespace-qualifier.test.ts (not served here)
- __tests__/cpp-nested-template-receiver.test.ts (not served here)
- __tests__/cpp-nullptr-deleted-overloads.test.ts (not served here)
- __tests__/cpp-operator-arrow-targets.test.ts (not served here)
- __tests__/cpp-outside-type-receiver.test.ts (not served here)
- __tests__/cpp-overload-argument-reentry.test.ts (not served here)
- __tests__/cpp-overload-argument-types.test.ts (not served here)
- __tests__/cpp-overload-choice.test.ts (not served here)
- __tests__/cpp-overload-constness.test.ts (not served here)
- __tests__/cpp-pimpl-member-chains.test.ts (not served here)
- __tests__/cpp-qualified-declarator-receiver.test.ts (not served here)
- __tests__/cpp-range-for-receiver.test.ts (not served here)
- __tests__/cpp-raw-string-delimiter-haserror.test.ts (not served here)
- __tests__/cpp-raw-string-preparse-1505.test.ts (not served here)
- __tests__/cpp-raw-strings.test.ts (not served here)
- __tests__/cpp-receiver-global-far-library.test.ts (not served here)
- __tests__/cpp-smart-pointer-receiver.test.ts (not served here)
- __tests__/cpp-specialization-names.test.ts (not served here)
- __tests__/cpp-specialization-qualifiers.test.ts (not served here)
- __tests__/cpp-string-literal-declarations.test.ts (not served here)
- __tests__/cpp-template-member-types.test.ts (not served here)
- __tests__/cpp-test-fixture-call.test.ts (not served here)
- __tests__/cpp-test-translation-unit.test.ts (not served here)
- __tests__/cpp-translation-unit-visibility.test.ts (not served here)
- __tests__/cpp-translation-unit.test.ts (not served here)
- __tests__/cpp-two-line-receivers.test.ts (not served here)
- __tests__/cpp-type-alias-receiver.test.ts (not served here)
- __tests__/cpp-undeclared-qualifier.test.ts (not served here)
- __tests__/cpp-written-scope-class-names.test.ts (not served here)
- __tests__/cross-file-visibility.test.ts (not served here)
- __tests__/cross-language-resolution.test.ts (not served here)
- __tests__/csharp-bare-member-scope.test.ts (not served here)
- __tests__/csharp-declared-receivers.test.ts (not served here)
- __tests__/csharp-member-initializers.test.ts (not served here)
- __tests__/csharp-namespace-scope.test.ts (not served here)
- __tests__/csharp-namespace-visibility.test.ts (not served here)
- __tests__/csharp-property-accessors.test.ts (not served here)
- __tests__/csharp-type-position-refs.test.ts (not served here)
- __tests__/curried-wrapper-handlers.test.ts (not served here)
- __tests__/daemon-attach-log.test.ts (not served here)
- __tests__/daemon-bind-failure.test.ts (not served here)
- __tests__/daemon-candidates.ts (not served here)
- __tests__/daemon-client-liveness.test.ts (not served here)
- __tests__/daemon-install-check.test.ts (not served here)
- __tests__/daemon-lock-holder.test.ts (not served here)
- __tests__/daemon-lock-refresh.test.ts (not served here)
- __tests__/daemon-manager.test.ts (not served here)
- __tests__/daemon-older-version.test.ts (not served here)
- __tests__/daemon-pid-reuse.test.ts (not served here)
- __tests__/daemon-registry.test.ts (not served here)
- __tests__/daemon-rendezvous-case.test.ts (not served here)
- __tests__/daemon-silent-holder.test.ts (not served here)
- __tests__/daemon-socket-fallback.test.ts (not served here)
- __tests__/dart-annotated-member-docs.test.ts (not served here)
- __tests__/dart-annotation-comments.test.ts (not served here)
- __tests__/dart-annotation-targets.test.ts (not served here)
- __tests__/dart-bare-call-scope.test.ts (not served here)
- __tests__/dart-bodiless-member-docs.test.ts (not served here)
- __tests__/dart-chain-links.test.ts (not served here)
- __tests__/dart-comment-split-chains.test.ts (not served here)
- __tests__/dart-const-constructors.test.ts (not served here)
- __tests__/dart-extension-receiver.test.ts (not served here)
- __tests__/dart-extension-type.test.ts (not served here)
- __tests__/dart-generic-calls.test.ts (not served here)
- __tests__/dart-generic-factory-chain.test.ts (not served here)
- __tests__/dart-getter-reads.test.ts (not served here)
- __tests__/dart-import-prefix-calls.test.ts (not served here)
- __tests__/dart-import-uris.test.ts (not served here)
- __tests__/dart-initializer-calls.test.ts (not served here)
- __tests__/dart-library-visibility.test.ts (not served here)
- __tests__/dart-local-call-scope.test.ts (not served here)
- __tests__/dart-part-directives.test.ts (not served here)
- __tests__/dart-part-privacy.test.ts (not served here)
- __tests__/dart-type-positions.test.ts (not served here)
- __tests__/dart-typedef-refs.test.ts (not served here)
- __tests__/dart-unnamed-extension.test.ts (not served here)
- __tests__/db-perf.test.ts (not served here)
- __tests__/db-reopen-on-replace.test.ts (not served here)
- __tests__/dead-code.test.ts (not served here)
- __tests__/declared-member-receiver.test.ts (not served here)
- __tests__/default-export-declaration.test.ts (not served here)
- __tests__/deprioritize-config.test.ts (not served here)
- __tests__/destructured-call-result.test.ts (not served here)
- __tests__/dominant-file-cache.test.ts (not served here)
- __tests__/dominant-file-plan.test.ts (not served here)
- __tests__/dominant-file-window.test.ts (not served here)
- __tests__/dotdot-folder-names.test.ts (not served here)
- __tests__/drupal-services.test.ts (not served here)
- __tests__/drupal.test.ts (not served here)
- __tests__/dynamic-boundaries.test.ts (not served here)
- __tests__/dynamic-import-binding.test.ts (not served here)
- __tests__/enclosing-class-member-call.test.ts (not served here)
- __tests__/enum-constant-bodies.test.ts (not served here)
- __tests__/erlang-arity-resolution.test.ts (not served here)
- __tests__/erlang-behaviour-synthesizer.test.ts (not served here)
- __tests__/esm-unexported-binding.test.ts (not served here)
- __tests__/evaluation/runner.ts (not served here)
- __tests__/evaluation/scoring.ts (not served here)
- __tests__/evaluation/test-cases.ts (not served here)
- __tests__/evaluation/types.ts (not served here)
- __tests__/exact-name-binding.test.ts (not served here)
- __tests__/exclude-config.test.ts (not served here)
- __tests__/explore-allocation-1500.test.ts (not served here)
- __tests__/explore-allocation-e2e.test.ts (not served here)
- __tests__/explore-blast-radius.test.ts (not served here)
- __tests__/explore-cluster-starvation.test.ts (not served here)
- __tests__/explore-cobol-copybook.test.ts (not served here)
- __tests__/explore-completeness-note.test.ts (not served here)
- __tests__/explore-corroboration-ranking.test.ts (not served here)
- __tests__/explore-cross-call-dedup.test.ts (not served here)
- __tests__/explore-declaration-only.test.ts (not served here)
- __tests__/explore-diagnostics.test.ts (not served here)
- __tests__/explore-displacement-guard.test.ts (not served here)
- __tests__/explore-elided-symbol-names.test.ts (not served here)
- __tests__/explore-empty-diagnostics.test.ts (not served here)
- __tests__/explore-exact-target.test.ts (not served here)
- __tests__/explore-factory-closure.test.ts (not served here)
- __tests__/explore-isolated-named-cluster.test.ts (not served here)
- __tests__/explore-long-query.test.ts (not served here)
- __tests__/explore-low-value-tests.test.ts (not served here)
- __tests__/explore-merged-spine-cluster.test.ts (not served here)
- __tests__/explore-named-file-valve.test.ts (not served here)
- __tests__/explore-named-gap-budget.test.ts (not served here)
- __tests__/explore-named-symbol-render.test.ts (not served here)
- __tests__/explore-nl-stopword-collision.test.ts (not served here)
- __tests__/explore-output-budget.test.ts (not served here)
- __tests__/explore-oversize-member.test.ts (not served here)
- __tests__/explore-path-pinning.test.ts (not served here)
- __tests__/explore-pinned-allocation.test.ts (not served here)
- __tests__/explore-pinned-cap-named.test.ts (not served here)
- __tests__/explore-proportional-allocation.test.ts (not served here)
- __tests__/explore-queried-oversize-symbol.test.ts (not served here)
- __tests__/explore-relevance-scoring.test.ts (not served here)
- __tests__/explore-reservation-invariant.test.ts (not served here)
- __tests__/explore-result-count.test.ts (not served here)
- __tests__/explore-same-basename-pin.test.ts (not served here)
- __tests__/explore-session-state.test.ts (not served here)
- __tests__/explore-synth-constant-endpoints.test.ts (not served here)
- __tests__/explore-synth-link-endpoints.test.ts (not served here)
- __tests__/explore-unindexed-names.test.ts (not served here)
- __tests__/expo-modules.test.ts (not served here)
- __tests__/expo-router.test.ts (not served here)
- __tests__/express-heuristic-same-file.test.ts (not served here)
- __tests__/express-inline-function-handler.test.ts (not served here)
- __tests__/expression-receiver-calls.test.ts (not served here)
- __tests__/extension-mapping.test.ts (not served here)
- __tests__/external-declared-receiver.test.ts (not served here)
- __tests__/external-type-receiver.test.ts (not served here)
- __tests__/extraction-old-git.test.ts (not served here)
- __tests__/extraction.test.ts (not served here)
- __tests__/fabric-view.test.ts (not served here)
- __tests__/fastendpoints-routes.test.ts (not served here)
- __tests__/fatal-handler.test.ts (not served here)
- __tests__/field-channel-scope.test.ts (not served here)
- __tests__/field-name-retrieval.test.ts (not served here)
- __tests__/file-cycles.test.ts (not served here)
- __tests__/file-routed-page-component.test.ts (not served here)
- __tests__/filelock-stale-takeover.test.ts (not served here)
- __tests__/fixtures/ambient-decls-ts/package.json (not served here)
- __tests__/fixtures/ambient-decls-ts/src/lib/bucket.ts (not served here)
- __tests__/fixtures/ambient-decls-ts/src/lib/queue.ts (not served here)
- __tests__/fixtures/ambient-decls-ts/src/lib/request.ts (not served here)
- __tests__/fixtures/ambient-decls-ts/src/routes/upload.ts (not served here)
- __tests__/fixtures/ambient-decls-ts/src/storage/metadata.ts (not served here)
- __tests__/fixtures/ambient-decls-ts/src/storage/stream.ts (not served here)
- __tests__/fixtures/ambient-decls-ts/src/storage/types.ts (not served here)
- __tests__/fixtures/ambient-decls-ts/types/platform-shims.d.ts (not served here)
- __tests__/fixtures/ambient-decls-ts/types/worker-configuration.d.ts (not served here)
- __tests__/fixtures/dense-header-ts/package.json (not served here)
- __tests__/fixtures/dense-header-ts/src/core/queue.ts (not served here)
- __tests__/fixtures/dense-header-ts/src/core/request-builder.ts (not served here)
- __tests__/fixtures/dense-header-ts/src/core/task-factory.ts (not served here)
- __tests__/fixtures/dense-header-ts/src/core/types.ts (not served here)
- __tests__/fixtures/dense-header-ts/src/index.ts (not served here)
- __tests__/fixtures/dense-header-ts/src/net/session.ts (not served here)
- __tests__/fixtures/displacement-ts/package.json (not served here)
- __tests__/fixtures/displacement-ts/src/index.ts (not served here)
- __tests__/fixtures/displacement-ts/src/pipeline/enrich.ts (not served here)
- __tests__/fixtures/displacement-ts/src/pipeline/ingest.ts (not served here)
- __tests__/fixtures/displacement-ts/src/pipeline/normalize.ts (not served here)
- __tests__/fixtures/displacement-ts/src/pipeline/publish.ts (not served here)
- __tests__/fixtures/displacement-ts/src/pipeline/sink.ts (not served here)
- __tests__/fixtures/displacement-ts/src/pipeline/types.ts (not served here)
- __tests__/fixtures/explore-path-pinning/package.json (not served here)
- __tests__/fixtures/explore-path-pinning/src/lib/background-image-table.ts (not served here)
- __tests__/fixtures/explore-path-pinning/src/lib/background-store.ts (not served here)
- __tests__/fixtures/explore-path-pinning/src/lib/runs-store.ts (not served here)
- __tests__/fixtures/explore-path-pinning/src/routes/(protected)/chat-window/+page.ts (not served here)
- __tests__/fixtures/explore-path-pinning/src/routes/m/projects/[id]/runs/[runId]/+page.ts (not served here)
- __tests__/fixtures/factory-closure-ts/package.json (not served here)
- __tests__/fixtures/factory-closure-ts/src/index.ts (not served here)
- __tests__/fixtures/factory-closure-ts/src/lib/http.ts (not served here)
- __tests__/fixtures/factory-closure-ts/src/lib/metrics.ts (not served here)
- __tests__/fixtures/factory-closure-ts/src/services/filter-parser.ts (not served here)
- __tests__/fixtures/factory-closure-ts/src/services/metric-service.ts (not served here)
- __tests__/fixtures/factory-closure-ts/src/stores/alerts-store.ts (not served here)
- __tests__/fixtures/factory-closure-ts/src/stores/dashboard-store.ts (not served here)
- __tests__/fixtures/factory-closure-ts/src/stores/session-store.ts (not served here)
- __tests__/fixtures/factory-closure-ts/src/stores/types.ts (not served here)
- __tests__/fixtures/factory-closure-ts/src/ui/panel.ts (not served here)
- __tests__/fixtures/filelock-contender.cjs (not served here)
- __tests__/fixtures/kernel-parity/ScalaCompanions.scala (not served here)
- __tests__/fixtures/kernel-parity/Torture.cs (not served here)
- __tests__/fixtures/kernel-parity/Torture.java (not served here)
- __tests__/fixtures/kernel-parity/TortureAnnotatedComments.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureAnnotatedDocs.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureCommentChains.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureConstCtors.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureCtors.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureDeclarationDocs.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureDirectives.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureDocs.scala (not served here)
- __tests__/fixtures/kernel-parity/TortureDoubleWalk.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureExt.scala (not served here)
- __tests__/fixtures/kernel-parity/TortureFileScoped.cs (not served here)
- __tests__/fixtures/kernel-parity/TortureFnref.scala (not served here)
- __tests__/fixtures/kernel-parity/TortureFnrefDart.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureGenericCalls.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureGiven.scala (not served here)
- __tests__/fixtures/kernel-parity/TortureHtml.php (not served here)
- __tests__/fixtures/kernel-parity/TortureIndent.scala (not served here)
- __tests__/fixtures/kernel-parity/TortureInitializers.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureMini.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureMisc.scala (not served here)
- __tests__/fixtures/kernel-parity/TortureModule.module (not served here)
- __tests__/fixtures/kernel-parity/TorturePartOfName.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureReadsTypes.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureRecord.java (not served here)
- __tests__/fixtures/kernel-parity/TortureScript.kts (not served here)
- __tests__/fixtures/kernel-parity/TortureScript.sc (not served here)
- __tests__/fixtures/kernel-parity/TortureSigs.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureTopLevel.cs (not served here)
- __tests__/fixtures/kernel-parity/TortureTry.swift (not served here)
- __tests__/fixtures/kernel-parity/TortureTypedefs.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureVref.scala (not served here)
- __tests__/fixtures/kernel-parity/TortureVrefDart.dart (not served here)
- __tests__/fixtures/kernel-parity/TortureWrappedCalls.swift (not served here)
- __tests__/fixtures/kernel-parity/docstrings.py (not served here)
- __tests__/fixtures/kernel-parity/torture-macros-ctors.cpp (not served here)
- __tests__/fixtures/kernel-parity/torture-macros.c (not served here)
- __tests__/fixtures/kernel-parity/torture.R (not served here)
- __tests__/fixtures/kernel-parity/torture.c (not served here)
- __tests__/fixtures/kernel-parity/torture.cpp (not served here)
- __tests__/fixtures/kernel-parity/torture.dart (not served here)
- __tests__/fixtures/kernel-parity/torture.go (not served here)
- __tests__/fixtures/kernel-parity/torture.hpp (not served here)
- __tests__/fixtures/kernel-parity/torture.js (not served here)
- __tests__/fixtures/kernel-parity/torture.kt (not served here)
- __tests__/fixtures/kernel-parity/torture.lua (not served here)
- __tests__/fixtures/kernel-parity/torture.luau (not served here)
- __tests__/fixtures/kernel-parity/torture.php (not served here)
- __tests__/fixtures/kernel-parity/torture.py (not served here)
- __tests__/fixtures/kernel-parity/torture.rb (not served here)
- __tests__/fixtures/kernel-parity/torture.rs (not served here)
- __tests__/fixtures/kernel-parity/torture.scala (not served here)
- __tests__/fixtures/kernel-parity/torture.swift (not served here)
- __tests__/fixtures/kernel-parity/torture.tsx (not served here)
- __tests__/fixtures/older-daemon.cjs (not served here)
- __tests__/fixtures/oversize-member-ts/package.json (not served here)
- __tests__/fixtures/oversize-member-ts/src/index.ts (not served here)
- __tests__/fixtures/oversize-member-ts/src/report/format.ts (not served here)
- __tests__/fixtures/oversize-member-ts/src/report/monthly.ts (not served here)
- __tests__/fixtures/oversize-member-ts/src/report/quarterly.ts (not served here)
- __tests__/fixtures/oversize-member-ts/src/report/store.ts (not served here)
- __tests__/fixtures/oversize-member-ts/src/report/types.ts (not served here)
- __tests__/fixtures/oversize-member-ts/src/report/weekly.ts (not served here)
- __tests__/fixtures/payroll-go/README.md (not served here)
- __tests__/fixtures/payroll-go/cmd/payrolld/main.go (not served here)
- __tests__/fixtures/payroll-go/go.mod (not served here)
- __tests__/fixtures/payroll-go/internal/domain/payroll/payslip.go (not served here)
- __tests__/fixtures/payroll-go/internal/gen/fkit/employee/contract.go (not served here)
- __tests__/fixtures/payroll-go/internal/gen/fkit/employee/employee.go (not served here)
- __tests__/fixtures/payroll-go/internal/gen/fkit/payroll/calculate.go (not served here)
- __tests__/fixtures/payroll-go/internal/gen/fkit/payroll/dto.go (not served here)
- __tests__/fixtures/payroll-go/internal/gen/fkit/payroll/payroll_cycle.go (not served here)
- __tests__/fixtures/payroll-go/internal/gen/fkit/payroll/payslip.go (not served here)
- __tests__/fixtures/payroll-go/internal/gen/fkit/payroll/store.go (not served here)
- __tests__/fixtures/payroll-go/internal/gen/fkit/timesheet/timesheet.go (not served here)
- __tests__/fixtures/payroll-go/internal/gen/payrollpb/payroll.pb.go (not served here)
- __tests__/fixtures/payroll-go/internal/gen/payrollpb/payroll_grpc.pb.go (not served here)
- __tests__/fixtures/payroll-go/internal/platform/clock/clock.go (not served here)
- __tests__/fixtures/payroll-go/internal/store/payslipstore/store.go (not served here)
- __tests__/fixtures/payroll-go/internal/transport/httpapi/payroll_handler.go (not served here)
- __tests__/fixtures/payroll-go/internal/transport/httpapi/router.go (not served here)
- __tests__/fixtures/payroll-go/internal/usecase/payroll/cycle.go (not served here)
- __tests__/fixtures/payroll-go/internal/usecase/payroll/payslip_builder.go (not served here)
- __tests__/fixtures/payroll-go/internal/usecase/payroll/prorate.go (not served here)
- __tests__/fixtures/php-import-alias-static/app/Http/Controllers/Backend/SettleController.php (not served here)
- __tests__/fixtures/php-import-alias-static/app/Repositories/SettleRepository.php (not served here)
- __tests__/fixtures/php-import-alias-static/app/Services/SettleService.php (not served here)
- __tests__/fixtures/record-detached-spawns.cjs (not served here)
- __tests__/fixtures/starved-cluster-ts/package.json (not served here)
- __tests__/fixtures/starved-cluster-ts/src/app/client.ts (not served here)
- __tests__/fixtures/starved-cluster-ts/src/app/config.ts (not served here)
- __tests__/fixtures/starved-cluster-ts/src/index.ts (not served here)
- __tests__/fixtures/starved-cluster-ts/src/pipeline/chain.ts (not served here)
- __tests__/fixtures/starved-cluster-ts/src/pipeline/framing.ts (not served here)
- __tests__/fixtures/starved-cluster-ts/src/pipeline/interceptors.ts (not served here)
- __tests__/fixtures/starved-cluster-ts/src/pipeline/types.ts (not served here)
- __tests__/fixtures/starved-cluster-ts/src/transport/socket.ts (not served here)
- __tests__/fixtures/tail-render-ts/README.md (not served here)
- __tests__/fixtures/tail-render-ts/package.json (not served here)
- __tests__/fixtures/tail-render-ts/src/components/ChatComposer.ts (not served here)
- __tests__/fixtures/tail-render-ts/src/lib/message-builder.ts (not served here)
- __tests__/fixtures/tail-render-ts/src/lib/session-store.ts (not served here)
- __tests__/fixtures/tail-render-ts/src/lib/socket.ts (not served here)
- __tests__/fixtures/tail-render-ts/types/worker-configuration.d.ts (not served here)
- __tests__/flask-url-rules.test.ts (not served here)
- __tests__/flow-directed-exact-names.test.ts (not served here)
- __tests__/flow-js.test.ts (not served here)
- __tests__/flow-library-globals.test.ts (not served here)
- __tests__/flow-named-implementation.test.ts (not served here)
- __tests__/flow-named-longer-route.test.ts (not served here)
- __tests__/foundation-enum-macros.test.ts (not served here)
- __tests__/foundation.test.ts (not served here)
- __tests__/framework-edge-name.test.ts (not served here)
- __tests__/framework-hint-resolver-only.test.ts (not served here)
- __tests__/framework-name-heuristic-scope.test.ts (not served here)
- __tests__/framework-resolve-language.test.ts (not served here)
- __tests__/frameworks-integration.test.ts (not served here)
- __tests__/frameworks.test.ts (not served here)
- __tests__/frontload-hook.test.ts (not served here)
- __tests__/fts5-fallback.test.ts (not served here)
- __tests__/function-ref-package-imports.test.ts (not served here)
- __tests__/function-ref-project-imports.test.ts (not served here)
- __tests__/function-ref.test.ts (not served here)
- __tests__/fuzzy-case-sensitivity.test.ts (not served here)
- __tests__/fuzzy-lexical-reach.test.ts (not served here)
- __tests__/generated-detection.test.ts (not served here)
- __tests__/generated-flag-index.test.ts (not served here)
- __tests__/generic-supertypes.test.ts (not served here)
- __tests__/gin-middleware-chain.test.ts (not served here)
- __tests__/git-changed-untracked-dir.test.ts (not served here)
- __tests__/git-hooks.test.ts (not served here)
- __tests__/git-ignored-dirs-scope.test.ts (not served here)
- __tests__/git-index-currency.test.ts (not served here)
- __tests__/git-status-subdir-project.test.ts (not served here)
- __tests__/gitignore-pattern-parity.test.ts (not served here)
- __tests__/global-setup-dist.test.ts (not served here)
- __tests__/global-setup-dist.ts (not served here)
- __tests__/global-setup-home-sandbox.ts (not served here)
- __tests__/glyphs.test.ts (not served here)
- __tests__/go-asserted-locals.test.ts (not served here)
- __tests__/go-bare-call-no-method.test.ts (not served here)
- __tests__/go-build-constraint-twins.test.ts (not served here)
- __tests__/go-chained-receivers.test.ts (not served here)
- __tests__/go-conversion-references.test.ts (not served here)
- __tests__/go-declared-receiver.test.ts (not served here)
- __tests__/go-defined-type-refs.test.ts (not served here)
- __tests__/go-detached-doc-comments.test.ts (not served here)
- __tests__/go-doc-directives.test.ts (not served here)
- __tests__/go-dot-import-field-type.test.ts (not served here)
- __tests__/go-embedding-no-override.test.ts (not served here)
- __tests__/go-external-package-qualifier.test.ts (not served here)
- __tests__/go-external-test-package.test.ts (not served here)
- __tests__/go-framework-own-package.test.ts (not served here)
- __tests__/go-func-field-dispatch.test.ts (not served here)
- __tests__/go-grouped-const-docs.test.ts (not served here)
- __tests__/go-implements-arity.test.ts (not served here)
- __tests__/go-implements-defined-types.test.ts (not served here)
- __tests__/go-implements-embedding.test.ts (not served here)
- __tests__/go-implements-stdlib.test.ts (not served here)
- __tests__/go-implements-types.test.ts (not served here)
- __tests__/go-implements-unexported.test.ts (not served here)
- __tests__/go-import-alias-precedence.test.ts (not served here)
- codegraph-kernel/grammars/dart/LICENSE
- codegraph-kernel/grammars/lua/LICENSE
- codegraph-kernel/grammars/scala/LICENSE
- codegraph-kernel/grammars/swift/LICENSE
- docs/AGENTS.md
- docs/README.md
- docs/SEARCH_QUALITY_LOOP.md
- docs/viewer-launch-changelog.md
Advantages
- Reduces token usage and tool calls by providing surgical, pre-indexed code context. evidence: README.md
- Operates 100% locally with no external dependencies or cloud data transmission. evidence: README.md
- Maintains an up-to-date index automatically through file-system watching. evidence: README.md
Limitations
- Requires an initial indexing step per project which may take time for very large codebases. evidence: README.md
- Increases the persistent context window footprint in long-running agent sessions. evidence: README.md
Security
Risk level: High risk
This brain runs
Agent instructions that run or fetch something (75)
.claude/skills/add-lang/SKILL.md:Run from the codegraph repo root..claude/skills/add-lang/SKILL.md:node scripts/add-lang/check-grammar.mjs <lang> path/to/valid-sample.<ext>.claude/skills/add-lang/SKILL.md:npm pack @tree-sitter-grammars/tree-sitter-<lang> # often ships a prebuilt *.wasm.claude/skills/add-lang/SKILL.md:then add the token to the vendored branch in Step 4 and re-run check-grammar on the vendored path until it PASSes..claude/skills/add-lang/SKILL.md:or `curl` a raw file from a known repo), then:· uses the network.claude/skills/add-lang/SKILL.md:node scripts/add-lang/dump-ast.mjs <lang> path/to/sample.<ext>.claude/skills/add-lang/SKILL.md:node scripts/add-lang/dump-ast.mjs src/extraction/wasm/tree-sitter-<lang>.wasm sample.<ext>.claude/skills/add-lang/SKILL.md:npm run build # tsc + copy-assets (copies any vendored *.wasm into dist/).claude/skills/add-lang/SKILL.md:node scripts/add-lang/verify-extraction.mjs <sample-repo> <lang>.claude/skills/add-lang/SKILL.md:On FAIL or a thin WARN: re-run `dump-ast.mjs` on a richer file, fix the mappings in `<lang>.ts`, `npm run build`, re-index, re-verify..claude/skills/add-lang/SKILL.md:npx vitest run __tests__/extraction.test.ts.claude/skills/add-lang/SKILL.md:npm run build && ./scripts/local-install.sh.claude/skills/add-lang/SKILL.md:`bench.sh` clones (shared `/tmp/codegraph-corpus`), wipes + indexes, runs `verify-extraction.mjs`, then the with/without retrieval A/B via `scripts/agent-eval/run-all.sh` (skips the paid A/B if extraction is broken)..claude/skills/add-lang/SKILL.md:Read each `parse-run.mjs` summary printed by `run-all.sh`: tool calls, file `Read`s, Grep/Bash, codegraph-tool calls, duration, and **cost** — for both the `with` and `without` arms..claude/skills/add-lang/SKILL.md:After the loop, restore the dev link if needed: `./scripts/local-install.sh`..claude/skills/add-lang/SKILL.md:Any new `*.wasm` must live in `src/extraction/wasm/` — `copy-assets` (run by `npm run build`) ships it;.claude/skills/agent-eval/SKILL.md:Run from the codegraph repo root..claude/skills/agent-eval/SKILL.md:- [ ] 5. Run audit.sh in the background.claude/skills/agent-eval/SKILL.md:**Step 4 — harness.** Ask with `AskUserQuestion` which harness to run, and map the answer to a MODE token:.claude/skills/agent-eval/SKILL.md:**Step 5 — run.** Launch in the background (sets the version, clones if missing, wipes + re-indexes, runs the chosen arms — several minutes):.claude/skills/agent-eval/SKILL.md:Headless (`parse-run.mjs`): total tool calls, file `Read`s, Grep/Bash, codegraph-tool calls, duration, **total cost**..claude/skills/agent-eval/SKILL.md:The index is rebuilt every run (`audit.sh` wipes `.codegraph`) — different versions extract differently, so an index must be served by the same binary that built it..claude/skills/agent-eval/SKILL.md:`audit.sh` temporarily mutates the global `codegraph` install for the test, then restores your dev link via `local-install.sh`..cursor/rules/codegraph.mdc:Indexing is the user's decision: tell them they can run `codegraph init` to build the index, but don't run it yourself.AGENTS.md:npm run build # tsc + copy schema.sql and *.wasm + build the viewer into dist/; chmods dist/bin/codegraph.jsAGENTS.md:npm run build:lib # the viewer's components as @colbymchenry/codegraph-ui (ui/dist) — NOT part of `build`AGENTS.md:npm run dev # tsc --watchAGENTS.md:npm run clean # rm -rf distAGENTS.md:npm test # vitest run (all)AGENTS.md:npm run test:watchAGENTS.md:npm run test:eval # only __tests__/evaluation/AGENTS.md:npm run eval # build then run __tests__/evaluation/runner.ts via tsxAGENTS.md:npm run cli # build then run the local dist binaryAGENTS.md:npx vitest run __tests__/installer-targets.test.tsAGENTS.md:npx vitest run __tests__/extraction.test.ts -t "TypeScript"AGENTS.md:`npm run build:lib` is separate and does NOT run as part of `npm run build`: it compiles the same `ui/src` tree a second way, with `svelte-package`, into `ui/dist` — the `@colbymchenry/codegraph-ui` component library the Pro app imports (task CG-61).AGENTS.md:Tests run as **two Vitest projects** (`test.projects` in `vitest.config.mts`): `engine` (node) and `ui` (jsdom, the Svelte plugin, `resolve.conditions: ['browser']`) for the single `__tests__/ui-package.test.ts`.AGENTS.md:`npm test` still runs both.AGENTS.md:That range is what users run.AGENTS.md:Working in this repo needs Node 22.12 or newer: `devEngines` makes `npm ci` and `npm run` stop on anything older, for Vite 7 and Vitest 4.AGENTS.md:Subcommands: `install`, `uninstall`, `init`, `uninit`, `index`, `sync`, `status`, `unlock`, `query`, `explore`, `context`, `node`, `files`, `callers`, `callees`, `impact`, `affected`, `cycles`, `daemon`, `telemetry`, `upgrade`, `version`.AGENTS.md:`src/installer/` is the entry point for `codegraph install` (and the bare `codegraph`/`npx @colbymchenry/codegraph` invocation).AGENTS.md:An entry already in the file is the user's: agents write switches into it and users pin a `command` or `cwd`, so a re-install or `--refresh` only fills in missing keys.AGENTS.md:Edits are surgical via `jsonc-parser` so user comments and formatting survive install/re-install/uninstall round-trips.AGENTS.md:a pre-#1698 `mcp.codegraph` + `enabled` entry is migrated on re-install and removed by uninstall.AGENTS.md:their `install` only strips one a previous install left behind.AGENTS.md:All installer changes need matching coverage in `__tests__/installer-targets.test.ts` — there are ~47 parameterized contract tests covering install idempotency, sibling preservation, uninstall reverses install, byte-equal re-runs returning `unchanged`, and partial-state recovery for Codex.AGENTS.md:Every expected/recoverable condition — project not indexed, symbol not found, file not in the index — returns a **SUCCESS-shaped response carrying the guidance** (`NotIndexedError` → `textResult`, see `ToolHandler.execute`'s catch).AGENTS.md:deterministic probes (`scripts/agent-eval/probe-*.mjs`) then agent A/B (`scripts/agent-eval/run-all.sh` / `ab-new-vs-baseline.sh`).AGENTS.md:run via `npm run eval` (builds first).AGENTS.md:Not part of `npm test`.AGENTS.md:The dev machine — and the default `npm test` target — is **macOS**, so local runs cover the macOS path.AGENTS.md:Build a throwaway image from the repo and run the suite inside it:AGENTS.md:`RUN npm ci && npm run build`.AGENTS.md:Run with **`docker run --rm --init`**.AGENTS.md:Connect / run from the Mac host: `ssh <user>@<guest_ip> "..."`.· uses the networkAGENTS.md:ssh [email protected] "powershell -NoProfile -ExecutionPolicy Bypass -Command -" <<'PS'· uses the networkAGENTS.md:Clone fresh into a **Windows-local** path (`C:\dev\codegraph`) and `npm ci` there — never run npm against the shared Mac repo, since `esbuild`/`rollup` ship platform-specific binaries.AGENTS.md:Fetch a contributor PR head straight from their fork to dodge `pull/<n>/head` lag: `git fetch <fork-url> <branch>` then `git checkout -f FETCH_HEAD`.· uses the networkAGENTS.md:**Keep:** language & framework names (Go, Spring, NestJS, …), things a user types or sets (`codegraph install`, `codegraph_explore`, the `CODEGRAPH_*` env vars), agent / IDE names (Claude Code, Cursor, opencode, Kiro, …), and a brief `Thanks @user` when a contributor is credited.AGENTS.md:Publishing manually is **wrong** now — a plain `npm publish` ships the root package (non-bundled), which breaks anyone on Node < 22.5.AGENTS.md:When the maintainer DOES bump the version, the only edit strictly required is to `package.json` — the workflow's "Sync package-lock.json" step detects a mismatch between `package.json` and `package-lock.json`, runs `npm install --package-lock-only --ignore-scripts` to rewrite the lock …[+120 chars]AGENTS.md:Once `package.json` is at the target version on `main`, trigger **Actions → Release → Run workflow** (on `main`).AGENTS.md:Any change to `src/installer/` (especially `targets/`) needs corresponding test coverage and a CHANGELOG entry — installer regressions break every new install silently.AGENTS.md:The **last main commit** — `git log --first-parent main -1 --format='%ai %h %s'`.docs/AGENTS.md:**Agent A/B** (`scripts/agent-eval/run-all.sh <repo> "<Q>"`): with vs without codegraph, **≥2 runs/arm** (run-to-run variance is large — never conclude from n=1).docs/AGENTS.md:**Every run also reports three feedback metrics** — residual context occupancy, explore sufficiency (what the agent did NEXT after each explore), and allocation efficiency (share of returned bytes the answer cited) — under each run, plus a side-by-side arm table (`compare-arms.mjs`).docs/AGENTS.md:Check the contamination row before believing any number: `CLI calls that RETURNED output` > 0 invalidates the run (in a new-vs-baseline A/B it silently drops calls from all three metrics, since a CLI explore is not a tool call).docs/AGENTS.md:(b) **Sonnet is the deliberate floor model** — codegraph's real users attach it to whatever agent they already run (Cursor Composer, Gemini, etc.), so we validate on a "dumber" model on purpose: a stronger model's tool-use covers up the salience/sufficiency problems a weaker one exposes.docs/AGENTS.md:**MCP attach is a startup-latency issue, not a hard block.** On a multi-step task the agent dives into Read/grep before codegraph finishes its ~2-3s startup (worse when the eval is itself run nested inside a Claude session, under CPU contention), so it runs with no codegraph.docs/AGENTS.md:judge by actual codegraph usage in `parse-run.mjs`'s `by type`.docs/AGENTS.md:To isolate a change — **new-build vs baseline-build, both codegraph-on** (vs run-all.sh's with-vs-without) — use `scripts/agent-eval/ab-new-vs-baseline.sh <indexed-repo> "<task>" [baseline-ref]` (it bakes in the pre-warm).docs/AGENTS.md:After steering flow questions to `codegraph_trace` first: **best run 0 Read / 0 Grep / 3 codegraph / 51s**;docs/AGENTS.md:Run-to-run variance is still real;docs/AGENTS.md:node count stable at 9,289;
MCP servers (1)
README.md(codegraph):codegraph serve --mcp
Install scripts (5)
README.md:curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh· uses the networkREADME.md:irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex· uses the networkREADME.md:- **You're on an old (pre-0.9) install.** Reinstall to get the bundled runtime — `curl -fsSL https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.sh | sh` (macOS/Linux), `irm https://raw.githubusercontent.com/colbymchenry/codegraph/main/install.ps1 | iex` (Windows), or …[+42 chars]· uses the networkinstall.ps1:pwsh install.ps1· uses the networkinstall.sh:sh install.sh· uses the network
Binaries (29)
src/extraction/wasm/tree-sitter-arkts.wasm:ships native binariessrc/extraction/wasm/tree-sitter-c.wasm:ships native binariessrc/extraction/wasm/tree-sitter-c_sharp.wasm:ships native binariessrc/extraction/wasm/tree-sitter-cfml.wasm:ships native binariessrc/extraction/wasm/tree-sitter-cfquery.wasm:ships native binariessrc/extraction/wasm/tree-sitter-cfscript.wasm:ships native binariessrc/extraction/wasm/tree-sitter-cobol.wasm:ships native binariessrc/extraction/wasm/tree-sitter-cpp.wasm:ships native binariessrc/extraction/wasm/tree-sitter-dart.wasm:ships native binariessrc/extraction/wasm/tree-sitter-erlang.wasm:ships native binariessrc/extraction/wasm/tree-sitter-go.wasm:ships native binariessrc/extraction/wasm/tree-sitter-java.wasm:ships native binariessrc/extraction/wasm/tree-sitter-javascript.wasm:ships native binariessrc/extraction/wasm/tree-sitter-kotlin.wasm:ships native binariessrc/extraction/wasm/tree-sitter-lua.wasm:ships native binariessrc/extraction/wasm/tree-sitter-luau.wasm:ships native binariessrc/extraction/wasm/tree-sitter-nix.wasm:ships native binariessrc/extraction/wasm/tree-sitter-pascal.wasm:ships native binariessrc/extraction/wasm/tree-sitter-php.wasm:ships native binariessrc/extraction/wasm/tree-sitter-python.wasm:ships native binariessrc/extraction/wasm/tree-sitter-r.wasm:ships native binariessrc/extraction/wasm/tree-sitter-ruby.wasm:ships native binariessrc/extraction/wasm/tree-sitter-rust.wasm:ships native binariessrc/extraction/wasm/tree-sitter-scala.wasm:ships native binariessrc/extraction/wasm/tree-sitter-swift.wasm:ships native binariessrc/extraction/wasm/tree-sitter-terraform.wasm:ships native binariessrc/extraction/wasm/tree-sitter-tsx.wasm:ships native binariessrc/extraction/wasm/tree-sitter-typescript.wasm:ships native binariessrc/extraction/wasm/tree-sitter-vbnet.wasm:ships native binaries
- High risk: runs-install-script-remote-exec (install.ps1)
- High risk: remote-exec (install.ps1:72)
- Medium risk: runs-instruction-instruction-network (.claude/skills/add-lang/SKILL.md:82)
- Medium risk: runs-instruction-instruction-network (AGENTS.md:209)
- Medium risk: runs-instruction-instruction-network (AGENTS.md:211)
- Medium risk: runs-instruction-instruction-network (AGENTS.md:218)
- Medium risk: runs-install-script-remote-exec (README.md:91)
- Medium risk: runs-install-script-remote-exec (README.md:94)
- Medium risk: runs-install-script-remote-exec (README.md:915)
- Medium risk: remote-exec-pipe (install.ps1:72)
- Medium risk: network-variable-host (install.ps1:146)
- Medium risk: runs-install-script-network-variable-host (install.sh)
- Medium risk: network-variable-host (install.sh:76)
- Low risk: scripts-present (__tests__/evaluation/runner.ts)
- Low risk: scripts-present (install.ps1)
- Low risk: scripts-present (install.sh)
- Low risk: scripts-present (scripts/add-lang/check-grammar.mjs)
- Low risk: scripts-present (scripts/add-lang/dump-ast.mjs)
- Low risk: scripts-present (scripts/add-lang/verify-extraction.mjs)
- Low risk: scripts-present (scripts/check-ui-build.mjs)
Attribution and licence
- First committed by
colbymchenry
- Publisher
- Colby Mchenry (Community)
- Source
- colbymchenry/codegraph · commit f72c9d2
- Licence
- MIT License (files served here)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Contributors
- colbymchenry, JosefAschauer, inth3shadows, maxmilian, GeneralClaw
AutoGineer is not affiliated with or endorsed by colbymchenry.
Install from source
493 of its files are not served here, so there is no ZIP. colbymchenry/codegraph at f72c9d2