соло · терминал

Ревью кода на своей машине до коммита

Обычно ревью приходит, когда код уже уехал в MR или PR. Здесь оно происходит раньше и на вашей машине: reviewgate читает незакоммиченные изменения прямо в рабочем дереве — ни бота, ни вебхука, ни доступа к площадке для этого не нужно.

О двух вещах первый отчёт умалчивает, и знать их лучше заранее: код возврата 0 — это не вердикт, и без правил команды ревью судит код по best practices, а не по вашим правилам.

Перед началом: нужен установленный git и доступ к модели (свой API-ключ, корпоративный шлюз или локальная модель). Если ни бинаря, ни ключа ещё нет, начните с CLI, хук и MCP.

Шаг 1. Установить бинарь

Один файл, ни Node, ни Docker — но git обязан быть: внутрь он не вшит. Команды для установки на своей платформе можете посмотреть на странице CLI, хук и MCP — скачать, сверить сумму, распаковать, перенести в /usr/local/bin.

Важно:

  • Качайте командой curl, а не браузером. На macOS файл, скачанный через браузер, получает метку карантина, а распаковка в Finder переносит её на исполняемый файл. Наши сборки подписаны ad-hoc, а не нотаризованы, поэтому Gatekeeper'у не с чем сверить файл — и он просто откажется его запускать. Файл, скачанный через curl, метки не получает.
  • xz не установлен по умолчанию. Его нет ни в голых Ubuntu, Debian и Alpine, ни в чистой macOS, а в Windows нет распаковщика этого формата вовсе — там нужен 7-Zip. Без распаковщика установка обрывается сразу после сверки суммы, поэтому поставьте его заранее.

Далее две команды: reviewgate version покажет версию, reviewgate help — что инструмент умеет.

одно важное предупреждениеreviewgate без аргументов — это не справка, а ревью. В отличие от git или docker, команда по умолчанию проверяет ваше рабочее дерево и зовёт модель, а это деньги. Чтобы разобраться, наберите reviewgate help.

Шаг 2. Настроить ReviewGate

reviewgate init создаёт два файла. Первый, личный, — ~/.config/reviewgate/config.yml: в нём подключение к модели и ключ к ней. На macOS и Linux он создаётся с правами 600, то есть читать его можете только вы. Второй, .reviewgate/config.yml, лежит в репозитории: в нём правила ревью, общие для всей команды, и его коммитят вместе с кодом. Разделение сделано намеренно: ни один файл из чекаута не настраивает, куда пойдёт ревью, поэтому клон чужого репозитория не может увести ваш дифф на чужой endpoint.

Ключ к модели впишите в личный конфиг. Если не хотите держать его в самом конфиге, укажите вместо него команду, которая достанет ключ — из менеджера паролей или из файла, где он у вас уже лежит, например:

~/.config/reviewgate/config.yml
llm:
  # 1Password
  api_key_command: op read op://dev/anthropic/key
  # or the macOS Keychain
  # api_key_command: security find-generic-password -s anthropic -w
  # or a .env file you already keep (macOS, Linux)
  # api_key_command: sed -n 's/^ANTHROPIC_API_KEY=//p' ~/.config/reviewgate/secret/.env

Важно:

  • На Windows личный конфиг — это %APPDATA%\reviewgate\config.yml, а не домашний каталог, как на macOS и Linux. Файл в другом месте ReviewGate не найдёт и не скажет об этом: прогон остановится с ошибкой llm_unavailable, будто с ключом что-то не так, и искать вы будете проблему в ключе, а не в пути.
  • Шаблон закомментирован целиком, а у взаимоисключающих опций решёток две: #. Снимайте обе у того варианта, который подходит вам.
  • init печатает «Next steps», и ключа среди них нет. Эти шаги про политику; без ключа первый прогон остановится на llm_unavailable. Ничего не сломалось — просто шага нет в списке.

Результат проверьте командой reviewgate doctor. Она не просто читает ваш конфиг, а стучится в endpoint провайдера, которого вы выбрали, поэтому ловит просроченный ключ или ключ с опечаткой. Запускать команду нужно внутри git-репозитория: снаружи она сообщит, что ревьюить нечего, и выйдет с кодом 1.

Шаг 3. Запустить ревью незакоммиченного кода

reviewgate review без флагов берёт незакоммиченные изменения — код ровно в том виде, в каком он сейчас на диске.

Важно:

  • --staged ревьюит индекс, а не диск. Если вы сделали git add ., а потом поправили ещё немного, ревью увидит прежнее содержимое — и не скажет об этом ни слова.
  • --refs main ревьюит коммиты вашей ветки против main, как это сделал бы merge request. Незакоммиченное он не видит.

Файлы, исключённые через .gitignore, для ревью невидимы — и, в отличие от файлов под ignore из правил команды, их отсутствие нигде не объявляется. Если проект держит так сгенерированные или локальные файлы, ревью их не увидит.

Шаг 4. Прочитать результат

Здесь вас ждёт неожиданность: нулевой код возврата — это не вердикт. Гейт по умолчанию выключен, поэтому прогон, нашедший замечания, всё равно выходит с нулём, и отчёт говорит об этом словами:

терминал
Findings: 4 (⛔ 0 · 🔴 0 · 🟠 2 · 🔵 2 · ⚪ 0); the gate is off.

Тот же код возврата 0 вы получите и тогда, когда ревью ничего не проверяло: чистое рабочее дерево, пустой индекс при --staged, уже влитая ветка при --refs:

терминал
Ревьюить нечего: в области нет изменений — модель не вызывалась.

Ничего не сломалось: модель не спрашивали, денег не потратили. Но скрипт — или агент, — читающий только код возврата, не отличит это от чистого ревью.

Чтобы код возврата что-то значил, его надо попросить: --fail-on major вернёт 1, если в отчёте осталось хотя бы одно замечание уровня 🟠 major или серьёзнее — 🔴 critical или ⛔ blocker. Именно это превращает ревью из текста, который читают, в гейт, на который можно опереться.

Читайте и строки над списком замечаний, отмеченные ⚠️ и ℹ️. В них прогон признаётся, что прошёл мельче заказанного: в репозитории нет правил команды, судья не настроен, модель взята из ваших личных настроек, а не закреплена командой.

Как выглядит первый прогон

Оба прогона ниже — настоящие, сделаны 7 сентября 2026 года на публичном учебном репозитории: один и тот же дифф в 31 строку и два файла, генератор claude-sonnet-5, судья claude-opus-5. Сначала, сразу после установки, когда политики команды в репозитории ещё нет:

терминал
ℹ️ No .reviewgate/config.yml in the repository — the review ran on default settings,
   without team rules.
⚠️ no judge is configured — the run effectively went as --fast: the findings are not
   confirmed by an independent model

🟠 src/pricing/pricing.service.ts:47 — couponDiscount computes
   (net.amount * percent) / 100 manually and returns a raw object literal, while the
   sibling discountAmount uses the shared multiply helper. This bypasses rounding
   safeguards, risking fractional-cent values.

Findings: 4 (⛔ 0 · 🔴 0 · 🟠 2 · 🔵 2 · ⚪ 0); the gate is off.

Да, вывод на английском, и это не ошибка перевода: язык вывода задаёт политика команды, а её пока нет, и по умолчанию продукт отвечает по-английски. Теперь тот же дифф, когда команда закоммитила правила (первые две строки — служебный журнал, он всегда на английском):

терминал
Config loaded: preset=nestjs, rules=6, gate=major
Judging (claude-opus-5): kept 3 of 3, dropped 0, downgraded 0, fixes revoked 0

🔴 src/pricing/pricing.service.ts:52 — couponDiscount вычисляет сумму купона как
   (net.amount * percent) / 100 напрямую, без округления через общий хелпер
   multiply/roundHalfUp. Результат может быть нецелым числом минорных единиц
   (например, net=999, percent=7 → 69.93), что нарушает инвариант Money.amount
   (целые центы). (team:money-in-minor-units)

       return multiply(net, percent / 100);

❌ Гейт (major) НЕ ПРОЙДЕН. Замечаний: 3 (⛔ 0 · 🔴 1 · 🟠 1 · 🔵 1 · ⚪ 0).
⚙️ Стоимость прогона: ≈ 0,14 $

Дефект тот же, найден оба раза. Изменилось то, что происходит дальше. В первый раз это 🟠 major с формулировкой «код написан не так, как соседний». Во второй — 🔴 critical: замечание ссылается на правило команды, предлагает готовый фикс кодом, а прогон заканчивается красным вместо зелёного — --fail-on major здесь остановил бы push.

Изменились сразу две вещи, и стоит сказать точно, что сделала каждая. Правила команды задали строгость и назвали договорённость. Судья, настроенный в том же файле, подтвердил замечания по полным файлам. И один набор не вложен в другой: часть замечаний повторяется в обоих прогонах, остальные различаются — модель недетерминирована, и второй прогон того же диффа не повторит первый слово в слово.

Сколько стоит один прогон

Цифры из тех же двух прогонов — один и тот же дифф, 7 сентября 2026 года, генератор claude-sonnet-5, судья claude-opus-5; цены по прайс-листу Anthropic на 23 сентября 2026 года:

ПрогонТокенов вход → выходВремяЦена
Без правил команды и без судьи6,8K → 2,8K30 с≈ 0,03 $
С правилами и судьёй28,7K → 8,9K79 с + 9 с≈ 0,14 $

Цена первого прогона — наш собственный расчёт по его токенам: отчёт её не показал, потому что показ стоимости включается политикой команды, а политики ещё не было. Если включить его в политике, каждый прогон будет заканчиваться своей ценой — как это сделать, см. справочник конфига.

Обе строки таблицы — повторные прогоны: промпт к этому моменту уже лежал в кэше. Кэш живёт час, его запись стоит вдвое дороже обычного входа, а чтение — в десять раз дешевле. Поэтому первый прогон в течение часа оплачивает запись, а следующие читают кэш: для этого диффа первые прогоны обошлись бы примерно в 1,8 раза дороже — ≈ 0,05 $ и ≈ 0,26 $ вместо 0,03 $ и 0,14 $. Сравнивая свои числа с нашими, посмотрите, какой у вас был прогон — первый за час или повторный.

На вашем проекте цифры будут другими. Цена зависит от четырёх вещей: какую модель вы выбрали, насколько велик дифф, работает ли судья вообще и сколько окружающего кода он получает. Модели подешевле меняют цифру на порядок в одну сторону, полные файлы большого репозитория — в другую. Поэтому каждая цена на этом сайте идёт вместе со схемой и датой замера.

Когда что-то пошло не так

Бинарь не запускается. На Linux обычно забыт chmod +x из команды установки — оболочка отвечает «Permission denied». На macOS система сообщает, что не может проверить разработчика: файл скачан браузером и несёт метку карантина. Снимите её и запустите снова:

терминал
xattr -d com.apple.quarantine reviewgate

llm_unavailable. Модель недоступна — почти всегда дело в ключе. Его либо нет вовсе, либо он лежит в файле, который CLI не читает (на Windows — %APPDATA%\reviewgate\config.yml), либо строка с ключом осталась закомментированной: спереди #. Что именно, покажет reviewgate doctor: он печатает, откуда взят ключ — из окружения, из вашего файла с полным путём или «not set». Коды возврата целиком — в справочнике CLI.

Ключ вставлен из мессенджера. Чаты вставляют невидимые символы и похожие на латиницу буквы, которых ключ не переживает, — известный случай, у него есть своё лечение.

Ревью закончилось за секунду и ничего не нашло. Оно ничего и не смотрело: рабочее дерево чистое, либо индекс пуст при --staged, либо ветка уже влита при --refs. Отчёт об этом говорит, и модель не вызывалась, так что деньги не потрачены.

doctor сообщает, что ревьюить нечего, и выходит с кодом 1. Его надо запускать внутри git-репозитория: из домашнего каталога ему нечего проверять.

Дальше