colbymchenry/codegraph · v0.9.4 · TypeScript · MIT

CodeGraph · agent 的代码索引后端

为 Claude Code、Cursor、Codex、opencode、Hermes 预建的语义代码知识图谱——本地、确定性、零 API。一次 MCP 调用拿到入口点、相关符号、调用关系和源码。

20.6k
Stars (+2,456/周)
~35%
更省钱
57%
更少 token
71%
更少工具调用
20,651
Stars (+2,456/周)
~35%
更省钱
57%
更少 token
71%
更少工具调用
100%
本地运行

一句话定位:给 coding agent 装一个“预先建好的代码地图”。当 agent 需要理解代码时,不再 grep 满仓库,而是向 CodeGraph 的 MCP 服务发一次结构化查询,一个调用拿到入口点、相关符号、调用关系和源码片段。作者强调一个关键设计哲学——抽取过程是确定性的,源自 AST 而非 LLM 总结,因此结果稳定、可复现、可进 git。

分层流水线架构

CodeGraph 的核心是一条清晰的单向流水线,src/index.ts 里的 CodeGraph 类把各层串起来,对外暴露 init / indexAll / sync / searchNodes / getCallers / getImpactRadius / buildContext 等 API:

01
ExtractionOrchestratortree-sitter WASM 解析文件 → 抽取符号(node)与关系(edge)
↓
02
SQLite 存储nodes / edges / files / unresolved_refs + FTS5 全文索引
↓
03
ReferenceResolverimport 解析 · 名称匹配 · 框架模式 · 回调合成(“秘方”所在)
↓
04
GraphTraverser / QueryManagerBFS/DFS 遍历:callers、callees、影响半径、路径查找
↓
05
ContextBuilder → MCP Server组装 markdown/JSON 上下文,经 stdio 暴露给 agent

数据模型:22 种节点 × 12 种边

图的表达力来自精心设计的类型系统(src/types.ts)。节点(NodeKind)覆盖 22 种符号——从 function/method/class 到 interface/trait/route/component;边(EdgeKind)覆盖 12 种关系:

src/types.ts
// 12 种关系,每条边还带 provenance:'tree-sitter' | 'scip' | 'heuristic'
export type EdgeKind =
  'contains' | 'calls' | 'imports' | 'exports'
  | 'extends' | 'implements' | 'references' | 'type_of'
  | 'returns' | 'instantiates' | 'overrides' | 'decorates';

interface Edge {
  source: string; target: string; kind: EdgeKind;
  line?: number; provenance: Provenance;  // 谁推断出这条边
}

抽取层:19+ 语言的 WASM 解析→ 深度页

CodeGraph 用 web-tree-sitter + tree-sitter-wasms 跑 WASM 语法,不依赖任何原生编译,因此“一个二进制到处跑”。它对工程细节的打磨令人印象深刻:

  • 按需加载语法:只为项目里真实出现的语言编译 WASM grammar,控制内存。
  • Worker 每 250 文件回收一次(WORKER_RECYCLE_INTERVAL):因为 WASM 线性内存只增不减,唯一回收堆的办法就是杀掉 isolate 重建。
  • 1MB 文件上限 + 10s 解析超时:跳过压缩 bundle / vendored 大文件,避免撑爆 WASM 堆。
  • Git 感知发现文件:git ls-files --recurse-submodules,尊重 .gitignore 与子模块。
  • 对 Svelte / Vue / Liquid / Delphi 表单(.dfm)这类 tree-sitter 搞不定的格式,写了专用 extractor。

解析层:tree-sitter 搞不定的,才是真功夫→ 深度页

这是 CodeGraph 最有价值的部分。tree-sitter 只能告诉你“这里有个调用”,却无法跨文件解析“它到底调的是谁”,更无法理解框架的动态分发。ReferenceResolver 用一套分层策略来补这个洞:

  • ① 内置过滤:跳过 stdlib / 内建函数(JS 内建、React hooks、Python 内建、Go stdlib…)。
  • ② 框架模式:识别 14+ 框架(Express / NestJS / Django / Laravel / Rails / Spring / Gin / Axum / ASP.NET / Vapor / SvelteKit / Vue-Nuxt / Drupal…),把 URL 路由 route 节点 连到处理函数。这里用的是对“去注释后源码”的正则,而非完整 AST,省开销。
  • ③ import 解析:按语言扩展名顺序解析(TS 会试 .ts/.tsx/.d.ts/index.ts…),并读 tsconfig 的 paths 别名、Cargo workspace 成员。
  • ④ 名称匹配:精确名 → 限定名 → 文件路径三级匹配,用路径邻近度给候选打置信分。
  • ⑤ 回调合成(最妙):见下。
回调合成 callback-synthesizer:tree-sitter 永远追不到 emitter.on('mount', handler) 和 emitter.emit('mount') 之间的隐式调用。CodeGraph 主动“合成”这些边——它识别注册器(/^on[A-Z]|subscribe|addListener/)与派发器(/emit|trigger|notify|dispatch/),按字段名/事件字符串配对,凭空补出 React 重渲染、EventEmitter、Vue/Svelte 组合式的动态调用边,并打上 provenance:'heuristic' 标注合成来源与注册位置。

存储层:node:sqlite + FTS5→ 深度页

CodeGraph 直接用 Node 内置的 node:sqlite(DatabaseSync,真正的 SQLite 编进了 Node),外面包了一层 better-sqlite3 形状的接口。因为自带 Node runtime,所以 WAL、FTS5 全平台可用。它的 PRAGMA 调优很“数据库工程师”:

src/db/index.ts — 连接调优
db.pragma('journal_mode = WAL');      // 读不阻塞写,MCP+git hook+CLI 可并发
db.pragma('synchronous = NORMAL');    // 配合 WAL 安全
db.pragma('cache_size = -64000');     // 64MB page cache
db.pragma('mmap_size = 268435456');   // 256MB 内存映射 I/O
db.pragma('busy_timeout = 5000');      // 锁等待 5s,骑过正常 sync

schema 设计同样克制:edges 表只建 (source,kind) 与 (target,kind) 两个复合索引,靠左前缀扫描覆盖正反向查询,故意不建多余的单列索引以减轻写入负担;unresolved_refs 表把引用解析延后到抽取之后,避免抽取阶段 N+1 查符号表;nodes_fts 用 FTS5 虚拟表 + 触发器自动同步全文索引。

服务层:10 个 MCP 工具,与自适应预算

CodeGraph 通过 stdio 暴露 10 个 MCP 工具给 agent,每个都为“一次调用解决一类问题”而设计:

🔎
codegraph_context

给定一个任务描述,一次返回入口点 + 相关符号 + 关系 + 源码——替代昂贵的探索 agent。

🧭
codegraph_trace

追踪 from→to 的调用路径,包含动态分发的跳点,附带 provenance / 注册位置元数据。

🗺️
codegraph_explore

一次性勘察多个相关符号,按文件分组返回源码,受自适应输出预算约束。

💥
codegraph_impact

分析改动某符号的“影响半径”——多步 BFS 找出所有受牵连的代码。

📞
codegraph_callers / callees

谁调用了它 / 它调用了谁,支持多层深度,秒级返回。

⚡
codegraph_search

FTS5 驱动的全文符号搜索,带 kind 加权与路径相关性打分。

一个被低估的细节:explore 工具的自适应预算会随项目规模缩放——小项目单次给 18K 字符、大项目(≥25k 文件)给到 38K,每行还带 cat -n 式行号前缀,让 agent 能直接引用 file:line 而无需回读文件。还有一条硬不变量:单文件字符上限在任何档位都不能下降——这是被一个真实回归逼出来的(excalidraw 的 415KB App.tsx 曾因上限太低只返回不到 1% 内容,迫使 agent 退回去 Read)。

增量同步与安装器

FileWatcher 用原生 OS 事件(FSEvents / inotify / ReadDirectoryChangesW)+ 防抖,让图随写随更新;在 WSL2 /mnt 与网络盘上自动降级为 git hook(post-commit / merge / checkout)。安装器(@clack/prompts 交互式)会自动探测并配置 Claude Code、Cursor、Codex、opencode、Hermes——比如给 Cursor 注入 --path 解决其子进程 cwd 错误的问题。

实测收益(v0.9.4,7 个真实仓库中位数)

对比 Claude Opus 4.7 在“有/无 CodeGraph”下回答同一架构问题。增益随仓库规模放大:

Excalidraw (TS)
−90%
Tokio (Rust)
−86%
VS Code (TS)
−78%
Alamofire (Swift)
−64%
Django (Python)
−36%

↑ token 削减幅度。Excalidraw 案例:工具调用 79→3,耗时 2m58s→48s。

对 coding agent 生态的意义

CodeGraph 把“代码理解”从 每次会话临时探索 变成 一次索引、长期复用的基础设施。它选择 100% 本地 + 确定性抽取,意味着结果可复现、可进 git、无隐私顾虑——这让它有资格成为多种 agent 共享的“代码索引标准件”。它证明了一件事:agent 性能的下一个数量级,可能不在更大的模型,而在更好的检索基建。