跳到正文
coograph 17

代码图

代码图是一个 SQLite 数据库——.code-graph/graph.db——保存仓库的结构关系。定义、调用、导入、继承、文件 → 文件的边。你的 AI 代理通过 MCP 工具查询它,而不是为每个问题重新 grep。

图里有什么

两张表,就是 sqlite3 .code-graph/graph.db ".schema" 打印出来的样子:

CREATE TABLE nodes (
    id         TEXT PRIMARY KEY,   -- content hash, not an autoincrement integer
    kind       TEXT NOT NULL,      -- file | function | method | class | interface | enum | table
    name       TEXT NOT NULL,
    file       TEXT NOT NULL,      -- repo-relative path
    start_line INTEGER,
    end_line   INTEGER
);
CREATE INDEX idx_node_file ON nodes(file);

CREATE TABLE edges (
    src  TEXT NOT NULL,            -- always a nodes.id
    dst  TEXT NOT NULL,            -- a nodes.id, or a raw name (see below)
    kind TEXT NOT NULL             -- imports | contains | calls | inherits
                                   -- | implements | depends_on | tests_for
);
CREATE UNIQUE INDEX uq_edges  ON edges(src, dst, kind);
CREATE INDEX idx_edge_dst ON edges(dst);

没有 language 列——语言在解析时从扩展名推断,不存进库。

dst 不一定是节点 id。 containstests_fordepends_on 能解析到 nodes 里的一行。importsinherits 存的是解析器看到的原始文本——'../api/metricsClient''argparse'、一个基类名——因为把模块路径解析成文件需要解析器不跑的一套规则。前一组用 join,后一组用匹配。下面的例子两种各一个。

这些都能直接用 sqlite3 查。MCP 服务器只是同一个文件之上的类型化便利层。

支持的语言

tree-sitter 解析器支持:Python、TypeScript、JavaScript、Go、Rust、Java、C#、Ruby 等。没有 tree-sitter 包的语言自动回退到正则解析器——覆盖度下降,但构建不会失败。

完整列表见 .github/code-graph/requirements.txt

MCP 工具

MCP 服务器向代理暴露以下查询:

工具用途
get_minimal_context(task)默认首调。 把任务里提到的符号解析到图上,返回 files_to_read:定义所在的文件、它们依赖的文件,再加一层依赖它们的文件,排序后最多 6 个。同时还有图的规模、未提交改动的风险,以及下一个该用的工具。控制在 ~150 token 以内,所以不带逐文件的 token 统计——需要就把 files_to_read 交给 get_review_context。空列表一定带着 files_reason
query_graph(pattern, node_name)具名模式:callers_ofcallees_ofimports_ofimporters_oftests_forfile_summary
get_impact_radius(files)传入的是一组仓库相对路径,不是符号。返回从它们可达的文件。
get_review_context(files, budget_tokens?)评审用:这些文件加上直接邻居,排序后按 token 预算裁剪。
find_large_functions(min_lines=50)超过 N 行的函数——技术债扫描。
detect_changes(base="HEAD")自指定 ref 以来 SHA-1 变化的文件。
update_graph()增量重新解析变化的文件。
build_graph()从零完整重建。
graph_stats()节点 + 边数量、上次更新时间、按语言分的文件数。
visualize_graph().code-graph/graph.html 渲染交互式 HTML 图。

可视化

uv run --with-requirements .github/code-graph/requirements.txt \
  .github/code-graph/server.py --visualize

输出 .code-graph/graph.html——独立的 D3 力导向视图(不需要服务器)。用任何浏览器打开。拖动节点、悬停看符号详情、按语言或类型筛选。用于检查新构建是否正确、找出孤立模块,以及给队友展示代理实际看到的内容。

Coograph 代码图可视化——节点和边的交互式 D3 力导向视图

为什么选 SQLite

直接走 SQL

如果没有 MCP,数据库就是一个文件:

# 一个文件里的所有定义,按源码顺序
sqlite3 -header -column .code-graph/graph.db "
  SELECT child.kind, child.name, child.start_line
  FROM edges e
  JOIN nodes parent ON parent.id = e.src
  JOIN nodes child  ON child.id  = e.dst
  WHERE parent.file = 'src/i18n/t.ts' AND e.kind = 'contains'
  ORDER BY child.start_line;
"
kind      name                    start_line
--------  ----------------------  ----------
function  localizedPath           43
function  detectLocaleFromHeader  49

因为 imports 的目标是原始字符串,查“谁导入了某模块”要用匹配而不是 join:

sqlite3 -header -column .code-graph/graph.db "
  SELECT DISTINCT n.file
  FROM edges e
  JOIN nodes n ON n.id = e.src
  WHERE e.kind = 'imports' AND e.dst LIKE '%metricsClient'
  ORDER BY n.file;
"

这是 MCP 服务器没注册时的官方回退方案。Coograph 项目里的代理知道先试 MCP,再回退到 sqlite3,只有两者都失败时——才用 grep。

自动更新

.github/code-graph/ 里的 git 钩子(post-commitpost-mergepost-rewrite)在每次变更后调用 --update。只有内容 SHA-1 变了的文件会被重新解析。从变更文件 import 的文件也会被重新解析,跨文件的边因此保持准确。

局限