Graft — граф контекста кодовой базы для Claude Code, Cursor и других агентов
📂 Исходный код на GitHubOpen-source контекстный слой для больших кодовых баз (TypeScript, MIT, 5.4k звёзд). Строит граф кода двумя уровнями — детерминированный tree-sitter и LLM-суммаризация — и подключает его к Claude Code, Cursor, Codex, Gemini и другим агентам через MCP, CLI и хуки. На SWE-bench Verified поднял точность Claude Sonnet 5 с 54% до 66% при 23% экономии токенов.
Graft — open-source контекстный слой для больших кодовых баз от команды Trail (ранее NanoNets). Инструмент один раз строит понимание репозитория и записывает его в виде графа связанных markdown-файлов, который кодинг-агенты читают так же, как любые другие файлы. Заявленный результат: до 4× дешевле, до 3× быстрее и точнее — с измерениями на собственном бенчмарке и на SWE-bench Verified. Проект написан на TypeScript, распространяется под лицензией MIT и набрал более 5 400 звёзд на GitHub.
Проблема: агент каждый раз начинает вслепую
Каждая задача для кодинг-агента начинается с нуля: он грепает термины, открывает файлы, идёт по импортам, откатывается и пробует снова. Агент перерисовывает карту кодовой базы, которую час назад уже строил — и выбрасывает. Авторы называют это чистым оверхедом, у которого три свойства:
- Повторяется — каждая задача платит за исследование заново.
- Выбрасывается — всё, что агент понял, умирает вместе с сессией.
- Не разделяется — следующий разработчик и его агент тоже начинают с нуля.
Человек онбордится в кодовую базу один раз. Агент — при каждой задаче.
Как работает Graft
Graft строит понимание один раз и пишет его в папку graft/ внутри репозитория — по одному узлу на подсистему, API или концепт. Ключевые принципы:
- Настоящие объяснения, а не список символов. Каждый узел описывает простым языком, что делает часть системы и как связан с остальной — так, как объяснил бы сеньор-инженер.
- Настоящий граф, который можно читать. Без эмбеддингов, similarity-поиска и индекса, который нужно поддерживать тёплым. Граф — это набор связанных файлов, которые агент открывает, грепает и обходит.
- Локальный кэш, а не артефакт в git.
graft buildавтоматически добавляетgraft/в.gitignore— это регенерируемый кэш, какnode_modules. В git коммитится только обвязка изgraft init(.claude/,AGENTS.md, MCP-конфиг); каждый член команды запускаетgraft buildсам. - Всегда свежий. Каждый запрос сначала сверяет рабочее дерево с отпечатком последней сборки (~3 мс, структурно, бесплатно) и перестраивает граф, если что-то изменилось — включая незафиксированные правки.
- Свой провайдер, свой ключ, своя модель. LLM-суммаризации пишутся через любой провайдер: OpenAI, Anthropic, OpenRouter, Fireworks, Groq, LiteLLM-прокси или локальная модель. Структурный граф (tree-sitter) вообще не вызывает модель и не требует ключа.
Установка занимает две команды:
npm install -g @nanonets/graft # install the CLI, once
graft init # build the graph + wire it into Claude Code
Что внутри узла
Обычные карты кода останавливаются на адресе — «эта вещь лежит в том файле на такой-то строке». Узел Graft несёт смысл внутри, чтобы агент узнавал нужное заранее и открывал файл только при необходимости:
| Часть | Содержимое |
|---|---|
| Summary | Объяснение на простом языке, что делает код; регенерируется при изменении источника |
| Crux | Несколько строк, которые несут логику: guard-условия, смена состояния — прямо в тексте узла |
| Sources | Точные файлы, из которых собран узел, с отслеживанием по хэшу содержимого |
| Links | Типизированные связи с другими узлами (depends_on, part_of, uses, implements) в виде [[wikilinks]] |
| Notes | Ваши заметки под сгенерированным блоком; сохраняются при перегенерации |
Crux хранится как сам код, а не диапазон строк: номера строк «плывут» при правках выше, а важные строки — нет.
Бенчмарки
Собственный харнесс: 162 прогона, два репозитория, три варианта одного агента Claude Sonnet 5 — «холодный», с Graft-контекстом «up front» (push) и с инструментами graft, ничего не инжектирующими (pull).
| Метрика (в среднем на задачу) | Холодный агент | С Graft (push) |
|---|---|---|
| Стоимость | $0.0429 | $0.0292 (−32%) |
| Токены | 8 070 | 4 650 (−42%) |
| Вызовы инструментов | 4.2 | 2.3 (−46%) |
| Задержка | 39.8 с | 15.8 с (−60%) |
| Корректность | 93% | 93% (равно) |
Graft ни разу не ответил хуже холодного агента. Вариант pull пожертвовал скоростью ради главного: корректность 98%, +5 пунктов к базовой линии.
SWE-bench Verified
Отраслевой стандарт: реальные GitHub-issues из реальных репозиториев, проверка официальным харнессом swebench — патч применяется, запускаются тесты мейнтейнеров. 50 инстансов, одна модель на обоих плечах (Claude Sonnet 5), одинаковые Docker-образы и лимиты ходов.
| Метрика | Холодный агент | С Graft | Выигрыш |
|---|---|---|---|
| Корректность | 27/50 (54%) | 33/50 (66%) | +12 п.п. |
| Токены | 142.0M | 109.4M | −23% |
| Стоимость | $52.34 | $42.43 | −19% |
| Вызовы инструментов | 1 370 | 1 031 | −25% |
| Время выполнения | 13 094 с | 8 922 с | −32% |
Каждый выигрыш по корректности имеет одну форму: базовая линия патчит один файл и пропускает родственные. На django-11532 холодный агент закрыл 1 из 5 нужных файлов и сломал 18 ранее проходящих тестов. Graft находил остальные — а на django-16263 сделал это за половину токенов и половину времени.
Языки и построение графа
Граф строится в два прохода: сначала LLM суммаризирует каждый файл (кэшируется по хэшу содержимого), затем сводки группируются в курированный набор узлов с типизированными связями. Параллельно детерминированный tree-sitter строит граф по символам — graft/.graph/wiring.json с каждой функцией, классом и call-edge, без модели и ключа.
Поддерживается 23 языка на трёх уровнях точности:
- Полная точность (ручные экстракторы, кросс-файловый резолв вызовов): TypeScript/JavaScript, Python, Go, Java, Kotlin, PHP, Swift, R.
- Широкая (символы + call-edges через generic tree-sitter): Rust, C, C++, C#, Ruby, Scala, Elixir, Solidity, OCaml, Zig, Dart, Clojure, Nix, Lua.
- Compiler-grade (опционально) —
graft build --lspдобавляет точные рёбра через language servers: rust-analyzer, clangd, gopls, pyright, typescript-language-server.
Интеграция с агентами
graft init определяет установленных агентов и подключает Graft к выбранным: Claude Code, Cursor, Codex, Gemini, Grok, Copilot, Kiro, Windsurf, AdaL, OpenCode. Каждый получает нативный файл инструкций, а Claude Code — глубокую интеграцию: живой statusline с предупреждением о устаревшем графе, auto-sync после каждого хода и «blast radius» при редактировании файла — кто зависит от изменённого кода.
Дополнительно регистрируется MCP-сервер с шестью инструментами: graft_find_code (ранжированные узлы по вопросу), graft_file_api (все сигнатуры файла без тел — API-поверхность за десятую долю токенов), graft_trace_calls (кто зависит от символа, N уровней вглубь), graft_find_all (поиск по регулярному выражению с группировкой по символам), graft_repo_map (карта незнакомого репозитория) и graft_check_freshness.
Полезные команды CLI:
graft build --deep # LLM layer: concept nodes + per-symbol summaries
graft ask "<task>" # ranked nodes + exact file:line (no LLM, no key)
graft callers <symbol> -d N # transitive blast radius
graft grep "<regex>" # exhaustive search grouped by symbol
graft map # token-budgeted repo orientation
graft blast --base origin/main # blast radius of a diff, what a PR job runs
graft viz # interactive local viewer
Для ревью пул-реквестов есть GitHub App, который автоматически считает blast-radius каждого PR. Телеметрия анонимная и отключается одной командой (graft telemetry disable).
Вывод
Graft решает конкретную и дорогую проблему: повторное исследование кодовой базы при каждой задаче. Подход «граф как файлы» без эмбеддингов и серверов выглядит прагматично, а цифры на SWE-bench Verified — редкий случай, когда инструмент для контекста агентов подтверждает и скорость, и корректность на отраслевом бенчмарке. Стоит попробовать на большом репозитории, где холодный старт агента болит сильнее всего.
Репозиторий: https://github.com/trailhq/Graft
Источник: https://github.com/trailhq/Graft