Pull request review you can read the source of.¶
open-nitpick reads a pull request, reviews it with a model you choose, and posts inline comments. Run it as a GitHub Action, as a CLI in any CI, or against your working tree before the pull request exists. Nothing is hosted, nothing is priced per seat, and your code stays with you.
5corpora, four re-runnable
20instrument bugs recorded
1published claim retracted
Corpora and the retraction as of 2026-09-05. The instrument-bug count is the number of rows in the findings table, and a test holds this figure to it.
# A signed binary, or `go install …/cmd/nitpick@latest` with Go 1.25.5.
v=$(gh release view --repo jdziat/open-nitpick --json tagName -q .tagName)
os=$(uname -s | tr 'A-Z' 'a-z'); arch=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
curl -fsSLo nitpick "https://github.com/jdziat/open-nitpick/releases/download/$v/nitpick_${v}_${os}_${arch}"
chmod +x nitpick && sudo mv nitpick /usr/local/bin/
export LLM_PROVIDER=synthetic
export LLM_MODEL=hf:moonshotai/Kimi-K3
export SYNTHETIC_API_KEY=syn_...
nitpick review # reviews your uncommitted changes
Any model, different models per job
Eighteen providers, with Synthetic and OpenRouter built in, plus any OpenAI-compatible endpoint, Ollama and llama.cpp. A cheap model triages and a strong one reviews. An expert pass can overrule either. Configuration →
Prompts you can print before you pay
Path-scoped instructions live next to the code they describe. nitpick explain-config shows the exact prompt a file would get. Instructions →
Context in both directions
The definitions a changed line calls are attached by default. So are the untouched callers of anything a change redefines, behind a switch of their own, since that walk reads files the change never named. Related context →
A trust model, written down
A change cannot supply the policy it is reviewed under. Policy is read from the base revision. Analyzer configuration never comes from the tree, and endpoint keys are stripped from a config the change could have written. Trust model →
Linters as evidence, not noise
Thirty-three, configured from outside the tree they read. golangci-lint and ruff ship enabled, twenty-four more run whenever they are installed, and eslint, semgrep and five others wait until you name them. Their output goes to the model for triage instead of into the pull request. Analyzers →
Measured, mistakes included
Five corpora and a judge-free harness. The findings document records the instrument bugs found along the way, including the one that forced a retraction. Findings →
How a review runs¶
- The change is read from GitHub or a local checkout, and the policy it is reviewed under comes from the base revision, not the branch.
- Analyzers that are installed run against the changed lines, isolated from the tree, and their output becomes evidence.
- Files are bundled into batches under a token budget, with related context attached: what a changed line calls by default, and who calls what the change redefined when the caller walk is switched on.
- Each batch is reviewed by the model the route selects. A triage model merges and filters. An optional expert pass refutes.
- The review is posted as inline comments, with a summary that lists every file not reviewed, every analyzer that did not run, and every finding that was discarded and why.
The quickstart uses Synthetic, which serves open-weight models on a flat subscription. Their pricing page read $30 a month for one pack on 2026-09-05. That link carries the author's referral code and pays the author referral credit if you sign up through it. synthetic.new without the code is the same service at the same price. You can spend nothing first: nitpick explain-config prints what a review would send without sending it, and LLM_PROVIDER=ollama runs against a local model. Why, and the alternatives →