solo · terminal

Review your code on your own machine before you commit

A review usually arrives once the code is already in a merge or pull request. Here it comes earlier, and on your own machine: reviewgate reads the uncommitted changes straight from your working tree, with no bot, no webhook and no access to GitLab or GitHub.

The first report keeps two things to itself, and it is better to know them up front: a zero exit code is not a verdict, and without team rules the review judges your code by general good practice rather than by what your team agreed on.

Before you start: you need git in PATH and access to a model — a key of your own, your company's LLM gateway, or a local model. If you have neither the binary nor the key yet, start with CLI, hook and MCP.

Step 1. Install the binary

One file, no Node and no Docker — but git must be in PATH: the binary does not bundle it. The install commands for your platform are on CLI, hook and MCP: download, verify the checksum, unpack, move into /usr/local/bin.

Important:

  • Download with curl, not with your browser. On macOS a file downloaded through a browser carries a quarantine flag, and unpacking it in Finder passes the flag on to the executable. Our builds are signed ad-hoc rather than notarised, so Gatekeeper has nothing to check the file against and simply refuses to run it. A file downloaded with curl gets no flag.
  • xz is not installed by default. Bare Ubuntu, Debian and Alpine do not have xz, and neither does a clean macOS; Windows has no unpacker for the format at all, and you need 7-Zip there. Without one the install stops right after the checksum, so install it first.

Then two commands: reviewgate version prints the version, and reviewgate help shows what the tool can do.

one important warningreviewgate with no arguments is not help — it is a review. Unlike git or docker, the default command reviews your working tree and calls the model, which costs money. Type reviewgate help to look around.

Step 2. Configure ReviewGate

reviewgate init creates two files. The personal one, ~/.config/reviewgate/config.yml, holds the connection to the model and its key; on macOS and Linux it is created with mode 600, so only you can read it. The other, .reviewgate/config.yml, lives in the repository: it holds the review rules the whole team shares, and it is committed with the code. The split is deliberate: no file in a checkout decides where the review goes, so cloning someone else's repository cannot send your diff to their endpoint.

Put the model key in the personal config. If you would rather not keep the key in the file itself, give a command that fetches it instead — from your password manager, or from a file where you already keep it, for example:

~/.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

Important:

  • On Windows the personal config is %APPDATA%\reviewgate\config.yml, not a file in your home directory as on macOS and Linux. ReviewGate will not find a file in the wrong place, and will not say so: the run stops with llm_unavailable, as if the key were the problem, and you end up checking the key rather than the path.
  • The template is commented out entirely, and mutually exclusive options carry two #. Delete both on the variant you pick.
  • init prints «Next steps», and the key is not among them. Those steps are about policy; without a key the first run stops with llm_unavailable. Nothing is broken — the step simply is not on the list.

Check the result with reviewgate doctor. It does not just read your config: it calls the endpoint of the provider you chose, so it catches a key that has expired or been mistyped. Run it inside a git repository: outside one it reports there is nothing to review and exits with 1.

Step 3. Review your uncommitted changes

reviewgate review with no flags takes your uncommitted changes — the code exactly as it is on disk right now.

Important:

  • --staged reviews the index, not the disk. If you ran git add ., then edited a bit more, the review sees the older content — and says nothing about it.
  • --refs main reviews the commits of your branch against main, the way a merge or pull request would. It does not see uncommitted work at all.

Files excluded by .gitignore are invisible to the review, and — unlike files under ignore in the team config — their absence is not announced anywhere. If your project keeps generated or local-only files that way, the review will not see them.

Step 4. Read the result

This is where people get surprised: a zero exit code is not a verdict. The gate is off by default, so a run that found problems still exits with 0, and the report says so in words:

terminal
Findings: 6 (⛔ 0 · 🔴 0 · 🟠 3 · 🔵 3 · ⚪ 0); the gate is off.

A green zero also comes back when the review looked at nothing at all — a clean working tree, an empty index under --staged, a branch already merged under --refs:

terminal
Nothing to review: the scope contains no changes — the model was not called.

Nothing failed here. The model was never asked, and no money was spent. But a script — or an agent — that reads only the exit code cannot tell this from a clean review.

To make the exit code mean something, ask for it: --fail-on major returns 1 when the report still holds at least one finding at 🟠 major or above — 🔴 critical or ⛔ blocker. That is what turns the review from a text you read into a gate you can rely on.

Read the lines marked ⚠️ and ℹ️ above the findings, too. That is where the run admits it went smaller than you asked: no team rules in the repository, no judge configured, the model taken from your personal settings rather than pinned by the team.

What the first run looks like

Both runs below are real, made on 7 September 2026 on a public sample repository: the same diff of 31 lines in two files, generator claude-sonnet-5, judge claude-opus-5. First — straight after installing, with no team policy in the repository:

terminal
ℹ️ 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:52 — Unlike discountAmount (which uses the shared
   multiply helper), this branch computes (net.amount * percent) / 100 and builds the
   Money object with a raw literal. This bypasses whatever rounding/validation
   invariants multiply/money enforce, risking fractional-cent amounts on invoices.

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

Useful, and honest about its own limits: the notices say the run went smaller than it could. Now the same diff, after the team has committed its rules:

terminal
Config loaded: preset=nestjs, rules=6, gate=major
Judging (claude-opus-5): kept 4 of 4, dropped 0, downgraded 0, fixes revoked 0

🔴 src/pricing/pricing.service.ts:52 — couponDiscount computes
   (net.amount * percent) / 100 directly instead of going through the shared
   multiply/roundHalfUp helper. Because minor units are integers, this division can
   yield a fractional amount (net=101, percent=7 -> 7.07), silently violating the money
   invariant. (team:money-in-minor-units)

       return multiply(net, percent / 100);

❌ Gate (major) FAILED. Findings: 4 (⛔ 0 · 🔴 1 · 🟠 2 · 🔵 1 · ⚪ 0).
⚙️ Run cost: ≈ 0.27 $

The same defect, found both times. What changed is what happens next. The first time it is 🟠 major, a remark that the code is written differently from its neighbour. The second time it is 🔴 critical: it cites the rule the team wrote, carries the fix as code, and the run ends red instead of green — --fail-on major here would stop the push.

Two things changed at once, and it is worth being precise about which did what. Team rules set the severity and named the agreement. The judge, configured in the same file, confirmed the findings against the whole files. And the two sets are not one inside the other: some findings appear in both runs, the rest differ — the model is not deterministic, and a second run of the same diff will not repeat the first word for word.

What one run costs

Numbers from those two runs — same diff, 7 September 2026, generator claude-sonnet-5, judge claude-opus-5; prices per Anthropic's price list on 23 September 2026:

RunTokens in → outTimeCost
No team rules, no judge6.8K → 11.2K107 s≈ $0.14
Team rules + judge29.2K → 9.2K75 s + 12 s≈ $0.27

The price of the first run is our own arithmetic over its tokens: the report did not show one, because cost display is switched on in the team policy, and there was no policy yet. Switch it on there, and every run ends with its own price — see the config reference.

Both runs in the table were the first of their hour, so both paid for writing the prompt cache. The cache lives for an hour: writing it costs twice the normal input rate, reading it a tenth. The first run within the hour pays for the write, and the runs after it read: repeated within the hour, these two reviews would have cost about $0.12 and $0.15 instead of $0.14 and $0.27. When you compare your numbers with ours, check which kind of run yours was — the first of the hour or a repeat.

Your numbers will differ. Cost depends on four things: which model you chose, how large the diff is, whether a judge runs at all, and how much of the surrounding code it gets. Cheaper models move the figure by an order of magnitude in one direction; whole files of a large repository move it in the other. That is why every price on this site comes with the schema and the date it was measured on.

When it goes wrong

The binary will not start. On Linux the chmod +x from the install command was usually skipped, and the shell answers «Permission denied». On macOS the system says it cannot verify the developer: the file was downloaded through a browser and carries a quarantine flag. Remove it and run again:

terminal
xattr -d com.apple.quarantine reviewgate

llm_unavailable. The model is unreachable, and it is almost always the key: there is none, or it sits in a file the CLI does not read (on Windows — %APPDATA%\reviewgate\config.yml), or the line with it is still commented out with a # in front. reviewgate doctor shows which: it prints where the key came from — the environment, your file with its full path, or «not set». The full list of exit codes is in the CLI reference.

The key was pasted from a messenger. Chat apps insert invisible characters and lookalike letters that a key does not survive — a well-known case with its own treatment.

The review finished in a second and found nothing. It looked at nothing: the working tree is clean, or the index is empty under --staged, or the branch is already merged under --refs. The report says so, and the model was never called, so nothing was spent.

doctor reports there is nothing to review and exits with 1. It has to be run inside a git repository; from a home directory it has nothing to check.

Next