跳到內文
coograph 17

程式碼圖

程式碼圖是一個 SQLite 資料庫 — .code-graph/graph.db — 記錄你儲存庫的結構關係。定義、呼叫、import、繼承、檔案對檔案的邊。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 套件的語言會自動回退到 regex 解析器 — 覆蓋率較差,但建置不會失敗。

完整清單見 .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 的目標是原始字串,查「誰 import 了某模組」要用比對而不是 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 的檔案也會重新解析,跨檔案的邊才會準。

限制