A CLI in any repository
reviewgate review reads the same .reviewgate/config.yml and the same presets as the bot, and runs the same two-pass judge. A single file with no Node and no Docker — six platforms, Windows and Alpine included.
Not generic checks, but the conventions of one particular team — on its stack, by its rules. In two places: with the agent in the editor while the code is being written, and on the pull request (merge request on GitLab) when it is time to accept it. Here is what the engine does.
Agents write more and more of the code, and catching their mistakes only on the pull request is late — every fix travels a full round trip. So the same team rules and the same judge work right inside the agent's loop. The bot on the pull request stays an independent gate: an agent cannot approve its own work, and the change-control point keeps its audit trail.
reviewgate review reads the same .reviewgate/config.yml and the same presets as the bot, and runs the same two-pass judge. A single file with no Node and no Docker — six platforms, Windows and Alpine included.
The agent cannot push changes that carry blocking findings. The gate sits on the push rather than on every step: a measurement showed the judge adds 35–47% to the cost of a run, so the thing to economise on is how often it is called.
Two tools: review the current changes, and read the team standards BEFORE writing code instead of learning them from a review afterwards. Works in MCP-compatible clients; we have run it on Claude Code.
Neither Node nor Docker is needed on the developer's machine, and the provider key is read only from the process environment — how to install and connect it →
Findings are anchored to specific diff lines — GitLab discussions, GitHub review comments — exactly as a human reviewer would leave them.
One summary comment with the findings grouped by severity and the verdict of the severity gate.
The bot publishes a check run on GitHub and a commit status on GitLab. With «pipeline must succeed» or a required check, that blocks the merge — a gate with real teeth.
The main pain of AI review is noise and repetition. The engine attacks it with three mechanisms.
A cheap model finds; a strong one judges every finding against the real code (whole files and the neighbours they import) and against the team standards. The judge contract is strict: it looks for nothing new, it can only lower severity, and when in doubt it must drop — so fewer false positives reach the pull request. One role in llm.judges turns it on.
A re-push reviews only the files that changed, never repeats an open finding and respects resolved threads — one living summary instead of an avalanche of duplicates. It saves tokens and reviewers alike.
Optionally the bot takes the changed files whole, the neighbours they import and the stack versions from the manifests — it catches problems outside the hunk without inventing what is not there. The strict anti-hallucination rail stays on.
For unambiguous problems the bot attaches a suggestion right in the thread: GitLab and GitHub render an «Apply» button, and the change lands in the branch with one click, with no copy-paste. Multi-line fixes included — up to 20 lines at a time. With a judge enabled (llm.judges) a strong model first checks the fix itself against the code — and its line range — so that only what genuinely repairs the problem can be applied in one click. Both options are switched on in the config.
How to enable it →🔴 web-api-di-tokens · auth.ts:42
Direct localStorage access breaks SSR.
- localStorage.setItem('token', v)
+ this.storage.set('token', v)
[ ✓ Apply suggestion ]A developer answers a comment with a question, and the bot explains itself in the same thread — usually within a minute or two — leaning on the code and on the team standards from your config. The answer arrives on its own: no CI job to trigger, no command to type. To «fixed» or «ok, thanks» the bot stays quiet, and it gives at most three answers per thread by default. When the objection is fair the bot says so plainly, and it can back a colleague's proposal — citing the team rule. A human always closes the thread. An @mention invites the bot into someone else's thread too.
How to enable it →🟠 magic-values · recently-viewed.ts:52
the id is concatenated with a «magic»
prefix 4200000 before it goes into the
shared list — the other write path stores
a clean id: one list, two formats.
├ author: that is intended — items from
│ the external catalogue carry a
│ prefixed id. isn't this a false
│ positive?
│
├ lead: @author, let's move the prefix
│ into a constant anyway
│
└ bot: Not a false positive: a deliberate
prefix does not remove the two id
formats living in one list. I back
@lead: by your magic-values rule
the prefix belongs in a constant and
the encoding in a single helper.The summary can carry the price of the run on your key: tokens and money, at your rates and in your currency. And a collapsed diagnostics block: what context the model saw, what calls were made and, above all, the judge's decisions with their reasons — including the findings it dropped. Why the judge removed a finding is visible right in the pull request, with no access to the bot's server or its logs. There is no code in the block, only metadata. Both options are switched on by a flag in the config.
⚙️ This review: 46.2K tokens in · 3.8K out · ≈ $0.21 🔬 Run diagnostics Model context: diff: 14 files · environment: ✓ Calls: · generator sonnet — 41.3K→2.9K tokens · 38s · findings: 5 · judge opus — 96K→1.1K tokens · 64s · dropped: 1 Judge dropped / lowered: ❌ cart.service.ts:42 error-handling — «catchError is already one line above»
Eleven language cores and dozens of frameworks from day one. The core knows the idioms and the traps of the language; the framework layer knows the specifics of the stack. The core itself is language-agnostic — without a preset it works on any language through your rules.
Strict types instead of any, races in async/await, leaked subscriptions, the trust boundary around input.
Typing and contracts, blocking calls inside async, resources through context managers, injections.
Every err handled, cancellation through context, goroutine races and leaks, defer for resources.
Optional instead of null, thread safety, resources through try-with-resources, boundary validation.
Nullable references, async/await without deadlocks, IDisposable/using, allocations in LINQ.
Strict comparisons instead of ==, typing and readonly, SQL injection, unsafe deserialisation.
Null safety, structured coroutine concurrency, exhaustive when, leaked CoroutineScope.
Metaprogramming risks, N+1 and blocking calls, exception handling, injections.
Borrows and lifetimes, no unwrap/panic, errors through Result, unsafe under a magnifying glass.
Optionals without force-unwrap, retain cycles and [weak self], async/await, value vs reference.
Null safety, dispose and the async gap, Future/Stream errors, needless widget rebuilds.
Team rules apply on top of any preset, and some presets have options of their own. The full list and every option are in the configuration reference →
The standards live in .reviewgate/config.yml next to the code and are versioned through pull requests. You describe your conventions in plain English and the model applies them to the diff — that is the difference from a linter. Rules can also see the pull request metadata: a rule «no description — ask {mr:author} to add one» puts the @author into the comment, and the platform notifies them. And review_prompt sets the whole review guideline — extending the default one (extend) or replacing it entirely (replace).
rules:
- id: commit-prefix
description: "Commits carry TASK-<number>"
severity: critical
- id: no-any
description: "No any, except in *.spec.ts"
severity: major
- id: mr-description
description: "No PR description —
ask {mr:author} to add one"
severity: minor
severity_gate: offAnthropic's cloud, any OpenAI-compatible endpoint (DeepSeek, OpenRouter, Yandex AI Studio, your own vLLM) or a local model through Ollama — in which case nothing leaves at all. The code never leaves your infrastructure.
Bring the bot up in your infrastructure and open a test pull request.