docs · no pep talks, only options
Configure it. Deploy it.
Then regret the PR.
Everything you need to install Nitpicker, pick a runtime, tune the model,
and teach Nit your house rules via REVIEWER.md and Copilot instruction files.
Overview
Nitpicker is an open-source AI PR reviewer. It reads the diff only (plus optional repo guides), then posts inline comments and a short summary.
- Auto-review on PR open, reopen, and ready for review
- On demand via a
/nitpickercomment - Q&A when you mention the bot on a PR
- Thread replies when you mention the bot on a review comment
Three deploy modes share the same review engine: AWS Lambda, Cloudflare Workers, and GitHub Actions.
Install
Fastest path — one command, short wizard:
curl -fsSL https://nitpicker.dev/install | bash
The installer clones into ~/nitpicker (override with NITPICKER_HOME), then:
- Asks for a deploy method (Lambda, Workers, or Actions)
- Installs missing tools when it can
- Creates the GitHub App when needed (browser flow)
- Asks for your LLM API key
- Deploys or prints workflow setup
- Opens the app install page (App modes)
Already cloned? Run the same wizard from the repo:
pnpm setup
# same flags as the curl installer, e.g.
pnpm setup -- --method worker --provider openai
Deploy methods
Pick based on how many repos you want and whether you want a real bot user.
| Method | Flag | Needs | Best for |
|---|---|---|---|
| AWS Lambda | lambda |
AWS account + GitHub App | Multi-repo bot, instant webhooks |
| Cloudflare Workers | worker |
CF account + GitHub App | Multi-repo bot, no AWS |
| GitHub Actions | actions |
Repo workflow + LLM_API_KEY |
No always-on server; per-repo |
App modes (Lambda / Workers): real bot user, multi-repo install, /nitpicker + @bot Q&A.
Actions mode: posts as github-actions[bot] unless you pass a PAT. Q&A via @github-actions ….
AWS Lambda
Packages the webhook handler as a Node 20 Lambda behind HTTP API Gateway
(see template.yaml). Idle cost is effectively zero.
DEPLOY_METHOD=lambda
APP_ID=…
WEBHOOK_SECRET=…
PRIVATE_KEY_BASE64=… # preferred over multiline PRIVATE_KEY
LLM_API_KEY=…
AI_PROVIDER=anthropic
AI_MODEL=claude-sonnet-5
BOT_NAME=nitpicker-bot
STACK_NAME=nitpicker
AWS_REGION=us-east-1
pnpm deploy
# prints WebhookUrl — set that as the GitHub App webhook endpoint
# (the installer usually wires this for you)
- Requires AWS credentials with rights to deploy CloudFormation / SAM.
- Function timeout 300s, 512 MB, arm64.
- After config changes in
.env, runpnpm deployagain.
Cloudflare Workers
Same Probot webhook app on Workers (src/worker.ts + wrangler.toml).
Secrets go through Wrangler; non-secrets live in [vars].
DEPLOY_METHOD=worker
APP_ID=…
WEBHOOK_SECRET=…
PRIVATE_KEY_BASE64=…
LLM_API_KEY=…
CF_WORKER_NAME=nitpicker
pnpm deploy:worker
# secrets set automatically from .env:
# APP_ID, PRIVATE_KEY_BASE64, WEBHOOK_SECRET, LLM_API_KEY
Manual secret updates anytime:
npx wrangler secret put LLM_API_KEY
npx wrangler secret put APP_ID
npx wrangler secret put PRIVATE_KEY_BASE64
npx wrangler secret put WEBHOOK_SECRET
Non-secret defaults in wrangler.toml:
AI_PROVIDER, AI_MODEL, BOT_NAME,
MAX_DIFF_SIZE, REVIEW_ON_OPEN.
Edit the file or override via the Cloudflare dashboard, then redeploy.
GitHub Actions
No webhook server. The composite action (action.yml) runs on PR and comment events
and posts as github-actions[bot] by default.
- Copy
examples/github-actions/nitpicker.ymlto.github/workflows/nitpicker.yml. - Add repository (or org) secret
LLM_API_KEY. - Optional repo variables:
AI_PROVIDER,AI_MODEL,BOT_NAME,REVIEW_ON_OPEN.
name: Nitpicker
on:
pull_request:
types: [opened, reopened, ready_for_review]
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
permissions:
contents: read
pull-requests: write
issues: write
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: sagivo/nitpicker@main
with:
llm-api-key: ${{ secrets.LLM_API_KEY }}
ai-provider: anthropic
ai-model: claude-sonnet-5
bot-name: github-actions
review-on-open: "true"
To post as a different user (or to hit private resources), pass
github-token with a PAT that has pull-requests: write
and set bot-name to that account’s login (no [bot] suffix).
Config reference
Runtime config is environment variables (written to .env by setup).
App modes load them at deploy time; Actions maps workflow inputs onto the same names.
AI & behavior
| Variable | Default | Notes |
|---|---|---|
LLM_API_KEY |
— | Provider API key. Required. |
AI_PROVIDER |
anthropic |
anthropic · openai · google |
AI_MODEL |
claude-sonnet-5 |
Any model id your provider accepts. See defaults below. |
BOT_NAME |
nitpicker-bot |
Login without [bot]. Must match @mentions. Use github-actions in Actions mode. |
REVIEW_ON_OPEN |
true |
Auto-review on open / reopen / ready for review. Set false to require /nitpicker only. Drafts are always skipped. |
MAX_DIFF_SIZE |
50000 |
Max diff characters sent to the model (truncated past this). |
MAX_REVIEWER_GUIDE_SIZE |
20000 |
Max chars loaded from REVIEWER.md. |
MAX_COPILOT_INSTRUCTIONS_SIZE |
20000 |
Max chars for .github/copilot-instructions.md and aggregate path instructions. |
Default models when you omit AI_MODEL / --model:
| Provider | Default model |
|---|---|
anthropic |
claude-sonnet-5 |
openai |
gpt-4.1 |
google |
gemini-2.5-flash |
GitHub App (Lambda / Workers)
| Variable | Notes |
|---|---|
APP_ID |
GitHub App ID from the app settings page. |
WEBHOOK_SECRET |
HMAC secret used to verify webhook payloads. |
PRIVATE_KEY_BASE64 |
App private key, base64-encoded PEM. Preferred — avoids multiline env pain. cat key.pem | base64 |
PRIVATE_KEY |
Raw PEM. Used only if PRIVATE_KEY_BASE64 is unset (deploy scripts will base64 it). |
The installer creates the app with the webhook permissions Nit needs (PR read/write, issue comments, contents read). Install the app on the orgs/repos you want reviewed.
Deploy variables
| Variable | Default | Notes |
|---|---|---|
DEPLOY_METHOD |
— | lambda · worker · actions |
STACK_NAME |
nitpicker |
CloudFormation stack name (Lambda). |
AWS_REGION |
us-east-1 |
Lambda deploy region. |
CF_WORKER_NAME |
nitpicker |
Cloudflare Worker name (updates wrangler.toml name). |
GitHub Action inputs
Mapped 1:1 onto the env vars above when using uses: sagivo/nitpicker@….
| Input | Default | Env equivalent |
|---|---|---|
llm-api-key |
— | LLM_API_KEY (required) |
ai-provider |
anthropic |
AI_PROVIDER |
ai-model |
claude-sonnet-5 |
AI_MODEL |
bot-name |
github-actions |
BOT_NAME |
review-on-open |
true |
REVIEW_ON_OPEN |
max-diff-size |
50000 |
MAX_DIFF_SIZE |
github-token |
${{ github.token }} |
GITHUB_TOKEN |
Customize reviews
Two layers. Most teams only need the first:
- Your own rules — guide files on each repo’s default branch. No fork, no redeploy.
- System prompt — edit the built-in persona / JSON contract in the Nitpicker source, then redeploy (or point Actions at your fork).
Give Nit your own rules
Drop guide files on the default branch of each repo you install the bot on. Nit fetches them on every review and injects them into the user prompt (below the diff). Use this for house style, severity, domain checks, and “never do X.”
Precedence
When sources conflict, highest wins:
- Path-specific instructions (
.github/instructions/*.instructions.md) - Copilot instructions (
.github/copilot-instructions.md) - Reviewer guide (
REVIEWER.md) - Built-in system prompt defaults
Guide files are treated as reference data, not a jailbreak. The system prompt still owns output format (JSON review shape, suggestion blocks, diff-only comments). Put product rules in the guides; put persona / contract changes in the system prompt.
REVIEWER.md
Repo root. Project-specific checklist: severity levels, security rules, domain must-haves. Treated as an authoritative guide on top of general review.
# Review standards
## Critical
- Never log secrets, tokens, or full auth headers
- All public API handlers must validate auth before side effects
- Payments: no empty catch around charge / refund paths
## Warning
- New DB queries in request paths need an index plan
- Avoid `any` in new TypeScript without a one-line justification
## Style
- Prefer early returns over deep nesting
- Tests that only assert `true` are not tests
Truncated at MAX_REVIEWER_GUIDE_SIZE (default 20k chars).
.github/copilot-instructions.md
Repo-wide conventions and architecture notes (same file many Copilot setups already use). Nit uses it as context for “how code should look here,” on reviews and Q&A.
# Project conventions
- Node 20, ESM only, no default exports in `src/`
- API errors: throw `AppError`; never raw strings
- React Query for server state; no ad-hoc fetch in components
- Name booleans `is*` / `has*`; no `flag` / `data2`
Path-specific instructions
Files under .github/instructions/ ending in .instructions.md,
with YAML frontmatter applyTo (glob). Nested one level is supported
(up to 5 subdirs, 15 files total). Aggregate size shares
MAX_COPILOT_INSTRUCTIONS_SIZE.
---
applyTo: "src/api/**/*.ts"
---
- Every route exports `methods` and a zod `input` schema
- No direct Prisma calls — use the service layer
- Return `json(data, status)` helpers only
---
applyTo: "**/*.{test,spec}.ts"
---
- Prefer `vi.mocked()` over manual casts
- No snapshot tests for error messages
- Integration tests go in `tests/integration/`
What Nit looks for by default
Even without guide files, reviews focus on:
- Bugs and logic errors on changed lines
- Security issues
- Performance footguns
- Readability / naming
Comments are severity-tagged (critical · warning · suggestion)
and may include GitHub suggestion blocks you can apply in one click.
Clean diffs get a short “looks good” review — no filler praise.
Tuning behavior without code changes
- Quieter bot:
REVIEW_ON_OPEN=falseand only run/nitpickerwhen you want it. - Cheaper / faster: smaller model (
gemini-2.5-flash, etc.) and lowerMAX_DIFF_SIZE. - Stricter house rules: put non-negotiables in
REVIEWER.mdunder Critical. - Different personality per area: path instructions for
src/payments/**vs UI.
Update the system prompt
The model always gets a system message (persona + output contract) and a user message (PR title, description, annotated diff, plus any guide files above). Guides cannot replace the system prompt. To change tone, focus areas, or the JSON schema the bot must return, edit the source prompts.
Where the prompts live
| File | Export | Used for |
|---|---|---|
src/prompts/review.ts |
REVIEW_SYSTEM_PROMPT |
Full PR reviews (auto + /nitpicker) |
src/prompts/review.ts |
buildReviewPrompt() |
User message: diff, file list, guide sections |
src/prompts/question.ts |
QUESTION_SYSTEM_PROMPT |
PR comment Q&A (@bot …) |
src/prompts/question.ts |
THREAD_SYSTEM_PROMPT |
Inline review-thread replies |
Wiring is in src/services/ai.ts — reviewPR / answerQuestion pass
these strings to the Vercel AI SDK as system + prompt.
Edit the review system prompt
- Clone or open your Nitpicker install (default
~/nitpicker), or forksagivo/nitpicker. - Open
src/prompts/review.tsand changeREVIEW_SYSTEM_PROMPT. - Redeploy (App modes) or point Actions at your fork/ref (Actions mode).
export const REVIEW_SYSTEM_PROMPT = `You are a senior software engineer reviewing a GitHub pull request.
// Personality & focus — safe to rewrite
Review the diff for bugs, security issues, performance problems, and readability.
Only comment on lines in the diff. Be concise — no filler praise.
If the PR looks good, return an empty comments array.
// Keep these if you still use repo guide files:
// REVIEWER.md, copilot-instructions, path-instructions (see full file)
// Output contract — keep in sync with src/services/ai.ts schemas
Respond with JSON only (no markdown fences):
{
"summary": "<2-4 sentence overview>",
"comments": [
{
"file": "<path>",
"line": <last line of range>,
"start_line": <first line, omit for single-line>,
"severity": "critical" | "warning" | "suggestion",
"body": "<markdown comment>"
}
]
}`;
What you can safely change
- Persona & tone — stricter, kinder, no humor, security-only, etc.
- Default focus — e.g. prioritize auth/payments, ignore pure style nits.
- Comment policy — max comments, when to leave the PR alone, severity bar.
- Language — review in another language.
What to keep stable
- JSON-only response with
summary+comments[]— parsed insrc/services/ai.ts(reviewResponseSchema). - Comment fields —
file,line, optionalstart_line,severity,body. Extra/missing fields break inline posting. - Line numbers — the user prompt annotates the diff as
[42] +code; the model must use those numbers. - Suggestion blocks — if you still want one-click fixes, keep the GitHub
```suggestionrules. - Guide-file sections — if you rely on
REVIEWER.md/ Copilot files, keep the instructions that describe<reviewer-guide>,<copilot-instructions>, and<path-instructions>.
If you change the JSON shape, update the Zod schemas and review-posting logic in
src/services/ai.ts and src/handlers/pull-request.ts to match —
otherwise reviews will fail to parse or won’t land as inline comments.
Example: stricter, security-first prompt
You are a staff security engineer reviewing a GitHub pull request.
Prioritize, in order:
1. AuthZ/AuthN bypasses, injection, secret leakage, unsafe deserialization
2. Data loss / incorrect financial or billing logic
3. Reliability (timeouts, retries, partial failure)
Do not comment on formatting, naming taste, or import order unless it hides a bug.
At most 8 inline comments — merge related notes. Severity "suggestion" only for
real defense-in-depth improvements.
If nothing material is wrong, return { "summary": "…", "comments": [] }.
// …keep line-number + JSON contract from the stock prompt…
Q&A and thread prompts
Mentions and review-thread replies use plain markdown (not JSON).
Edit QUESTION_SYSTEM_PROMPT and THREAD_SYSTEM_PROMPT in
src/prompts/question.ts the same way — tone, length, whether to emit
```suggestion blocks in threads.
Ship the change
| Deploy mode | After editing prompts |
|---|---|
| AWS Lambda | pnpm deploy (rebuilds the bundle and updates the stack) |
| Cloudflare Workers | pnpm deploy:worker |
| GitHub Actions | Point the workflow at your fork/ref:
uses: your-org/nitpicker@your-branch
(or vendor the action path). No separate deploy step. |
| Local | pnpm dev picks up src/prompts/* on restart / via tsx watch |
- uses: your-org/nitpicker@main # or a tag / commit SHA
with:
llm-api-key: ${{ secrets.LLM_API_KEY }}
Keeping a fork up to date: periodically merge sagivo/nitpicker into yours,
resolve conflicts in src/prompts/*, redeploy.
Rules vs system prompt — which to use?
| Goal | Use |
|---|---|
| Per-repo standards, checklists, path rules | REVIEWER.md / Copilot / path instructions |
| Same rules on many repos without repeating files | System prompt (or org-wide template repos that ship REVIEWER.md) |
| Change voice, severity bar, max comments, language | System prompt |
| Change JSON fields or review posting behavior | System prompt and ai.ts / handlers |
| No fork, no redeploy | Guide files only |
Usage
| Trigger | Where | What happens |
|---|---|---|
| PR opened / reopened / ready for review | Pull request | Full review if REVIEW_ON_OPEN is true (skips drafts) |
/nitpicker |
PR comment (exact body) | On-demand full review |
@bot-name … |
PR comment | Answers using PR diff + guides |
@bot-name … |
Review thread comment | Continues the thread; can emit apply-able suggestions |
BOT_NAME must match how you mention the bot.
App slug nitpicker-bot → @nitpicker-bot why is this cached?.
Actions default → @github-actions can we drop this index?.
While working, Nit adds an eyes reaction, then removes it when the reply is posted.
Local dev
cd ~/nitpicker
pnpm install
cp .env.example .env # fill APP_ID, keys, etc.
pnpm dev # http://localhost:3000
# forward GitHub webhooks
smee -u <your-smee-url> --target http://localhost:3000/api/github/webhooks
Point the GitHub App’s webhook URL at your smee (or similar) channel while developing.
Typecheck with pnpm typecheck.
Installer flags & env
Pass after bash -s --, or to pnpm setup:
| Flag | Default | Notes |
|---|---|---|
--method NAME |
prompt | lambda · worker · actions |
--org ORG |
your user | GitHub org that will own the app |
--name NAME |
nitpicker |
GitHub App name |
--provider NAME |
anthropic |
anthropic · openai · google |
--model MODEL |
per provider | Model id |
--llm-key KEY |
— | Skip the API key prompt |
--region R |
us-east-1 |
AWS region (Lambda) |
--stack NAME |
nitpicker |
CloudFormation stack (Lambda) |
--worker-name N |
nitpicker |
Worker name |
--skip-deploy |
off | Write .env only; deploy later |
-h, --help |
— | Flag help |
Environment variables the installer also honors:
| Variable | Default | Notes |
|---|---|---|
NITPICKER_HOME |
~/nitpicker |
Clone / install directory |
NITPICKER_REPO |
official git remote | Override to install a fork |
NITPICKER_BRANCH |
main |
Branch to check out |
DEPLOY_METHOD |
— | Same as --method |
LLM_API_KEY |
— | Same as --llm-key; provider-specific *_API_KEY also accepted |
AI_PROVIDER / AI_MODEL |
anthropic / per provider | Same as flags |
AWS_REGION / STACK_NAME |
see above | Lambda |
CF_WORKER_NAME |
nitpicker |
Workers |
NITPICKER_HOME=~/tools/nitpicker \
curl -fsSL https://nitpicker.dev/install | bash -s -- \
--method worker \
--provider openai \
--model gpt-4.1
How it compares
Looking for a CodeRabbit alternative, a self-hosted AI code review bot, or an open-source AI PR reviewer that doesn’t meter every comment? Here’s how Nitpicker stacks up against the top AI pull request review tools.
Most “AI code review” products are SaaS: they host the bot, pick the model, and bill per seat or per PR. Nitpicker is the other path — MIT-licensed, self-hostable, bring-your-own-LLM, and built for diff-only code review so you spend tokens on the change, not the whole monorepo.
Nitpicker vs popular AI PR reviewers
Snapshot against the tools teams usually evaluate for automated GitHub PR review: CodeRabbit, Greptile, Graphite Diamond, Qodo (PR-Agent), and GitHub Copilot code review.
| Capability | Nitpicker | CodeRabbit | Greptile | Graphite | Qodo | Copilot |
|---|---|---|---|---|---|---|
| Open source | ✓ | ✕ | ✕ | ✕ | ~ | ✕ |
| Self-host / on-prem | ✓ | ✕ | ✕ | ✕ | ✓ | ~ |
| Bring your own model | ✓ | ✕ | ✕ | ✕ | ✓ | ~ |
| Diff-only (token-efficient) | ✓ | ~ | ✕ | ~ | ~ | ~ |
| No per-seat AI tax | ✓ | ✕ | ✕ | ✕ | ~ | ✕ |
| Inline GitHub comments | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| PR Q&A / chat | ✓ | ✓ | ✓ | ~ | ✓ | ✓ |
| Custom review rules | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Low vendor lock-in | ✓ | ✕ | ✕ | ✕ | ~ | ✕ |
| Code stays on your terms | ✓ | ✕ | ✕ | ✕ | ~ | ✕ |
✓ yes ~ partial ✕ no Hover a mark for detail.
Features move fast. Treat the table as positioning, not a sales contract — always verify current pricing and capabilities on each vendor’s site. Nitpicker’s edge is structural: open source, self-host, BYO model, diff-first.
When to pick Nitpicker
- Open-source AI code review you can audit — read the prompts, fork the bot, keep the workflow in git.
- Self-hosted PR review for regulated or private repos — run on your AWS, Cloudflare, or Actions runners; send tokens only to the provider you already trust.
- Cost-effective automated code review at scale — diff-only context keeps LLM spend predictable vs full-repo indexers.
- Model freedom — swap Claude, GPT, Gemini, or whatever ships next without changing review vendors.
- No per-seat AI review tax — pay infrastructure + API usage, not another SaaS line item for every engineer.
When a SaaS AI reviewer might fit better
- Zero ops — you want a managed GitHub App and someone else’s uptime page (CodeRabbit, Greptile, Copilot).
- Full-codebase semantic review — you want a continuously indexed monorepo graph (Greptile-style) more than a sharp diff pass.
- Stacked-PR workflow lock-in — you’re all-in on Graphite and want review inside that product surface.
- Enterprise procurement bundle — you need a vendor MSA, SOC2 packet, and a single throat to choke already on the shortlist.
Nitpicker as an alternative
Jump to a specific comparison: vs CodeRabbit · vs Greptile · vs Graphite Diamond · vs Qodo PR-Agent · vs Copilot code review.
Nitpicker vs CodeRabbit
Searching for a CodeRabbit alternative? CodeRabbit is a polished managed AI PR reviewer with deep product UX. Choose Nitpicker when seat pricing, data residency, or black-box prompts become the bottleneck — you self-host, bring your own model, and audit every prompt in git. See the feature matrix.
Nitpicker vs Greptile
Looking for a Greptile alternative? Greptile indexes the full codebase for semantic review. Nitpicker stays lightweight: diff-only AI code review for fast pull request feedback without maintaining a second source-of-truth index. Pick Greptile when monorepo graph context is the product; pick Nitpicker when cost, speed, and self-host control matter more.
Nitpicker vs Graphite Diamond
Graphite Diamond folds AI review into stacked PRs and the Graphite workflow. Nitpicker is a standalone open-source AI PR reviewer that runs on Lambda, Workers, or Actions — no Graphite stack required. Use Diamond if you’re already all-in on Graphite; use Nitpicker if you want the bot without the platform.
Nitpicker vs Qodo (PR-Agent)
Qodo
(and open-source PR-Agent) is a capable multi-tool review agent. If you
like that idea but want a smaller surface area focused on inline nits,
suggestion blocks, and @bot Q&A — with the same
bring-your-own-LLM freedom — Nitpicker is the tighter lane.
PR-Agent on GitHub
remains a solid OSS peer; Nitpicker optimizes for diff-first simplicity.
Nitpicker vs GitHub Copilot code review
GitHub Copilot code review
lives inside the GitHub UI on a Copilot seat. Nitpicker gives you
Copilot-style automated review energy with your own API keys,
model choice, and deploy target — plus fully customizable
REVIEWER.md rules. Ideal when you want AI pull request
comments without another seat tax or Microsoft-only pipeline.
Bottom line: Nitpicker is the developer-owned AI pull request reviewer — automated code review on GitHub, open source, self-hosted, multi-model, optimized for the diff. Ship the bot you control; keep the pride (and the invoice) honest.
Ready to try it? Install in one line or pick a deploy method.