ripwire — «ripgrep для AI-контекста»: карта репозитория вместо grep-и-читай

· 3 мин чтения
cli tools mcp context-engineering tokens
📂 Исходный код на GitHub

CLI-утилита и MCP-сервер на C++23 без единой рантайм-зависимости: строит ранжированную карту репозитория (граф вызовов, blast radius, тесты) для кодинг-агентов за доли секунды и в разы дешевле по токенам, чем grep и чтение файлов целиком.

ripwire — «ripgrep для AI-контекста»: карта репозитория вместо grep-и-читай

Проблема любого кодинг-агента в незнакомом репозитории: сначала grep по общему слову с сотнями совпадений, потом чтение целых файлов в поисках нужных символов, и за каждый прочитанный — даже впустую — символ платят токены. Проект ripwire из RedHat's Emerging Technologies предлагает другой подход: один бинарник без зависимостей, который отдаёт агенту ранжированную карту репозитория вместо бесконечного чтения. Слоган проекта — «the ripgrep of AI context»: укажите на любой репозиторий, и агент получит детерминированный граф вызовов — что трогать, что это ломает, какие тесты запускать.

Что это и почему быстро

ripwire — это одновременно CLI-утилита и опциональный MCP-сервер на C++23. Никаких API-ключей, эмбеддингов, индекс-серверов или демонов: один процесс, работа офлайн, ноль рантайм-зависимостей. Скорость — главный аргумент: репозиторий самого проекта индексируется за 0,25 с с использованием 6,6 МБ памяти, тогда как графово-табличный MCP-сервер, с которым сравнивали, тратит 46,8 с и 391 МБ. Тёплые запросы отвечают за 197 мс против 1082 мс у конкурента (замеры на 48 задачах по django, webpack и самому ripwire).

Под капотом — tree-sitter грамматоры для разбора кода, разрешение ссылок в граф вызовов и ранжирование через Personalized PageRank; на выходе — детерминированный минифицированный XML на stdout. Два запуска по одному дереву дают побайтово идентичный результат.

Экономия токенов в цифрах

Авторы замерили десять типовых сценариев на собственном репозитории. Все цифры — в приблизительных токенах (≈ байты/4), каждый расклад воспроизводится командой из строки таблицы:

Вопрос Команда ripwire Наивное чтение Экономия
«Сориентируй меня в репо» ripwire . ~5.6K ~20K–25K 3.6×–4.5×
«Где обрабатывается X?» ripwire . --for="…" ~2.1K ~4.9K–20K 2.3×–9.3×
«Что я уже знаю?» ripwire . --recall="…" ~15K ~445K 29.2×
«Подготовь меня к задаче» ripwire . --pack-task="…" ~2.1K ~16K–80K 7.7×–37.7×
«Покажи одну функцию» ripwire . --expand=SYM ~260–16.5K ~43K–174K 2.6×–670×
«Кто вызывает функцию?» ripwire . --callers=SYM ~580 ~40K–52K 69×–89×
«Безопасно ли это менять?» ripwire . --impact=SYM --uses=SYM ~1.3K ~18K 14.4×
«У меня stack trace» ripwire . --from-trace=FILE ~1.4K ~124K–298K 87×–209×
«Что тестировать после правок?» ripwire . --situ ~410 ~3K–132K 7.3×–324×
«Сделай ревью диффа» ripwire . --pr-context=REF ~1.9K ~4.8K–51K 2.6×–27.5×

Ключевая оговорка проекта: это не суммаризации, играющие с информацией. Каждый сценарий проверялся по принципу «тот же правильный ответ — или не считается», а каждая усечённая выдача помечается в заголовке ответа. Если ранжированный бандл для маленькой функции вышел бы дороже самого файла, ripwire честно отдаст файл целиком, пометив ответ как mode="whole-file".

Ответ агента выглядит так — ранжированные символы с doc-комментариями, аннотации риска на каждой строке (сложность cx, частота правок по git churn, усиление изменений amp), контекст вызовов в один хоп и самооценка уверенности confidence="high":

<ctx task="incremental cache invalidation" confidence="high"
     bundle="compact" est_tokens="3995">
  <sigs shown="23" total="40" capped="1">
    <d l="106" n="kCacheMagic" p="src/ingest_cache.h"
       cx="0" churn="11" amp="71" r="1">…</d>
    …
  </sigs>
  <hops shown="2" total="6" capped="1">
    <h n="spanTierMemoPath">
      <calls total="3"><c n="shaKeyedCachePath" l="1621"/></calls>
    </h>
  </hops>
</ctx>

Установка и первые команды

Однострочный установщик качает готовый бинарник (macOS и Linux, arm64/x86-64), проверяет SHA-256, кладёт в ~/.local/bin и активирует навыки для всех обнаруженных агентов:

RIPWIRE_REPO=redhat-et/ripwire bash -c "$(curl -fsSL https://raw.githubusercontent.com/redhat-et/ripwire/main/scripts/install.sh)"
export PATH="$HOME/.local/bin:$PATH"
cd your-repo
ripwire . --for="<опишите задачу словами>"

Сборка из исходников требует только CMake 3.24+ и компилятор с C++23 — все грамматики и зависимости вендорены в репозиторий, сборка проходит с выключенной сетью:

git clone https://github.com/redhat-et/ripwire.git
cd ripwire
cmake -S . -B build && cmake --build build -j
./build/ripwire . --max-tokens=3000

Четыре команды, которые авторы советуют выучить сразу:

ripwire . --max-tokens=3000                        # ranked map — с этого начинают
ripwire . --for="incremental cache invalidation"   # задачная линза: что трогать
ripwire . --callers=someFunction                   # кто вызывает
ripwire . --test-gate                              # какие тесты обязательны перед коммитом

Всего у инструмента 179 длинных флагов в семи семействах — от навигации (--callers, --impact, --connect) до качества (--quality-panel, --clones, --merge-scout) и безопасности (--scan-skill для проверки skill-файлов перед установкой). Если непонятно, какая команда нужна, — ripwire . --help-task="<задача>" порекомендует ровно одну команду и честно откажется, если данных мало.

CLI или MCP-сервер

Авторы рекомендуют CLI как основной интерфейс: он ничего не стоит, пока агент не выполнил команду — никакие схемы инструментов не висят в контексте каждой сессии. MCP-сервер (ripwire --mcp) — опциональный второй вход: 31 глагол (16 чтения, 12 «рефлексных», 3 адресных правки кода), ленивые хэндлы на тела символов и работа для клиентов без доступа к шеллу.

Регистрация через ripwire wrap <agent> — команда печатает рецепт и никогда не правит конфиг сама:

ripwire wrap claude    # напечатает: claude mcp add ripwire -- ripwire --mcp
ripwire wrap --all     # обнаружить все установленные агенты

Поддерживаются Claude Code, Codex, Cursor, Windsurf, Gemini, opencode и aider. Для любого другого MCP-клиента достаточно стандартного stanza:

{
  "mcpServers": {
    "ripwire": { "command": "ripwire", "args": ["--mcp"] }
  }
}

Навыки и визуализация

Помимо бинарника поставляется 18 задачных навыков (seventeen в релизном архиве + один контрибьюторский), которые учат агента, когда какой из 31 глагола уместен — без них у агента 175 флагов и никакой карты применимости. Устанавливаются симлинками через skills/install.sh.

Для людей есть --html: один самодостаточный HTML-файл с графом — стрелки от вызывающего к вызываемому, раскраска по сложности (cyclomatic), git-churn, языку, community-структуре или покрытию тестами. Показная деталь: пунктирные рёбра помечают вызовы, которые резолвер не смог однозначно привязать — по заверению авторов, ни один другой инструмент не отмечает, в каких именно стрелках он не уверен.

Качество кода: панель и дельта

--quality-panel автоматизирует суждения хорошего ревьюера через шесть независимых семейств доказательств, каждое из которых опирается на опубликованные исследования: форма (McCabe), нейминг (Butler), атомы запутанности (Gopstein), churn (Nagappan & Ball), колокация (Beck & Diehl), поток состояния (Henry & Kafura). Максимальная корреляция между любыми двумя семействами — +0.168, то есть они действительно меряют разное: совпадение двух семейств на одной функции — это подтверждение, а не одна метрика, посчитанная дважды.

Отдельная ставка — код, написанный самим AI: по данным GitClear, маскирование ошибок пустыми catch-блоками на +47% чаще встречается в AI-авторских коммитах, а переписывание функции в течение двух недель — на +15% вероятнее. --quality-delta проверяет дифф по 10 таким шаблонам и сообщает только то, что изменение ухудшило; --exemplar показывает образец правильного паттерна в вашем же репозитории.

Бенчмарки и «контракт честности»

На held-out срезе LocBench (60 инстансов, парное сравнение) ripwire находит все gold-файлы в топ-10 на 58.3% инстансов против 40.0% у лучшего из пяти конкурентов (codebase-memory-mcp), индексируясь за 0.31 с — быстрее, чем конкуренты заканчивают индексацию. На расширенной выборке из 243 инстансов по 78 репозиториям — 60.9% против 27.6% у собственного бейзлайна. Проигрышные инстансы названы поимённо, харнесс бенчмарка закоммичен в репозиторий, любой может всё перепроверить.

Проект ведёт себя редким для README образом: публикует собственные контрпримеры (docs/EVALS.md §7), честно называет места, где проигрывает конкуренту, и разделяет «измерено» и «не запускалось, но логично». «Контракт честности» в одну строку: любой счётчик, который нельзя доказать как полный, помечается как нижняя граница, каждое усечение раскрывается в месте возникновения, а ноль означает «не найдено», а не «не существует».

Поддерживаемые языки

22 вендоренных tree-sitter-грамматики: C, C++, Objective-C/C++, Metal, CUDA, Python, TypeScript, JavaScript, Java, Ruby, PHP, Lua, Elixir, Bash, Go, Rust, Swift, C#, плюс JSON/TOML/YAML (конфиг-ключи становятся грепаемыми символами) и Markdown — заголовки которого индексируются как символы-секции, так что --for и --recall работают и по документации. Ограничения каждого парсера (динамический диспетч в PHP, метатаблицы Lua) заявлены явно, а не замалчиваются.

Итог

ripwire — это инструмент из категории «дешёвая детерминированная карта вместо дорогого вероятностного поиска». Он не претендует на семантическое понимание кода и не прячет, где именно не уверен. Максимум выгоды он даёт там, где контекст стартует с нуля: мультиагентные оркестраторы, где каждая линия начинается холодной на одном и том же дереве, и финальные проверочные проходы (--quality-delta, --test-gate, --edit-check). На вопрос, который закрывает один grep по конкретному файлу, карта, по признанию авторов, не окупается — и --help-task прямо скажет «просто сделайте grep».

Проект распространяется под Apache 2.0, репозиторий: https://github.com/redhat-et/ripwire

Источник: https://github.com/redhat-et/ripwire