Inkvi/code-reviewer:基于 Claude、Codex 等模型的 AI 代码审查工具
Inkvi/code-reviewer
Inkvi/code-reviewer 是一款由 Claude、Codex、Antigravity、OpenCode 和 pi 驱动的 AI 代码审查工具,可作为 GitHub PR 守护进程或本地 git 仓库审查工具运行。它支持多审查器并行、PR 评论 /review 触发、按风险分级自动批准,并可通过 TOML 配置模型与推理强度。
AI code review tool powered by Claude, Codex, Antigravity, OpenCode, and pi. Works as a GitHub PR daemon or as a local review tool for git repositories.
Requirements
- Python 3.12+
uvghauthenticated (gh auth login)codexauthenticatedclaudeauthenticated (Agent SDK depends on Claude Code runtime)- if using
antigravityreviewer:agy(Antigravity CLI) installed and authenticated - if using
opencodereviewer:opencodeinstalled and configured with a provider - if using
pireviewer:pi(@mariozechner/pi-coding-agent) installed, with a provider API key in the environment (e.g.OPENROUTER_API_KEY) - for
codex_backend = "agents_sdk": OpenAI Agents SDK package +OPENAI_API_KEY
Setup
uv sync --extra dev
cp config.example.toml config.toml # optional for run-once/review; required for check/startCommands
uv run code-reviewer check # preflight checks + runtime summary (requires config) uv run code-reviewer run-once # one polling cycle (requires config with github_orgs) uv run code-reviewer start # daemon mode (requires config with github_orgs) uv run code-reviewer review --uncommitted # review local uncommitted changes (config optional) uv run code-reviewer webhook # GitHub App webhook server (env: WEBHOOK_SECRET)
Note:
run-once --pr-urlandreviewwork without a config file — sensible defaults are used. If./config.tomlexists it is loaded automatically; pass--config path.tomlto use a different file.
Local Review
Review changes in any local git repository without GitHub:
# Review uncommitted changes (staged + unstaged) against HEAD uv run code-reviewer review --uncommitted # Compare branches uv run code-reviewer review --base main uv run code-reviewer review --base main --branch feature-x # Review a specific commit uv run code-reviewer review --commit abc1234 # Review a different repo uv run code-reviewer review --repo /path/to/repo --uncommitted # JSON output uv run code-reviewer review --uncommitted --output-format json
All reviewer/model override flags work with review too:
uv run code-reviewer review --uncommitted --enabled-reviewer codex uv run code-reviewer review --base main --reconciler-backend antigravity
PR Daemon
Target specific PRs
uv run code-reviewer run-once --pr-url https://github.com/<org>/<repo>/pull/<number> uv run code-reviewer run-once --pr-url https://github.com/<org>/<repo>/pull/<number> --auto-post-review uv run code-reviewer run-once --pr-url https://github.com/<org>/<repo>/pull/<number> --use-saved-review --auto-post-review uv run code-reviewer run-once --no-auto-post-review
JSON output (requires --pr-url)
uv run code-reviewer run-once --pr-url https://github.com/<org>/<repo>/pull/<number> --output-format json
Slash command trigger
The daemon monitors PR comments for /review commands:
/review— request a review on the current PR/review force— force a re-review even if the current HEAD was already reviewed
Who can trigger: PR author and org members in monitored orgs.
Behavior:
- Reacts with 👀 to the triggering comment
- If already reviewed at the current commit: replies with a skip message (unless
forceis used) - Runs the full review pipeline and posts results
Configuration
GitHub scope
github_orgs = ["Inkvi"] # github_orgs = ["polymerdao", "another-org", "Inkvi"] excluded_repos = ["polymerdao/infra", "sandbox-repo"]
Reviewers
# Run both in parallel (default) enabled_reviewers = ["claude", "codex"] # Single reviewer # enabled_reviewers = ["codex"] # enabled_reviewers = ["claude"] # enabled_reviewers = ["antigravity"] # enabled_reviewers = ["opencode"] # enabled_reviewers = ["pi"]
Codex backend
# Stable default: codex_backend = "cli" # Experimental OpenAI Agents SDK backend: # codex_backend = "agents_sdk"
Both backends use the same configurable full-review prompt.
Model and reasoning tuning
# Claude # claude_model = "claude-sonnet-4-5" # claude_reasoning_effort = "low" # low|medium|high|max # Codex codex_model = "gpt-5.3-codex" codex_reasoning_effort = "low" # low|medium|high # Antigravity # antigravity_model = "" # leave unset to use agy default # OpenCode (access models via OpenRouter, OpenAI, Google, etc.) # opencode_model = "openrouter/zhiyu/glm-5" # pi (provider/model, optional :<thinking> suffix; key from env, e.g. OPENROUTER_API_KEY) # pi_model = "openrouter/deepseek/deepseek-v4-flash:high" # Reconciler (claude|codex|antigravity|opencode|pi) reconciler_backend = "claude" # reconciler_model = "claude-opus-4-1" # reconciler_reasoning_effort = "high" # claude: low|medium|high|max, codex: low|medium|high
Triage and lightweight review
Every PR goes through triage first. Simple changes (config, version bumps, image tags) get a lightweight single-model checklist review. Complex changes go through the full multi-reviewer pipeline.
Triage also rates how risky the PR would be to merge without a careful human review: low, medium, or high, with a one-sentence reason. By default only low-risk PRs can be auto-approved; auto_approve_max_risk changes that (see Auto submission). A custom triage_prompt_path must ask for "risk" and "risk_reason" in its JSON reply, or PRs will never be auto-approved unless auto_approve_max_risk = "high".
triage_backend = "claude" # claude|codex|antigravity|opencode|pi # triage_model = "" # leave unset to use backend default triage_timeout_seconds = 60 lightweight_review_backend = "claude" # claude|codex|antigravity|opencode|pi # lightweight_review_model = "" # leave unset to use backend default # lightweight_review_reasoning_effort = "low" # low|medium|high|max lightweight_review_timeout_seconds = 300
Prompt overrides
Every model-backed step can load an optional TOML prompt spec from config:
triage_prompt_path = "./prompts/triage.toml" lightweight_review_prompt_path = "./prompts/lightweight_review.toml" full_review_prompt_path = "./prompts/full_review.toml" reconcile_prompt_path = "./prompts/reconcile.toml"
Prompt paths are resolved relative to the config.toml file that declares them. Each spec fully replaces that step's built-in instruction package:
prompt = """Review {url}""" system_prompt = """Optional system prompt"""
Built-in prompt specs now live in src/code_reviewer/prompt_specs/triage.toml, src/code_reviewer/prompt_specs/lightweight_review.toml, src/code_reviewer/prompt_specs/full_review.toml, and src/code_reviewer/prompt_specs/reconcile.toml. Use them as the visible source of truth for the default templates.
Rules:
promptis requiredsystem_promptis optional- unknown keys or unknown placeholders fail config load
- prompt override paths are config-only in this release; there are no CLI flags for them
Supported placeholders:
- Common:
{url_label},{url},{title},{base_ref},{head_sha},{changed_files},{additions},{deletions},{workspace} triage:{diff_section}full_review:{diff_command},{extra_context}reconcile:{pr_comments},{reviewer_sources},{max_findings},{max_test_gaps}
Notes:
full_review_prompt_pathapplies to Claude, Codex CLI, Codex Agents SDK, Antigravity, OpenCode, and pi- Antigravity only supports prompt execution and requires
full_review_prompt_pathto be set - for Codex Agents SDK,
system_promptis used as the agent instruction layer code-reviewer checkshows whether each step is using the default prompt or an override path
Examples:
# triage.toml prompt = """Classify this PR using the diff: {diff_section} Respond with JSON only.""" system_prompt = "You are a PR triage classifier."
# lightweight_review.toml prompt = """Review this simple change: - URL: {url} - Files: {changed_files} Return only ### Findings and ### Test Gaps.""" system_prompt = "You are a lightweight config reviewer."
# full_review.toml prompt = """Review this change set: - URL: {url} - Base: {base_ref} Run `{diff_command}` and inspect the workspace at {workspace}. Return only ### Findings and ### Test Gaps.""" system_prompt = "You are a code reviewer. Use the diff command and repository context. Do not invent evidence."
# reconcile.toml prompt = """Reconcile these reviewer outputs: {reviewer_sources} PR comments: {pr_comments} Keep at most {max_findings} findings and {max_test_gaps} test gaps.""" system_prompt = "You are a review reconciler."
Auto submission
# Post concise review as a normal PR comment auto_post_review = false # Submit formal review decision automatically: # - approve when no P0/P1 findings, triage risk is at or below auto_approve_max_risk, # CI checks have all passed, and there are no merge conflicts # - otherwise, a clean review is posted as a comment explaining why it was not approved # - request changes when any P0/P1 finding exists auto_submit_review_decision = false # Highest triage risk that can be auto-approved: low|medium|high # "high" turns the risk check off, so PRs triage could not rate are approved too auto_approve_max_risk = "low" # Changed files matching these patterns make a PR high risk, whatever triage says. # fnmatch rules: * also matches across folders, so "*auth*" matches src/auth/login.py. # Checked against the reviewed commit; a renamed file counts under both names, and if # the changed files cannot be listed the PR is treated as high risk. # Has no effect when auto_approve_max_risk = "high". Default: no patterns. # high_risk_paths = ["*auth*", "*billing*", "migrations/*", ".github/workflows/*"] # Extra patterns for specific repos. A PR is checked against high_risk_paths plus the # list for its repo. Keys are "owner/repo" (case-insensitive). Default: none. # high_risk_paths_by_repo = { "polymerdao/obul" = ["contracts/*", "deploy/*"] }
Trigger mode
trigger_mode = "rerequest_only" # rerequest_only|rerequest_or_commit
Output limits
max_findings = 10 # 1-20 max_test_gaps = 3 # 1-10
Slash command
slash_command_enabled = true
Other settings
poll_interval_seconds = 60 skip_own_prs = true include_reviewer_stderr = true max_parallel_prs = 1 max_mid_review_restarts = 2 # 0-5, restart review when new commits land mid-flight claude_timeout_seconds = 900 codex_timeout_seconds = 900 antigravity_timeout_seconds = 900 opencode_timeout_seconds = 900 pi_timeout_seconds = 900 output_dir = "./reviews" state_file = "./.state/pr-reviewer-state.json" clone_root = "./.tmp/workspaces"
CLI overrides
Most config fields can be overridden from the CLI:
uv run code-reviewer run-once --enabled-reviewer codex uv run code-reviewer start --enabled-reviewer claude --enabled-reviewer codex uv run code-reviewer run-once --codex-backend cli uv run code-reviewer run-once --codex-model gpt-5.3-codex --codex-reasoning-effort high uv run code-reviewer run-once --claude-model claude-sonnet-4-5 --claude-reasoning-effort medium uv run code-reviewer run-once --reconciler-backend codex --reconciler-model gpt-5.3-codex uv run code-reviewer run-once --reconciler-backend antigravity uv run code-reviewer run-once --enabled-reviewer opencode --opencode-model openrouter/zhipu/glm-5 uv run code-reviewer run-once --enabled-reviewer pi --pi-model openrouter/deepseek/deepseek-v4-flash:high uv run code-reviewer start --slash-command-enabled uv run code-reviewer start --no-slash-command-enabled uv run code-reviewer run-once --triage-backend claude --triage-model claude-sonnet-4-5 uv run code-reviewer run-once --lightweight-review-backend claude
Review Pipeline
flowchart TD
Start([PR or local changes]) --> Triage
subgraph Triage
T[Run triage classifier<br><i>claude / codex / antigravity / opencode / pi</i>]
T --> Simple{simple?}
end
Simple -->|yes| LW[Lightweight review<br><i>single-model checklist</i>]
LW --> LWValid{success?}
LWValid -->|yes| Validate1[Validate output format]
LWValid -->|no| Fallback[Fall back to full review]
Validate1 --> Output
Simple -->|no| FullReview
Fallback --> FullReview
subgraph FullReview [Full Review]
direction TB
Launch[Launch enabled reviewers<br>in parallel]
Launch --> Claude[Claude<br><i>Agent SDK</i>]
Launch --> Codex[Codex<br><i>CLI exec or Agents SDK</i>]
Launch --> Antigravity[Antigravity<br><i>prompt only</i>]
Launch --> OpenCode[OpenCode<br><i>any model via providers</i>]
Launch --> Pi[pi<br><i>any model via providers</i>]
Claude --> Collect[Collect outputs]
Codex --> Collect
Antigravity --> Collect
OpenCode --> Collect
Pi --> Collect
Collect --> Multi{multiple<br>reviewers?}
Multi -->|yes| Reconcile[Reconciler<br><i>claude / codex / antigravity / opencode / pi</i><br>merges + deduplicates findings]
Multi -->|no| Single[Use single reviewer output]
end
Reconcile --> Validate2[Validate output format]
Single --> Validate2
Validate2 --> Output
subgraph Output [Output]
direction TB
Write[Write review markdown<br>+ raw sidecar + versioned history]
Write --> Mode{PR or local?}
Mode -->|PR| Publish{auto post<br>enabled?}
Publish -->|yes| Post[Post comment /<br>submit review decision]
Publish -->|no| Done1([Done — file only])
Post --> Done2([Done])
Mode -->|local| Print[Print review to terminal]
Print --> Done3([Done])
end
style Triage fill:#f0f4ff,stroke:#4a6fa5
style FullReview fill:#fff5f0,stroke:#c56a3a
style Output fill:#f0fff4,stroke:#3aaa5b
Loading
Webhook Server (GitHub App)
Run as a GitHub App webhook receiver instead of polling. The webhook server listens for PR events and spawns run-once --pr-url for each actionable event.
Setup
- Create a GitHub App with permissions:
pull_requests: write,issues: read,contents: read - Subscribe to events:
pull_request,issue_comment - Set the webhook URL to
https://<your-host>/webhook - Note the webhook secret
Run locally
WEBHOOK_SECRET=your-secret code-reviewer webhook WEBHOOK_SECRET=your-secret code-reviewer webhook --host 127.0.0.1 --port 9090
Environment variables
| Variable | Description | Default |
|---|---|---|
WEBHOOK_SECRET |
GitHub App webhook secret (recommended) | (none, signature validation disabled) |
WEBHOOK_HOST |
Bind address | 0.0.0.0 |
WEBHOOK_PORT |
Bind port | 8000 |
Endpoints
POST /webhook— GitHub App webhook receiverGET /healthz— liveness probe
Handled events
| Event | Action | Behavior |
|---|---|---|
pull_request |
opened, synchronize, review_requested |
Spawns review (skips drafts) |
issue_comment |
created with /review or /review force |
Spawns review |
ping |
— | Returns pong |
Docker
docker build -t code-reviewer .
docker run -e WEBHOOK_SECRET=your-secret -p 8000:8000 code-reviewerThe default entrypoint runs code-reviewer webhook. Override the command for other modes:
docker run code-reviewer run-once --pr-url https://github.com/org/repo/pull/123 docker run code-reviewer start
Behavior
- Polls open PRs in all configured owners (
github_orgs) wherereview-requested:@me - Excludes repos listed in
excluded_repos - Runs only reviewers listed in
enabled_reviewers - Skips draft PRs and (by default) PRs authored by you
- Triages every PR as "simple" or "full_review" and rates its risk as low, medium, or high
- Simple PRs get a lightweight single-model checklist review
- Complex PRs go through the full multi-reviewer + reconciler pipeline
- If triage or lightweight review fails, falls back to full review
- Bootstraps all discovered candidate PRs when no prior state exists
- After bootstrap, processes PRs when a newer direct re-request is observed
- Runs all enabled reviewers in parallel
- Injects PR issue-thread comments into reconciliation context
- Writes reconciled review to
reviews/<org>/<repo>/pr-<number>.md - Writes versioned history to
reviews/<org>/<repo>/pr-<number>/<timestamp>-<shortsha>.md - Saves raw reviewer outputs to
reviews/<org>/<repo>/pr-<number>.raw.md run-once --pr-url ... --use-saved-reviewreuses existing review and continues to posting/submission
Lint and test
uv run ruff check . uv run ruff format . uv run pytest
来源:GitHub · github.com