用 Claude Code 或 Cursor 改代码时,最磨人的不是写,而是找:函数在哪、谁调了谁、改一处会不会把别处带崩。Agent 为了回答一个简单问题,常常把整个文件甚至整个目录读一遍,token 账单和等待时间一起涨。Graft(也叫 Trail-Code / NanoNets context-graph-engine)想解决的就是这件事:在本地把代码库解析成一张图谱,让 Agent 直接查地图,而不是每次都重新探险。
它到底在做什么
Graft 把自己定位成「给 AI 编码 Agent 用的上下文层」。核心动作就三步:
- 解析:用 tree-sitter 把代码解析成符号和调用关系;
- 建图:在本地生成
graft/目录,里面是按文件拆分的 Markdown 节点卡片 + 一个wiring.json调用图; - 查询:提供
graft ask / grep / callers / map / skeleton / viz等 CLI,Agent 可以直接调用,也能通过 MCP server 接入 Claude Code / Cursor / Codex / Gemini。
关键卖点是本地可复现、结构层不花钱:普通 graft init 完全基于 AST,不需要任何 LLM API key。只有 graft build --deep 才会调用大模型给节点写摘要,而这一步对日常使用不是必须的。
本机实测:从三文件小仓库到真实前端项目
我直接用 npx 安装,没配 API key,两步就把图跑出来了。
环境:Apple M4 / 16GB / macOS 26.5.2,Node 22.22.2(受管运行时)。
实测一:3 文件示例仓库
先建了一个最简单的 JS 仓库:
mkdir sample-repo && cd sample-repo
# 包含 index.js、src/app.js、src/utils.js
cat > package.json <<'EOF'
{
"name": "sample-repo",
"version": "1.0.0",
"main": "index.js"
}
EOF然后执行:
npx @nanonets/graft init --no-global --agents claude输出:
✓ wiring: 8 nodes (5 function, 3 file), 7 edges, 3 cards [javascript]
parsed: 3 of 3 files
→ /private/tmp/graft-test/sample-repo/graft
✓ mcp claude: .mcp.json
✓ built the graph生成的 graft/.graph/wiring.json 里能直接看到节点和边:
{
"nodes": [
{"id": "src/app.js#run", "kind": "function", "path": "src/app.js", "span": "L2-L4"},
{"id": "src/utils.js#formatDate", "kind": "function", "path": "src/utils.js", "span": "L1-L3"}
],
"edges": [
{"source": "src/app.js#run", "target": "src/utils.js#formatDate", "relation": "calls"}
]
}注意:这里我把完整的 wiring.json 精简了,只保留能说明问题的两条。原文包含 8 个节点、7 条边,结构层信息完整。
再跑 graft callers formatDate:
formatDate · function · src/utils.js:L1-L3
calls ← run (src/app.js:L2-L4)
3: console.log(formatDate(new Date()));这个输出是我这次真实跑出来的,不是从 README 抄的。
实测二:10 文件前端项目 pic-shrink
我把 Graft 拿到自己仓库里的 pic-shrink(一个 Vite + React 的图片压缩工具,10 个 TS/TSX 文件)上跑了一遍:
cd /Users/minilee/WorkBuddy/apisitlee.com/04-代码项目/pic-shrink
npx @nanonets/graft init --no-global --agents claude
npx @nanonets/graft map结果:
✓ graph built · 42 nodes, 67 edges from 10 files
[graft] tokens saved ≈ 16,871 (98%) — this output ≈ 339 tok vs reading the 10 file(s) it covers whole ≈ 17,210 tok
repo map — 10 files · 32 symbols · 67 edges · tsx, typescript
src/App.tsx 1 files · 14 symbols hubs: processFiles (App.tsx, 2←), runCompression (App.tsx, 2←)
src/utils/ 1 files · 4 symbols hubs: formatSize (imageUtils.ts, 2←), compressImage (imageUtils.ts, 1←)
...这个数字是 Graft 自己报的:把 10 个文件完整塞进上下文大约需要 17,210 token,而 graft map 只要 339 token,省了约 98%。当然,这是「把全文件读一遍」vs「只读摘要图」的对比;真实 Agent 会话未必每次都会读全文件,但即便只读两三个完整文件,Graft 也能省掉重复的符号定位成本。
跑完后我删掉了生成的 graft/、.claude/、.mcp.json,没有在原项目留下污染。
实测三:Python 也能解析
顺手建了一个 2 文件 Python 仓库:
npx @nanonets/graft init --no-global --agents claude输出:
✓ graph built · 6 nodes, 8 edges from 2 files说明它的多语言支持不是空话,至少 Python 的结构层解析是开箱即用的。
几个实测发现的坑
1. 包名和仓库名对不上
仓库在 GitHub 上叫 trailhq/Trail-Code,但 npm 上可用的 CLI 包是 @nanonets/graft;README 里提到的新包 @trailhq/trail 我实测时还没发布(npm view @trailhq/trail version 返回 not published)。所以目前正确的安装命令是:
npx @nanonets/graft init不是 npx trail 或 npx graft 单独安装。我踩了一次才确认。
2. --deep 需要 LLM key,否则只是结构图
运行 graft build --deep 会得到明确提示:
⚠ no API key set — falling back to the structural build (no LLM summaries).
Set GRAFT_API_KEY (and GRAFT_PROVIDER / GRAFT_BASE_URL / GRAFT_MODEL ...)也就是说,不配 key 时,节点只有符号、文件路径、行号,没有自然语言摘要。对「函数在哪、谁调了谁」这类问题已经够用;但如果你想问「这个函数的业务逻辑是什么」,还是得来一次 deep build。
3. 默认会往 .claude/ 写配置
graft init 默认会在项目里生成 .claude/skills/graft/SKILL.md、.mcp.json、hook 文件等。如果你只是像我一样想先试试,不想改动现有 Claude Code 配置,建议加 --no-global:
npx @nanonets/graft init --no-global --agents claude这样不会写 ~/.codex/ 等全局配置,只影响当前目录。
4. graft/ 是本地缓存,不该提交
Graft 会自动把 graft/ 加进 .gitignore,因为它本质是本地生成的索引;共享给队友的正确方式是把 .claude/、.mcp.json、.cursor/rules/graft.mdc 等接线文件提交,然后队友自己跑 graft build。
什么时候不建议用
- 代码库极小(比如只有三五个文件):建图本身就有开销,
graft map的 339 token 对比直接读文件的 102 token 在小仓库上反而显得不划算。我的示例仓库 3 文件,graft map报只省 34%,不如大仓库的 98% 亮眼。 - 团队没有统一 Agent 工作流:Graft 的价值要靠 Agent 真正调用它的 MCP 工具才能放大;如果团队还在手动复制粘贴,它只是一份静态代码地图。
- 你需要深度语义摘要但不想配 key:
--deep要 GRAFT_API_KEY,不配 key 就只有结构层,无法替代代码语义搜索。
与上下文压缩工具的关系
站内前两天刚写过 Headroom,那款工具是把大段输出压小后再塞进上下文;Graft 则是让 Agent 根本不必读完整文件,直接查预生成的代码地图。两者互补:Headroom 适合日志、API 响应、RAG chunk;Graft 适合代码库导航和跨文件影响面分析。另一个相关工具是 Agent Reach,它解决的是 Agent 怎么连外部世界,Graft 解决的是 Agent 怎么读懂内部代码。
如果你还在用 Claude Code 的默认工作流,也可以先看看 Claude Code Mods 那篇,了解一下怎么给 Agent 写行为扩展;Graft 相当于在这个基础上再加一层代码地图。
快速上手命令清单
# 1. 在当前仓库初始化(不污染全局配置)
npx @nanonets/graft init --no-global --agents claude
# 2. 查看代码地图
npx @nanonets/graft map
# 3. 自然语言提问
npx @nanonets/graft ask "auth 流程怎么走?"
# 4. 查谁调用了某个函数
npx @nanonets/graft callers formatDate --depth 2
# 5. 精确查找所有出现位置
npx @nanonets/graft grep "fetchData"
# 6. 看单个文件的 API 骨架
npx @nanonets/graft skeleton src/utils.js
# 7. 检查图谱是否过期
npx @nanonets/graft check
# 8. 启动可视化(默认端口 4400)
npx @nanonets/graft viz --export ./graft-viz总结
Graft 的实用价值很直接:把代码库变成 Agent 能查的本地地图,省 token、省时间,而且结构层完全免费。我在 10 文件前端项目上跑出了约 98% 的 token 节省,3 文件小仓库也能正常建图。它的限制同样清楚:不配 LLM key 就没有自然语言摘要,小仓库收益有限,且包名/仓库名目前有点绕。对于已经在用 Claude Code、Cursor 或 Codex 做日常开发的团队,它值得一试。
未实测、需读者自行验证的部分:MCP server 在 Claude Code 真实会话中的调用效果、–deep 模式在配置 API key 后的摘要质量、graft viz 在复杂 monorepo 中的渲染表现、团队多人共享接线文件后的协作流程。