MCPcopy Create free account
hub / github.com/arsenyinfo/nitpicker

github.com/arsenyinfo/nitpicker @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
568 symbols 1,139 edges 28 files ⚖ MIT 80 documented · 14% updated 11d ago★ 117

Browse by type

Functions 451 Types & classes 117
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

nitpicker

crates.io

Multi-reviewer code review using LLMs. Spawns parallel agents with different models/prompts, aggregates their feedback into a final verdict. Supports two modes — parallel aggregation and actor-critic debate — across two task types: code review and free-form questions.

Free Web version is available for open source projects.

Each reviewer is an agentic loop that can call tools (read files, grep, glob, git commands) to explore the repo before writing its review. Discovery roles build a quick initial map and default to one early, disjoint subagent wave for multi-surface targets; validation roles delegate only bounded verification of submitted claims. Tool outputs include lightweight headers and clearer truncation/no-match messages so agents can reason about partial evidence more reliably. A separate aggregator model deduplicates and synthesizes the individual reviews into a final verdict.

Diff and PR reviews capture a frozen orientation snapshot before reviewers fan out: full HEAD, resolved base and merge-base revisions, the exact committed comparison, working-tree status, and committed/uncommitted file maps. Every lane and later debate round receives the same snapshot; agents still inspect the actual hunks and code with tools.

Requirements

  • Rust toolchain
  • A git repository to review
  • At least one configured LLM (API key or Gemini OAuth)

Installation

cargo install nitpicker

Quick start

export ANTHROPIC_API_KEY="your-api-key-here"

Review

nitpicker
nitpicker --repo /path/to/repo
nitpicker --repo /path/to/repo --prompt "focus on src/api/"
nitpicker --fallback  # try the next configured reviewer if a model fails
nitpicker --analyze src/components/
nitpicker --analyze  # entire repo

Parallel Mode

nitpicker --no-debate
nitpicker --no-debate --analyze src/
nitpicker --no-debate --max-turns 40

PR review

nitpicker pr
nitpicker pr https://github.com/owner/repo/pull/42
nitpicker pr --no-comment
nitpicker pr https://github.com/owner/repo/pull/42 --no-comment
# force a fresh temp clone even when the URL points to your current repo
nitpicker pr https://github.com/owner/repo/pull/42 --clone
# machine-readable output for embedding (one JSON object on stdout)
nitpicker pr https://github.com/owner/repo/pull/42 --no-comment --json

Ask

nitpicker ask "should we use eyre or thiserror for error handling?"
nitpicker ask --no-debate "is this authentication flow secure?"
nitpicker ask --rounds 3 "should we split this module?"
nitpicker ask --max-turns 40 "should we split this module?"

Configuration

Configuration is loaded from (first match wins):

  1. --config <path> (explicit flag)
  2. nitpicker.toml in repo root
  3. ~/.nitpicker/config.toml (global config)
# create a config in current directory
nitpicker init

# prefer OpenRouter experimental free models when OPENROUTER_API_KEY is set
nitpicker init --free

# create a global config at ~/.nitpicker/config.toml
nitpicker init --global

Example nitpicker.toml:

[defaults]
debate = true          # optional, default: true
fallback = true        # optional, default: false; use reviewer order as a failover ring
max_turns = 100        # optional, default: 100
log_trajectories = false # optional, default: false
# presets = ["correctness", "security"]  # optional; default also includes performance, simplicity

[aggregator]
model = "claude-sonnet-5"
provider = "anthropic"
max_tokens = 16384       # optional, default: 16384

[[reviewer]]
name = "claude"          # used in output headers and logs
model = "claude-sonnet-5"
provider = "anthropic"
# max_tokens = 32768     # optional, default: unset (the provider's own per-model limit)

[[reviewer]]
name = "gpt"
model = "gpt-5.6-sol"
provider = "openai_compatible"
base_url = "https://api.openai.com/v1"
api_key_env = "OPENAI_API_KEY"

# optional: define a custom review angle (or override a built-in by using its name)
[presets.api-security]
prompt = """
Review trust boundaries, authentication, authorization, input handling, and secret exposure.
Require a concrete attacker-controlled path and plausible impact for every finding.
"""

Tip: Use providers that were not used for the initial building of your codebase to enforce diversity of thought.

Review presets

A preset is one named review angle — a rubric that tells a reviewer what to investigate; the execution mode (parallel, debate, alloy) decides how. Every review run resolves an ordered preset list: --preset on the command line beats [defaults].presets, which beats the four-angle built-in default (correctness, security, performance, simplicity). Domain-specific built-ins ai-systems, ml-rigor, and tone are opt-in. general is a standalone broad review for unusual targets or user-defined concerns and cannot be combined with another preset. A [presets.<name>] table with a built-in's name replaces it.

nitpicker --preset security                      # one focused angle
nitpicker --preset security,ml-rigor             # commas split
nitpicker --preset ai-systems                    # agent/prompt/tool/context audit
nitpicker --preset general --prompt "review the plugin contract"
nitpicker pr --preset api-security               # project-defined preset

Fan-out: parallel mode runs every configured reviewer against every selected preset (reviewers × presets jobs); debate mode runs one independent Reviewer/Validator debate per preset, lanes concurrent, with a single meta-review across all lanes. Spend and wall-clock scale with the selection: the untouched default runs four lanes (or 4× the parallel jobs) where 0.8.x ran one combined review. Names are case-sensitive; unknown or empty names, mixing general with another preset, or selecting more than 16 presets fails before any model call. Every final finding includes a Lens field naming the angle that produced it (or all contributing angles when synthesis merges duplicates). ask, init, and reflect take no presets — the flag is rejected there.

Built-in rubrics and review/debate protocols live as auditable Markdown under prompts/ and are compiled into the binary. Generic loop contracts such as compaction and final-turn handling live under crates/nitpicker-agent/prompts/ and are compiled into the library that interprets them. Rust owns selection and interpolation, not the prompt prose.

Unknown config keys are rejected. For example, use max_tokens for output length; token_limit is not a supported field.

max_tokens caps a single response, and on a reasoning model it is a budget for reasoning plus the answer — set too low, the model spends it all thinking and returns empty content, which is indistinguishable from a model that said nothing. Reviewers therefore default to no cap (the provider applies its own per-model limit); set one only to bound spend. The aggregator writes one bounded synthesis and defaults to 16384. Two exceptions: Anthropic's API requires the field, so an unset reviewer cap becomes 8192 there — raise it explicitly if your model reasons past that; and auth = "codex" ignores the setting entirely, since that endpoint rejects max_output_tokens.

Debate mode is enabled by default for nitpicker, nitpicker ask, and nitpicker pr. Pass --no-debate to use parallel aggregation for a single run. Use [defaults].max_turns or --max-turns to control the per-agent tool-use loop limit.

Fallback mode is opt-in with [defaults].fallback = true or --fallback and requires at least two reviewers. Each logical reviewer keeps its normal primary, then tries subsequent [[reviewer]] entries in declaration order, wrapping at the end. Failover retries only the failed completion with the existing conversation history; it does not restart the agent. The successful route remains active for that agent, and a quota-limited route is skipped by the other jobs for the rest of the run. The aggregator tries its configured model first, then the reviewer list. A successful fallback is logged but does not make the verdict degraded. With Alloy, each completion still chooses its first healthy reviewer randomly; a failed choice then follows declaration order.

Set [defaults].log_trajectories = true to save per-agent JSONL traces and a final aggregation.json under ~/.nitpicker/sessions/session-<timestamp>-<pid>/.

Provider types

provider Auth Notes
anthropic ANTHROPIC_API_KEY env var (or api_key_env), or auth = "azure-ad" base_url optional
gemini GEMINI_API_KEY/GOOGLE_AI_API_KEY env var (or api_key_env), or auth = "agy-keyring" base_url optional (e.g. a local Gemini-compatible server); agy-keyring reuses the Antigravity CLI OAuth token from the system keyring — research only, see warning
openai OPENAI_API_KEY env var (or api_key_env), auth = "azure-ad", or auth = "codex" codex reuses your ChatGPT subscription via the Codex CLI token — research only, see warning
openrouter OPENROUTER_API_KEY env var (or api_key_env) explicit model names are recommended; model = "free" is experimental

anthropic_compatible and openai_compatible are accepted as aliases for backward compatibility.

First-party Anthropic routes enable five-minute prompt caching automatically, with stable tool/system breakpoints and a moving conversation breakpoint. Azure AI Foundry Anthropic routes use the same policy. An explicit custom Anthropic base_url keeps the compatibility request shape without cache fields because not every Anthropic-shaped gateway accepts cache_control.

auth = "azure-ad" authenticates with a refreshing Azure AD (Entra ID) token instead of a static key — for OpenAI and Anthropic models hosted on Azure AI Foundry. Requires a build with the azure feature, see below.

auth = "codex" authenticates with your ChatGPT Plus/Pro (Codex) subscription instead of a paid API key, reusing the token the Codex CLI stores on disk, see below.

OpenRouter models

openrouter supports both explicit pinned models and an experimental free auto-selection mode.

Pinned models are the supported default and the recommended setup:

# recommended: explicit model
[[reviewer]]
name = "qwen"
model = "qwen/qwen3-30b-a3b"
provider = "openrouter"

Experimental best-effort free auto-selection is also available:

# experimental: auto-select a currently available free model
# omit `model` or set model = "free"
[[reviewer]]
provider = "openrouter"

# explicit experimental form
[[reviewer]]
model = "free"
provider = "openrouter"

When model is omitted or set to "free", nitpicker tries to pick a currently working free model at startup.

This mode is convenient, but it is not production-stable and may fail due to upstream availability, routing differences, or timeouts.

If you want predictable behavior, pin explicit model names instead of relying on free auto-selection.

export OPENROUTER_API_KEY="your-key"

A free OpenRouter account is sufficient for the experimental free mode — no credit card required, just rate limits.

Antigravity Keyring (research only)

[!CAUTION] Research only — do not use on a Google account you care about. AG2's Additional Terms of Service Section 6 prohibits "using the Service in connection with products not provided by us", which directly covers reusing the agy OAuth token from a third-party client like nitpicker. Google has been actively enforcing this in 2026: paid AI Ultra subscribers have received account suspensions, often without warning, for using third-party AG2 OAuth bridges (OpenClaw, OpenCode, Pi Agent). Detection appears aggressive — even light testing has triggered bans. The earlier gemini-cli OAuth path was discouraged on similar grounds (discussion). If you want billed Gemini access without this risk, set GEMINI_API_KEY and drop the auth line.

AG2 is Google's current agentic IDE, succeeding both the older Gemini CLI OAuth path and the earlier AG1 preview. The gemini-3.x family ships only through AG2's CloudCode backend, so auth = "agy-keyring" exists purely as a research path to compare those models against the rest of the reviewer pool, with full awareness of the ToS posture above.

The proxy reads agy's OAuth token from the system keyring (service=gemini, account=antigravity) via the keyring crate (Secret Service on Linux, Keychain on macOS, Credential Manager on Windows), relies on agy to refresh it, and routes chat through CloudCode's v1internal:streamGenerateContent SSE endpoint. Run agy and complete its login first. NITPICKER_ANTIGRAVITY_PLATFORM can override the auto-detected platform enum if needed.

This path requires a build with the antigravity feature (off by default, since it pulls in the local proxy stack — axum — and the keyring crate with its native backends):

cargo build --release --features antigravity
# or: cargo install --features antigravity ...

Without the feature, auth = "agy-keyring" is rejected at config validation with a build hint, and nitpicker init won't offer the keyring reviewer.

Tested AG2 models (current author config): gemini-3.1-pro-low, gemini-3.5-flash-low. Other IDs returned by fetchAvailableModels (e.g. gemini-3-flash-agent) likely work but have not been exercised.

```toml [aggregator] model = "gemini-3.5-flash-low" provider = "gemini" auth = "agy-keyring"

[

Extension points exported contracts — how you extend this code

browse all types & interfaces →

Core symbols most depended-on inside this repo

browse all functions →

Shape

Function 348
Method 103
Class 99
Enum 14
Interface 4

Languages

Rust100%

Modules by API surface

crates/nitpicker-agent/src/llm.rs81 symbols
crates/nitpicker-agent/src/codex.rs55 symbols
crates/nitpicker-agent/src/config.rs39 symbols
src/pr.rs32 symbols
crates/nitpicker-agent/src/tools.rs32 symbols
crates/nitpicker-agent/src/azure.rs30 symbols
crates/nitpicker-agent/src/agent.rs27 symbols
src/main.rs25 symbols
src/debate.rs25 symbols
src/progress.rs24 symbols
crates/nitpicker-agent/src/openrouter.rs21 symbols
src/detect.rs19 symbols

For agents

$ claude mcp add nitpicker \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page