CodeGraph
tree-sitter → SQLite + FTS5 · CLI + MCP · 无模型
一份索引,三个入口。 tree-sitter 把仓库解析成项目级的符号、调用与依赖索引,再针对它回答结构性问题:谁调用了这个函数、它能到达什么、改动它会牵连到哪里。这些答案来自一次完整的语法树解析,不是文本匹配的猜测,也不来自任何模型。
里面没有模型,所以输出是可复现的字节
这不是省下来的一个功能,而是整件事成立的前提。二进制里不含 embedding、不含向量索引、不含 LLM 调用,因此同一个仓库、同一个问题,在你的笔记本、同事的机器和 CI runner 上返回完全相同的字节。可复现,才谈得上下面这三件事:
- 交给编码 Agent 时,它拿到的是事实而不是一次采样;同一轮对话里问两次不会得到两个答案。
- 放进 CI 里可以 diff —— 影响半径变了就是代码结构变了,而不是模型今天心情不同。
- 出错时可以复现。一个确定性的管道,bug 有唯一的重现路径。
它解决什么问题
"改这个函数会碰到哪些地方" 这类问题,grep 答不了。grep 认字符串,不认调用关系:同名的方法、被重新导出的符号、经由 trait 或接口分发的调用,它一律看不见;反过来,注释和字符串里的同名文本它又一定会报出来。于是你回到人工翻文件,一层层往上追调用者,直到自己觉得追干净了 —— 这个"觉得"就是回归 bug 的来源。CodeGraph 把这层结构预先算好、落盘,然后用一次查询回答它。
在陌生仓库里改代码的人
接手一个几十万行的项目,需要在动手之前知道这个符号被谁用着、改了会波及哪里。先看半径,再决定改法。
编码 Agent
一次结构化查询就拿到相关符号的源码和它们之间的调用路径,替代几十轮 grep 加读文件 —— 上下文更准,token 更少,来回更短。这也是 MCP 入口存在的原因。
编辑器与 IDE
通过本机 HTTP 接同一份索引,不必各自再实现一套跨文件解析。远程开发场景下 HTTP 也是唯一可靠的通道。
CI 与代码评审
一次改动的影响半径可以被算出来、被 diff、被写进评审意见。确定性输出让"这次变更比上次多牵连了三个模块"成为一句可以验证的话。
它怎么工作
四个阶段,没有一步需要联网,也没有一步会把你的代码发出去。索引写在仓库自己的 .codegraph/ 目录里。
解析
tree-sitter 按语言逐文件构建语法树,提取符号定义、调用点与文件间依赖,并做跨文件符号解析 —— 这一步决定了后面所有答案的精度。
落盘
结果写进项目级的 SQLite 数据库,全文检索由 FTS5 承担。SQLite 静态链接进二进制,机器上不需要预装任何系统库。
查询
符号搜索、调用者、被调用者、依赖、变更影响半径,以及按 PageRank 中心度排序的全图导出。查询走索引,是亚毫秒级的读,不是重新扫一遍仓库。
跟随
后台守护进程监听文件变化(2 秒防抖)并增量更新索引,落后写入约一秒。同一个项目的多个客户端 —— 终端标签页、Agent、编辑器 —— 共用这一个守护进程;全部断开且空闲超时后它自行退出。
三个入口,同一份索引
索引只建一次。人、Agent 和编辑器分别从最顺手的那个口子进来,看到的是同一份数据,不存在"CLI 的答案和 Agent 的答案不一致"这种情况。
CLI
给人用
$ codegraph query "<symbol>" -p .基于 Clap 的子命令集:init、index、sync、query、files、status、callers、callees、impact、affected、check、export、unlock,外加 bash / zsh / fish / powershell / elvish 的补全安装。
MCP over stdio
给编码 Agent
$ codegraph serve --mcp标准 MCP stdio 服务。codegraph install --yes 会自动探测已安装的 Agent 与 IDE 并写好配置;codegraph skill install 还能把使用说明作为 Skill 装进它们各自的技能目录。
MCP over HTTP
给编辑器与远程开发
$ codegraph serve --http默认只监听 127.0.0.1:8111,不对外暴露。codegraph http list 查看在跑的实例,codegraph http stop <addr> 停掉其中一个。SSH 远程开发时这是推荐通道。
能问它什么
同一个问题,命令行和 MCP 两侧是同一份实现,答案一致。
| 问题 | 命令行 | MCP 工具 |
|---|---|---|
| 这个符号在哪里定义的? | codegraph query | codegraph_search |
| 这块代码是怎么工作的? | codegraph export | codegraph_explore |
| 把它的源码和调用链一起给我 | codegraph query --json | codegraph_node |
| 谁调用了它? | codegraph callers | codegraph_callers |
| 它调用了什么? | codegraph callees | codegraph_callees |
| 改动它会牵连到哪里? | codegraph impact | codegraph_impact |
| 这次改动影响了哪些文件? | codegraph affected | — |
| 索引建好了吗? | codegraph status | codegraph_status |
impact 给出的是传递闭包,不是一层调用者 —— 这也是它和手工往上翻调用链最主要的差别。export 支持按 PageRank 中心度排序输出全图,用来找一个陌生仓库里真正的枢纽。
安装与上手
一键脚本会探测平台、下载对应的预编译二进制并放到 PATH 上 —— 不需要 Rust 工具链,也不需要等编译。它没有发布到 crates.io,所以从 crates 索引走的那条 cargo 安装路径找不到它;有 Rust 环境时,用下面的 --git 从源码装。
# Linux 与 macOS
$ curl -fsSL https://raw.githubusercontent.com/sunerpy/codegraph-rust/main/scripts/install.sh | sh# Windows(PowerShell 5.1 及以上)
$ irm https://raw.githubusercontent.com/sunerpy/codegraph-rust/main/scripts/install.ps1 | iex# 或从源码 —— 未发布到 crates.io
$ cargo install --git https://github.com/sunerpy/codegraph-rust codegraph-rs# 建索引,写进 ./.codegraph/
$ codegraph init && codegraph index# 查一个符号
$ codegraph query "GraphTraverser" -p .# 看改动它的影响半径
$ codegraph impact GraphTraverser# 把 MCP 服务写进已安装的 Agent 配置
$ codegraph install --yes设 CODEGRAPH_VERSION 可以钉住某个版本而不取最新。CI 里设 CODEGRAPH_NO_DAEMON=1 走前台模式;不想要文件监听就传 --no-watch。排除规则写在 .codegraph/config.toml 的 [indexing] exclude 下,自定义扩展名映射写在 .codegraph/codegraph.json。
语言支持
一共解析 38 种语言,但深度不一样,而深度才是有用的那个数字。下面三档是按提取深度分的,不要把它们合成一个"支持 38 种语言"。
- 29完整符号提取
- TypeScript · TSX · JavaScript · JSX · ArkTS · Python · Go · Rust · Java · C · C++ · C# · PHP · Ruby · Swift · Kotlin · Dart · Scala · Lua · Luau · Objective-C · R · Solidity · Nix · Terraform · Erlang · CFML · GDScript · Pascal
- 6嵌入与模板提取
- Vue · Svelte · Astro · Razor (.cshtml) · Liquid · XML / MyBatis mapper
- 3仅到文件级
- YAML · Twig · Properties
第二档的语言,符号来自宿主文件里嵌入的脚本与模板标记,粒度取决于该文件的写法。第三档只登记文件与文件间的关系,没有符号级的调用图。语言集合是固定的,不做启发式兜底 —— 一个文件要么在这套表里,要么不进图。
平台
六个预编译目标,发布产物命名为 codegraph-<version>-<target>.<ext>。Linux 走 musl 静态链接,不依赖 glibc,也不依赖系统 SQLite。
| 平台 | 架构 | 目标三元组 | 格式 |
|---|---|---|---|
| Linux | x86_64(musl 静态) | x86_64-unknown-linux-musl | .tar.gz |
| Linux | aarch64(musl 静态) | aarch64-unknown-linux-musl | .tar.gz |
| macOS | x86_64 | x86_64-apple-darwin | .tar.gz |
| macOS | aarch64(Apple Silicon) | aarch64-apple-darwin | .tar.gz |
| Windows | x86_64 | x86_64-pc-windows-msvc | .zip |
| Windows | aarch64(ARM64) | aarch64-pc-windows-msvc | .zip |
也可以直接从发布页下载压缩包,解压后把 codegraph 放到 PATH 上。
技术栈与存储
选型都是为了同一件事:单文件二进制、无外部依赖、输出可复现。
- Rust,Edition 2024。发布产物是单个可执行文件,不带运行时。
- tree-sitter,每种语言一套语法,加上跨文件符号解析。
- 项目级 SQLite,全文检索用 FTS5。数据库静态链接进二进制,机器上不需要预装 SQLite。
- 仓库内的 .codegraph/ —— codegraph.db 及其 WAL、config.toml、codegraph.json,守护进程的 pid / socket / 日志也在这里。
- Clap 子命令,五种 shell 的补全脚本随二进制分发。
- 按项目共享的守护进程,通过 Unix socket 通信,监听文件变化并增量更新;客户端全部断开且空闲超时后自行退出。
- 索引与查询全程离线。HTTP 传输默认只绑 127.0.0.1。
- MIT。
它不做什么
写清边界比多列几条功能有用 —— 尤其是这类容易被误当成别的东西的工具。
不做相似度检索
没有 embedding,没有向量库,问题不会被转成向量再找最近邻。它回答的是结构性问题,靠的是语法树里真实存在的边。想按"意思相近"找代码,这个工具帮不上。
不判断对错
它告诉你调用关系和影响半径,不告诉你这段代码写得好不好、有没有 bug。类型检查交给编译器,风格交给 linter,正确性交给测试。
跨文件解析是尽力而为
跨文件符号解析基于名字匹配,有歧义的调用会返回多个候选。动态分发、反射、运行时拼出来的调用,静态解析看不见。
索引不是实时的
守护进程落后文件写入约一秒。刚改完立刻查,可能读到上一版;工具响应会标出处于陈旧状态的文件。
语言集合是固定的
不在那 38 种里的语言不会被启发式地"尽量解析一下",而是直接不进图。这是明确的取舍:宁可少收,也不产出没有依据的边。
在任意仓库里跑一次 init
预编译二进制、一键脚本、源码安装三条路都在下面。它是 MIT 许可的个人项目,issue 和 PR 都在同一个仓库里。