Metadata-Version: 2.4
Name: rag-reviewer
Version: 0.2.11
Summary: AI pull-request reviewer: hybrid RAG + a code graph + Claude Code. Whole-repo context, inline GitHub comments grounded on exact code.
Author: rag_for_git contributors
License: MIT
Project-URL: Homepage, https://github.com/mimfort/rag_for_git
Project-URL: Repository, https://github.com/mimfort/rag_for_git
Project-URL: Issues, https://github.com/mimfort/rag_for_git/issues
Project-URL: PyPI, https://pypi.org/project/rag-reviewer/
Keywords: code-review,pull-request,rag,code-graph,mcp,claude-code,static-analysis,neo4j,pgvector,tree-sitter,scip,voyage
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Version Control :: Git
Requires-Python: <3.14,>=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pydantic-settings>=2.3
Requires-Dist: tree-sitter<0.26,>=0.25
Requires-Dist: tree-sitter-python<0.26,>=0.25
Requires-Dist: voyageai<0.5,>=0.4
Requires-Dist: psycopg[binary]>=3.2
Requires-Dist: psycopg_pool>=3.2
Requires-Dist: pgvector>=0.3.6
Requires-Dist: neo4j<7,>=6.2
Requires-Dist: httpx>=0.27
Requires-Dist: langchain-core<2,>=1.4
Requires-Dist: pyyaml>=6.0
Requires-Dist: click>=8.1
Requires-Dist: protobuf>=5.27
Requires-Dist: mcp<2,>=1.9
Provides-Extra: dev
Requires-Dist: pytest>=8.2; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: ruff>=0.5; extra == "dev"
Requires-Dist: grpcio-tools>=1.64; extra == "dev"
Provides-Extra: web
Requires-Dist: fastapi>=0.115; extra == "web"
Requires-Dist: uvicorn[standard]>=0.30; extra == "web"
Dynamic: license-file

# rag_for_git

> **An AI pull-request reviewer that reads your whole repository — hybrid RAG + a code graph + Claude Code.**
> Plain linters check a diff in isolation; this agent gives the model the same context a human
> reviewer has — semantic + lexical retrieval over the entire repo and a structural code graph —
> then posts the result back to GitHub as **inline comments on the exact diff lines, with applyable fixes**.

[![PyPI](https://img.shields.io/pypi/v/rag-reviewer?color=2563eb&label=PyPI)](https://pypi.org/project/rag-reviewer/)
[![Python 3.11–3.13](https://img.shields.io/badge/python-3.11%E2%80%933.13-2563eb)](https://pypi.org/project/rag-reviewer/)
[![License: MIT](https://img.shields.io/badge/license-MIT-22c55e)](LICENSE)
[![MCP server](https://img.shields.io/badge/MCP-server-8b5cf6)](#mcp-tools-reference)
[![Claude Code plugin](https://img.shields.io/badge/Claude%20Code-plugin-d97757)](#claude-code-plugin-marketplace)

> 🇷🇺 Русская версия — глубокий, сверенный с кодом разбор: [README.ru.md](README.ru.md)

---

## Table of contents

- [Why it exists](#why-it-exists)
- [What a review looks like](#what-a-review-looks-like)
- [Highlights](#highlights)
- [How a review runs](#how-a-review-runs)
- [How it works / Architecture](#how-it-works--architecture)
- [One-click install prompt](#one-click-install-prompt)
- [Installation](#installation)
- [Configuration reference](#configuration-reference)
- [CLI reference](#cli-reference)
- [Skills reference](#skills-reference)
- [MCP tools reference](#mcp-tools-reference)
- [Plugin usage](#plugin-usage)
- [Per-repo policy & task board](#per-repo-policy--task-board)
- [Observability web admin](#observability-web-admin)
- [Known limitations & caveats](#known-limitations--caveats)
- [Tests](#tests)
- [Project layout](#project-layout)
- [Contributing](#contributing)
- [License](#license)

---

## Why it exists

Plain linters catch syntax and style but miss **meaning and relationships** — the things a human
reviewer actually looks for:

- a changed function contract that silently breaks its callers,
- a guard clause removed three files away from where it mattered,
- a change that contradicts an existing test,
- an edge case that only shows up once you read the helper it calls.

Catching those needs **context beyond the diff**: who calls this, what it implements, which tests
pin its behaviour. `rag_for_git` gives the model that context — semantic + lexical retrieval over
the **whole repository** and a structural **code graph** — then runs an agentic tool loop per
changed file and posts the result back to GitHub as **inline comments on the exact diff lines,
plus a summary and applyable fixes**.

It is not a wrapper around "send the diff to an LLM." It is a retrieval + code-graph pipeline with
a deterministic, anti-hallucination publishing tail.

## What a review looks like

An *illustrative* inline comment the agent posts on a changed line — it found the bug by following
the call graph from the edited function to its callers and a contract test:

> **🟠 correctness — an expired token is no longer rejected**
>
> `verify_token` used to raise on an expired signature; the new guard only checks `payload is
> None`, so `_decode()` returning a payload with a past `exp` now passes as valid. Two call sites
> depend on that raise — `require_auth` (`auth/deps.py:48`) and the contract test
> `test_expired_rejected` (`tests/test_auth.py:71`), which this change would break.
>
> ```suggestion
>     if payload is None or payload.get("exp", 0) < now:
>         raise InvalidToken("expired or malformed token")
> ```
> <!-- ai-review:9f3c2a -->

Every finding is grounded on an **exact quote** from the diff, carries a category / severity /
confidence, and — when it's safe — ships as a one-click GitHub `suggestion`. A hidden fingerprint
(`<!-- ai-review:… -->`) makes re-runs **idempotent**: the same issue is never posted twice.

## Highlights

- **Whole-repo context, not just the diff.** Hybrid retrieval (pgvector ANN + BM25, fused with RRF,
  reranked by Voyage) over the entire indexed codebase — for changed files the agent sees the new
  version, for everything else a stable base index.
- **It sees impact.** A Neo4j code graph (`CALLS` / `IMPLEMENTS`) expands each changed symbol 1–2
  hops to surface callers, callees, implementations, and the tests that pin them.
- **Anti-hallucination by construction.** A finding must quote real code to be placed on a line; a
  dedicated **verify** pass drops invented findings; line grounding is exact-match.
- **Real GitHub output.** Inline comments on diff lines, applyable `suggestion` blocks under safe
  invariants, a summary for everything off-diff — idempotent across re-runs.
- **Lives in your editor, not a CI black box.** Ships as a **Claude Code plugin** and as an **MCP
  server** usable from 12+ AI clients (Cursor, VS Code, Gemini CLI, Codex, Windsurf, Claude
  Desktop, …). One `uvx` command; published on [PyPI](https://pypi.org/project/rag-reviewer/).
- **Local-first.** Your code stays on your machine — only embedding/query text goes to Voyage; the
  stores (Postgres/ParadeDB + Neo4j) run in local Docker.
- **More than review.** The same RAG + graph powers grounded codebase **Q&A** (`ask`), disciplined
  task-context gathering (`solve-task`), PR **walkthroughs**, and per-subsystem summaries.

## How a review runs

A single PR review is three stages:

**`prepare_review` (MCP)** → **analyze (Claude subagents)** → **`publish_review` (MCP)**

1. **prepare** — `GitHubProvider` pulls the PR (base/head SHA) and changed files; changed `.py`
   files are chunked (tree-sitter) and embedded (Voyage) into an ephemeral overlay `ref="pr:N"`;
   policy and per-file review units are assembled.
2. **analyze** — the Claude Code skill fans out one subagent per file. Each reasons over the diff
   in a tool loop, pulling in whatever code it needs: `search_code`, `get_related_symbols`,
   `read_file`, `get_definition`, `find_callers`, `get_changed_file_diff`. In parallel,
   dimension subagents run a **performance** and **maintainability** pass, plus a **requirements**
   pass when a task board is wired up, and a final **verify** pass strips hallucinations.
3. **publish** — a deterministic tail: policy gate (category/severity/confidence/paths) → line
   grounding by exact code quote (anti-hallucination) → dedup → assemble (inline vs summary,
   suggestion invariants, fingerprint idempotency, comment cap) → post to GitHub → history record
   → overlay/session cleanup.

> Status: working v1. Target analysis language is **Python**; VCS is **GitHub** (behind a
> `VCSProvider` interface). Proven live: it catches real bugs and sees the impact on calling code
> and existing tests.

## How it works / Architecture

The core is the `reviewer/` library, assembled in `reviewer/app.py::build_components(settings)`
from `Settings` (pydantic-settings, `.env`). Entry points are `reviewer/entrypoints/cli.py` (Click)
and `reviewer/entrypoints/mcp_server.py` (FastMCP). Three pieces work together:

- **RAG (hybrid retrieval).** Postgres/ParadeDB stores code chunks with `pgvector` (HNSW ANN) and
  `pg_search` (BM25). A query embeds with Voyage, runs both ANN and BM25 search, and the result
  lists are merged with **Reciprocal Rank Fusion (RRF)**, then reranked with Voyage `rerank-2.5`.
- **Code graph (SCIP or tree-sitter, Neo4j).** Symbols and their relationships live in Neo4j.
  The graph orchestrator (`graph/backend.py`) picks a backend via `GRAPH_BACKEND`
  (`auto|scip|treesitter`): **SCIP** (`@sourcegraph/scip-python`) gives a precise, type-aware graph
  with `CALLS` + `IMPLEMENTS` edges; **tree-sitter** is a fast fallback with `CALLS`-by-name only.
  Retrieval expands the changed symbols 1–2 hops to surface callers/callees/implementations/tests.
- **Claude Code plugin via MCP.** The `reviewer-mcp` server exposes `prepare_review`,
  `publish_review`, and the agent tools. The Claude Code plugin (`plugin/`) drives the review: it
  calls `prepare_review`, runs analysis subagents against those MCP tools, then calls
  `publish_review`.

**The single key linking RAG and the graph is `node_id = "path#fqn"`** (e.g.
`rag/embedder.py#VoyageEmbedder.embed_query`). Both the chunk in Postgres and the node in Neo4j use
it, so graph expansion and chunk retrieval are stitched together without any mapping table.

**Index freshness: a stable base + a PR overlay.** A full reindex of a large repo is expensive, so
the index keeps a persistent base and layers PR changes on top:

- **`ref="base:<branch>"`** — the persistent index of a tracked branch (e.g. `"base:main"`,
  `"base:master"`). Each tracked branch in `REVIEW_BRANCHES` has its own isolated index. Updated
  incrementally by `reviewer index --ref <branch>` (only changed files are chunked; only chunks
  with a new `content_hash` are re-embedded — embeddings are reused across branches by hash,
  saving Voyage quota).
- **`ref="pr:N"`** — an ephemeral overlay of just the PR's changed files at its HEAD.
- **On a query**: `retrieval = (base:<branch> where path ∉ changed) ∪ overlay`. For changed files
  the agent sees the **new** version; for everything else, the stable base.
- **Multi-branch.** A PR is reviewed against the index of its target branch (`base_ref` from the
  PR). A PR targeting an untracked branch is skipped (`prepare_review` returns
  `{"status":"skipped",...}`). The code graph (Neo4j `:Symbol`) is also branch-scoped via a
  `branch` property, with unique constraint `(repo, branch, id)`.

```
                ┌─────────────────────────── reviewer (core library) ───────────────────────────┐
                │                                                                                 │
  GitHub PR ───▶│  VCSProvider (github.py)  ──diff/files/patches──▶  MCPReviewService             │
  (owner/repo#N)│        ▲  publish inline + summary                       │ prepare_review        │
                │        │                                                 ▼                       │
                │        │                          ┌──────────── retrieval/Retriever ──────────┐ │
                │        │                          │  hybrid search        graph expansion      │ │
                │        │                          │  ┌──────────────┐   ┌───────────────────┐  │ │
                │        │                          │  │ Postgres      │   │ Neo4j             │  │ │
                │        │                          │  │ (ParadeDB)    │   │ Symbol(path#fqn)  │  │ │
                │        │                          │  │ pgvector(HNSW)│   │ -[:CALLS]->        │  │ │
                │        │                          │  │ + pg_search   │   │ (IMPLEMENTS: SCIP) │  │ │
                │        │                          │  │   (BM25, RRF) │   │ expand 1–2 hops    │  │ │
                │        │                          │  └──────┬───────┘   └─────────┬─────────┘  │ │
                │        │                          │   Voyage embed/rerank   tree-sitter graph  │ │
                │        │                          └─────────────────┬─────────────────────────┘ │
                │        │                                            ▼ ContextPack                │
                │        │                       Claude Code subagents (skill /rag-reviewer:reviewer_review-pr)
                │        │                         tools: search_code, get_related_symbols,        │
                │        │                         read_file, get_definition, find_callers, …      │
                │        └─────────────────── publish_review (gate/grounding/dedup/assemble) ◀─────┘
                └─────────────────────────────────────────────────────────────────────────────────┘

  Stores (Docker):  Postgres/ParadeDB (:5433)  ·  Neo4j (:7687)
  External API:     Voyage (embeddings voyage-code-3 + reranker rerank-2.5)
```

For a deeper, code-verified walkthrough of every module and the data flow, see
[README.ru.md](README.ru.md) (Russian).

## One-click install prompt

Copy and paste into any AI coding assistant (Claude Code, Cursor, Gemini CLI, Mimo Code, OpenCode, Kimi Code, Codex, Copilot…):

```
Set up the rag-reviewer MCP server in this AI tool.

Rules:
- Use `uvx --from rag-reviewer@latest reviewer-mcp` to run the server — do NOT pip install, do NOT clone the repo.
- The `bash -lc` wrapper is required on macOS/Linux so uvx is found by GUI tools: command="/bin/bash", args=["-lc", "uvx --from rag-reviewer@latest reviewer-mcp"]. On Windows use command="uvx", args=["--from","rag-reviewer@latest","reviewer-mcp"].
- Detect which AI tool you are running in, then write the MCP entry to the correct config file:
  - Claude Code / generic: .mcp.json in project root ({"mcpServers":{"reviewer":{"command":"/bin/bash","args":["-lc","uvx --from rag-reviewer@latest reviewer-mcp"]}}})
  - Cursor: ~/.cursor/mcp.json (same format)
  - Gemini CLI: ~/.gemini/settings.json ({"mcpServers":{"reviewer":{"command":"/bin/bash","args":["-lc","uvx --from rag-reviewer@latest reviewer-mcp"]}}})
  - Antigravity: ~/.gemini/antigravity/mcp_config.json (same mcpServers format)
  - Windsurf: ~/.codeium/windsurf/mcp_config.json (same mcpServers format)
  - Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json on macOS, %APPDATA%\Claude\claude_desktop_config.json on Windows (same mcpServers format)
  - Mimo Code: ~/.config/mimocode/mimocode.json ({"mcp":{"reviewer":{"type":"local","command":["/bin/bash","-lc","uvx --from rag-reviewer@latest reviewer-mcp"],"enabled":true}}})
  - OpenCode: ~/.config/opencode/opencode.json ({"mcp":{"reviewer":{"type":"local","command":["/bin/bash","-lc","uvx --from rag-reviewer@latest reviewer-mcp"]}}})
  - Kimi Code: ~/.kimi-code/mcp.json ({"mcpServers":{"reviewer":{"command":"/bin/bash","args":["-lc","uvx --from rag-reviewer@latest reviewer-mcp"]}}})
  - Codex CLI: ~/.codex/config.toml ([mcp_servers.reviewer] command="/bin/bash" args=["-lc","uvx --from rag-reviewer@latest reviewer-mcp"])
  - VS Code: ~/Library/Application Support/Code/User/mcp.json (key is "servers" not "mcpServers": {"servers":{"reviewer":{"command":"/bin/bash","args":["-lc","uvx --from rag-reviewer@latest reviewer-mcp"]}}})
- After writing the config, run: uvx --from rag-reviewer reviewer check
- Report what config file was written and whether the check passed.
```

---

## Installation

The MCP server is published on PyPI as [`rag-reviewer`](https://pypi.org/project/rag-reviewer/)
and runs via `uvx` — **no clone of this repo required**.

Requirements: Docker, [`uv`](https://docs.astral.sh/uv/getting-started/installation/) (includes `uvx`),
a Voyage API key, a GitHub token. Python 3.11–3.13 (only needed for a `pip`/editable install; `uvx`
manages its own).

### Quick setup (recommended, all platforms)

```bash
# 0) Install the reviewer CLI — once, globally
uv tool install rag-reviewer
# uv and uvx are the same binary; installing uv gives you both.
# The MCP server launched by your editor uses uvx @latest and self-updates automatically.

# 1) Infrastructure
curl -O https://raw.githubusercontent.com/mimfort/rag_for_git/main/docker-compose.yml
docker compose up -d          # Postgres/ParadeDB (:5433) + Neo4j (:7687)

# 2) Configure keys and settings interactively
reviewer init
#    Interactive wizard: fills VOYAGE_API_KEY, GITHUB_TOKEN, and optional groups
#    (stores, multi-repo, task board). Re-run any time to update settings.
#    CI / non-interactive: reviewer init --yes  (accepts all defaults silently)

# 3) Register the MCP server (and skills) in your editor/CLI
reviewer install --all        # auto-detect installed clients + install skills
#    or a specific one: reviewer install cursor|vscode|claude-code|claude-desktop|windsurf|gemini|antigravity|mimo|opencode|kimi|trae|codex
#    skills go to clients that support them (Gemini/Mimo/Kimi); add --no-skills to skip

# 4) Verify
reviewer check

# Update CLI later:
uv tool upgrade rag-reviewer
```

> **`reviewer install` is cross-platform** (Windows / macOS / Linux). It injects the
> absolute path to `uvx` automatically — no `bash -lc` wrapper needed. The manual
> JSON configs below use `bash -lc` for macOS/Linux only; on Windows use
> `reviewer install` or set `"command": "uvx"` with `"args": ["--from",
> "rag-reviewer@latest", "reviewer-mcp"]` directly.

> **Claude Code: tools work out of the box.** `reviewer install claude-code` also
> writes an allowlist rule `mcp__reviewer__*` into your global
> `~/.claude/settings.json` (`permissions.allow`), so the reviewer MCP tools run in
> **every** project without hitting the `auto`-mode safety classifier — no manual
> settings edits. Being global, it also covers the plugin (marketplace) install,
> where the server is available everywhere but ships no permission grants.

> **Where keys are read from.** The reviewer resolves its `.env` from a fixed
> location, **not** the current working directory — MCP clients launch the server
> with an arbitrary CWD, so a project-local `.env` is unreliable. Lookup order:
> `$REVIEWER_ENV_FILE` → `$XDG_CONFIG_HOME/rag-reviewer/.env` (default
> `~/.config/rag-reviewer/.env`) → `./.env` (handy when running from a repo clone).
> Real environment variables always win over the file, so you can instead pass keys
> via an `"env": { "VOYAGE_API_KEY": "…", "GITHUB_TOKEN": "…" }` block in your MCP
> client config — works in every client.

- **Voyage** (`VOYAGE_API_KEY`): https://dashboard.voyageai.com/ — free token pool; attach a card
  to lift the 3 RPM / 10K TPM limit (charged only beyond the free pool).
- **GitHub** (`GITHUB_TOKEN`): a PAT with *Pull requests: Read and write* + *Contents: Read*
  (fine-grained) or the `repo` scope (classic). Quick option: `gh auth token`.

All other settings have defaults (documented in `.env.example` and in
[Configuration reference](#configuration-reference) below).

### Manual setup (alternative)

If you prefer to configure your client config by hand rather than using `reviewer install`:

Each AI coding tool has its own config file. Pick yours:

| Tool | Global config file | Project config | Install guide |
|---|---|---|---|
| **Claude Code** | `/plugin marketplace add` (see below) | `.claude-plugin/` ✓ | — |
| **Cursor** | `~/.cursor/mcp.json` | `.cursor/mcp.json` ✓ | — |
| **Windsurf** | `~/.codeium/windsurf/mcp_config.json` | — | — |
| **Claude Desktop** | macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`; Windows: `%APPDATA%\Claude\claude_desktop_config.json` | — | — |
| **Antigravity** | `~/.gemini/antigravity/mcp_config.json` | — | — |
| **Mimo Code** | `~/.config/mimocode/mimocode.json` | `.mimocode/mimocode.json` ✓ | [INSTALL.md](.mimocode/INSTALL.md) |
| **OpenCode** | `~/.config/opencode/opencode.json` | `.opencode/opencode.json` ✓ | [INSTALL.md](.opencode/INSTALL.md) |
| **Kimi Code** | `~/.kimi-code/mcp.json` | `.kimi-code/mcp.json` ✓ | [INSTALL.md](.kimi-code/INSTALL.md) |
| **Gemini CLI** | `~/.gemini/settings.json` | `.gemini/settings.json` ✓ | [GEMINI.md](GEMINI.md) |
| **Codex CLI** | `~/.codex/config.toml` | `.codex-plugin/plugin.json` ✓ | [AGENTS.md](AGENTS.md) |
| **Copilot CLI** | — | `.github-copilot/plugin.json` ✓ | — |
| **Trae IDE** | `~/Library/Application Support/Trae/User/mcp.json` | — | — |
| **VS Code** | `~/Library/Application Support/Code/User/mcp.json` (key: `servers`, not `mcpServers`) | — | — |

Files marked ✓ are already present in this repo — if you open rag_for_git as a project in
that tool, the MCP server auto-connects. For a **global install** (works from any project),
add the entry to the corresponding global config file.

The MCP entry format by tool (macOS/Linux — use `reviewer install` on Windows):

**Mimo Code** (`mimocode.json`):
```json
{
  "$schema": "https://mimo.xiaomi.com//config.json",
  "mcp": {
    "reviewer": {
      "type": "local",
      "command": ["/bin/bash", "-lc", "uvx --from rag-reviewer@latest reviewer-mcp"],
      "enabled": true
    }
  }
}
```

**OpenCode** (`opencode.json`):
```json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "reviewer": {
      "type": "local",
      "command": ["/bin/bash", "-lc", "uvx --from rag-reviewer@latest reviewer-mcp"]
    }
  }
}
```

**Kimi Code / Cursor / Gemini CLI / Codex CLI / Trae / Claude Desktop / Windsurf / Antigravity** (standard `mcpServers` JSON):
```json
{
  "mcpServers": {
    "reviewer": {
      "command": "/bin/bash",
      "args": ["-lc", "uvx --from rag-reviewer@latest reviewer-mcp"]
    }
  }
}
```

**VS Code** (`mcp.json` — note: key is `servers`, not `mcpServers`):
```json
{
  "servers": {
    "reviewer": {
      "command": "/bin/bash",
      "args": ["-lc", "uvx --from rag-reviewer@latest reviewer-mcp"]
    }
  }
}
```

**Codex CLI** (`~/.codex/config.toml`):
```toml
[mcp_servers.reviewer]
command = "/bin/bash"
args = ["-lc", "uvx --from rag-reviewer@latest reviewer-mcp"]
```

After adding, restart the tool — `reviewer` will appear alongside other MCP servers.

### Claude Code (plugin marketplace)

Two commands, from any project:

```text
/plugin marketplace add mimfort/rag_for_git
/plugin install rag-reviewer@rag-reviewer-marketplace
```

You get:

- **Skills:** `/rag-reviewer:reviewer_review-pr`, `/rag-reviewer:reviewer_solve-task`,
  `/rag-reviewer:reviewer_sync-codebase`, `/rag-reviewer:reviewer_sync-tasks`,
  `/rag-reviewer:reviewer_performance-review`, `/rag-reviewer:reviewer_maintainability-review`,
  `/rag-reviewer:reviewer_ask` (see [Skills reference](#skills-reference)).
- **MCP server** `reviewer` exposing the 20 tools in [MCP tools reference](#mcp-tools-reference).

> Run `/plugin` to confirm `rag-reviewer` is installed and enabled.

### Install skills globally (optional)

The 7 skills wrap the MCP tools into guided flows. Without them you can still call MCP tools
directly, but the skills are the intended entry point.

**`reviewer install` already installs them** for clients that support file-based skills (Gemini,
Mimo, Kimi). To (re)install just the skills — or pick a specific client — use:

```bash
uvx --from rag-reviewer reviewer install-skills --all     # all detected skills-capable clients
uvx --from rag-reviewer reviewer install-skills gemini    # a specific one
uvx --from rag-reviewer reviewer install-skills --list    # show targets + directories
```

It downloads the skills from GitHub (no repo clone) and unpacks them into each client's global
skills directory, with a path-traversal guard. Manual fallback (equivalent):

```bash
curl -sL https://github.com/mimfort/rag_for_git/archive/refs/heads/main.tar.gz -o /tmp/rag-reviewer.tgz
mkdir -p ~/.gemini/skills
tar xz -C ~/.gemini/skills --strip-components=3 -f /tmp/rag-reviewer.tgz 'rag_for_git-main/plugin/skills'
rm /tmp/rag-reviewer.tgz
```

| Tool | Global skills directory |
|---|---|
| Gemini CLI | `~/.gemini/skills/` |
| Mimo Code | `~/.config/mimocode/skills/` |
| Kimi Code | `~/.kimi-code/skills/` + `extra_skill_dirs` in `~/.kimi-code/config.toml` |
| OpenCode | `~/.config/opencode/skills/` |
| Claude Code | bundled in the plugin (step above) |
| Cursor | project-level via `.cursor-plugin/plugin.json` |

That's it. Build the base index (recommended — see [CLI reference](#cli-reference)) and review a PR
(see [Plugin usage](#plugin-usage)).

---

## Configuration reference

Everything is configured through environment variables (`.env`, see `.env.example` with comments).
The **only required external key is `VOYAGE_API_KEY`**; `GITHUB_TOKEN` is required for PR review.
All other settings have working defaults that match the bundled `docker-compose.yml`. The `.env`
is resolved from `$REVIEWER_ENV_FILE` → `~/.config/rag-reviewer/.env` → `./.env` (real env vars
always win).

### Voyage — embeddings + reranker (required)

| Variable | Default | Purpose |
|---|---|---|
| `VOYAGE_API_KEY` | `""` | **Required.** Voyage key for embeddings + reranking. |
| `EMBEDDING_MODEL` | `voyage-code-3` | Embedding model. |
| `EMBEDDING_DIM` | `1024` | Embedding dimension; **must match** the `vector(N)` column in Postgres — changing it requires a reindex. |
| `EMBEDDING_BATCH_SIZE` | `256` | Texts per embedding request (≤1000 and ≤120K tokens). |
| `RERANK_MODEL` | `rerank-2.5` | Voyage reranker model. |

### GitHub (required for PR review)

| Variable | Default | Purpose |
|---|---|---|
| `GITHUB_TOKEN` | `""` | PAT — *Pull requests: Read and write* + *Contents: Read*. |
| `GITHUB_RETRY_ATTEMPTS` | `3` | Retries on GitHub API network errors. |
| `GITHUB_RETRY_BACKOFF_BASE` | `1.0` | Exponential backoff base (seconds). |

### Stores (Postgres/ParadeDB + Neo4j)

| Variable | Default | Purpose |
|---|---|---|
| `PG_DSN` | `postgresql://reviewer:reviewer@localhost:5433/reviewer` | ParadeDB (pgvector + pg_search) on host port **5433**. |
| `PG_POOL_MIN_SIZE` | `1` | Min Postgres pool connections. |
| `PG_POOL_MAX_SIZE` | `4` | Max Postgres pool connections. |
| `NEO4J_URI` | `neo4j://localhost:7687` | Neo4j bolt URI. |
| `NEO4J_USER` | `neo4j` | Neo4j user. |
| `NEO4J_PASSWORD` | `reviewerpass` | Neo4j password (one-off dev default). |
| `GRAPH_BACKEND` | `auto` | Code-graph engine: `auto` (SCIP if `scip-python` in PATH, else tree-sitter), `scip`, `treesitter`. |

### Multi-repo / multi-branch (optional)

| Variable | Default | Purpose |
|---|---|---|
| `DEFAULT_REPO` | `""` | Default `owner/name` for session-less tools and `reviewer index` without `--repo`; empty = multi-repo (repo must be passed explicitly). |
| `REVIEW_BRANCHES` | `main` | CSV of tracked branches; the first is **primary** (default for `reviewer index --ref` and CLI search). PRs targeting a branch outside the list are skipped. |

### Review policy (env defaults; per-repo `.review.yml` overrides)

| Variable | Default | Purpose |
|---|---|---|
| `REVIEW_SEVERITY_THRESHOLD` | `medium` | Minimum severity to keep: `low`/`medium`/`high`/`critical`. |
| `REVIEW_MIN_CONFIDENCE` | `0.5` | Drop findings with confidence below this (0..1). |
| `REVIEW_MAX_COMMENTS` | `25` | Cap on inline comments per review. |
| `REVIEW_MAX_FILES` | `50` | Cap on `.py` files reviewed; the rest go to the summary as skipped. |
| `REVIEW_CATEGORIES` | `""` | CSV whitelist of categories (`correctness`, `security`, `performance`, `style`, `requirements`); empty = all. |
| `REVIEW_SUGGESTIONS` | `apply` | `apply` = applyable GitHub `suggestion` blocks; `text` = text-only advice. |
| `REVIEW_OUTPUT_LANGUAGE` | `ru` | Language of the published findings' text. |
| `REVIEW_SKIP_DRAFTS` | `true` | Don't review draft PRs. |
| `MAX_TOOL_RESULT_CHARS` | `8000` | Max length of a tool result fed into the prompt. |

### Observability & sessions (optional)

| Variable | Default | Purpose |
|---|---|---|
| `REVIEW_HISTORY` | `true` | Record run history in Postgres (`review_runs`/`review_findings`), fail-soft. |
| `REVIEW_SESSION_PERSIST` | `true` | Persist the PR session in Postgres for crash recovery. |
| `REVIEW_SESSION_TTL_HOURS` | `24` | TTL (hours) of a persisted session. |
| `WEB_ADMIN_USER` | `""` | Basic-auth user for `reviewer serve`; empty = no auth. |
| `WEB_ADMIN_PASSWORD` | `""` | Basic-auth password; empty = no auth. |

### Task board (optional) — deploy-wide default

A board connection is the same for every repo of one team, so it is configured **once** in the
reviewer-mcp env rather than duplicated in each repo's `.review.yml`. See
[Per-repo policy & task board](#per-repo-policy--task-board).

| Variable | Default | Purpose |
|---|---|---|
| `TASK_BOARD_TYPE` | `""` | Board type: `yougile`, `jira`, … (selects the skill playbook). |
| `TASK_BOARD_MCP` | `""` | Name of the connected board MCP server (LLM-side tools `mcp__<mcp>__*`). |
| `TASK_BOARD_KEY_PATTERN` | `""` | Task-key regex, e.g. `[A-Z]+-\d+`. |
| `TASK_BOARD_URL_TEMPLATE` | `""` | Task-link template, e.g. `https://ru.yougile.com/team/<id>/#{code}`. |
| `TASK_BOARD_API_KEY` | `""` | **REST API key** for server-side bulk sync (`sync_board`). Server-internal — never returned to clients. |
| `TASK_BOARD_API_BASE` | `""` | REST API base URL; empty → default per type (`yougile`: `https://yougile.com/api-v2`). |

> **Getting `TASK_BOARD_API_KEY` (Yougile).** UI: press `Ctrl + ~` (or ⚙ next to the company name →
> "Настроить") → **API** → create/copy the key. REST: get `companyId` (`Ctrl + Alt + Q`, or
> `POST /api-v2/auth/companies {login,password}`), then `POST /api-v2/auth/keys {login,password,companyId}`.
> The key belongs **only** in the reviewer-mcp env (`~/.config/rag-reviewer/.env`), not in a chat or a client config.

---

## CLI reference

All commands run via `uvx --from rag-reviewer <command>`, or after `uv tool install rag-reviewer` /
`pip install -e ".[dev]"` simply as `reviewer`. Two entry points are installed:
`reviewer` (the CLI below) and `reviewer-mcp` (the MCP server, started by your editor/plugin).

| Command | Arguments | Options | What it does |
|---|---|---|---|
| `check` | — | — | Verify environment readiness (keys, Postgres, Neo4j, GitHub). Prints ✓/✗ per item; exits 1 on any problem. Spends no Voyage quota. |
| `init` | — | `--path FILE` (default `~/.config/rag-reviewer/.env`), `--yes` (accept defaults, CI mode) | Interactive wizard that writes the `.env` (Voyage/GitHub + optional groups). |
| `install` | `[client]` | `--all`, `--list`, `--path FILE`, `--pin VERSION`, `--no-latest`, `--no-skills`, `--dry-run` | Register the MCP server (and skills) in AI clients (cross-platform). |
| `install-skills` | `[client]` | `--all`, `--list`, `--path FILE` | Install only the skills into a client's global skills directory. |
| `update` | — | — | Check PyPI for a newer `rag-reviewer` and report how to upgrade. |
| `index` | `<repo>` (path to local clone) | `--ref BRANCH` (git ref to read; default = primary branch), `--branch NAME` (storage key; default = `--ref`), `--repo OWNER/NAME` (default from git `origin`) | Build/update the base index of a branch (vectors + graph). Done once, then incremental. |
| `search` | `<query>` | `--repo OWNER/NAME` (default `DEFAULT_REPO`), `--branch NAME` (default primary) | Diagnostic hybrid search over a branch's base index. |
| `status` | `[path]` (default `.`) | `--repo OWNER/NAME` (default from git `origin`), `--branch NAME` (default: all `REVIEW_BRANCHES`) | Index health / freshness vs the clone's HEAD. Spends no Voyage quota. |
| `migrate-branches` | — | — | One-time: rename legacy `ref="base"` → `base:<primary>` after upgrading to multi-branch. |
| `serve` | — | `--host HOST` (default `127.0.0.1`), `--port PORT` (default `8000`) | Run the observability web admin on the host. |
| `reviewer-mcp` | — | — | MCP server (stdio transport). Started automatically by the plugin / editor. |

Examples:

```bash
# First-time setup
uvx --from rag-reviewer reviewer init
uvx --from rag-reviewer reviewer install --all
uvx --from rag-reviewer reviewer check

# Build the base index (whole-repo context for RAG + graph)
uvx --from rag-reviewer reviewer index /path/to/repo --ref main --repo owner/name
uvx --from rag-reviewer reviewer index /path/to/repo --ref master --repo owner/name   # second tracked branch

# Diagnostics (no Voyage spend except `search`)
uvx --from rag-reviewer reviewer search "token verification" --branch master
uvx --from rag-reviewer reviewer status /path/to/repo --branch dev

# Web admin
uvx --from rag-reviewer reviewer serve --host 127.0.0.1 --port 8000
```

Reviewing works even without a prior `index` — context is then limited to the diff and the overlay
(RAG/graph are "thin"). For full whole-repo impact analysis, run `index` against the target branch.

---

## Skills reference

Skills are the guided entry points for the workflow. With the plugin installed they are invoked as
`/rag-reviewer:<name>` in Claude Code (the leading `/rag-reviewer:` is the plugin namespace; on
other clients the skill name is the same). Arguments are passed as free text after the skill name
(`$ARGUMENTS`).

### `reviewer_review-pr` — full PR review

Orchestrates the three-stage pipeline (`prepare_review` → subagents → `publish_review`).

- **Arguments:** the PR as `owner/repo#N`, `owner/repo N`, or a GitHub PR URL. Add `--dry-run` to
  assemble and return the full report **without** posting to GitHub.
- **MCP tools used:** `prepare_review`, `search_code`, `get_related_symbols`, `read_file`,
  `get_definition`, `find_callers`, `get_changed_file_diff`, `publish_review`; plus
  `index_task` / `get_task_context` / `search_tasks` when a task board is wired up.
- **Flow:** prepare (PR + policy + units + board config) → fan out one analysis subagent per file →
  parallel **performance** / **maintainability** dimensions (+ **requirements** if a `TaskBrief`
  exists) → **verify** pass (drops `is_real=false` findings) → publish (gate/grounding/dedup/assemble).
  If `prepare_review` returns `status:"skipped"` (target branch not tracked) it stops; draft PRs are
  skipped unless `REVIEW_SKIP_DRAFTS=false`.

### `reviewer_solve-task` — gather context for a task, then hand off to dev

Reads a task (if a key + board), pulls related/similar tasks and relevant code, distills a brief,
and enters brainstorming. It disciplines context-gathering — it does **not** write the code.

- **Arguments:** a task key (e.g. `PRI-4`, must match `key_pattern`) **or** a free-text description
  (e.g. "add a logout endpoint"). Board-less mode falls back to description + code search.
- **MCP tools used:** `get_board_config`, `index_task`, `get_task_context`, `search_tasks`,
  `search_codebase`, `related_symbols`, `callers`, `definition`, `get_pr_diff`; plus the connected
  board MCP (`mcp__<board>__*`) to read the task.
- **Flow:** resolve board config → identify task (key vs free text) → best-effort, fail-open context
  gathering (task graph, similar tasks, relevant code, lazy PR diffs of similar tasks) → distill a
  structured brief (Task / Related work / Relevant code / Constraints) → hand off to
  `superpowers:brainstorming` with the brief as seed.

### `reviewer_sync-codebase` — build/update the base index

Thin wrapper over `reviewer index` (vector store + code graph) from a local clone.

- **Arguments (all optional):** `--path <path>` (default: CWD), `--ref <branch>` (default: `main`),
  `--repo <owner/name>` (default: derived from `git remote get-url origin`),
  `--backend <auto|scip|treesitter>` (default: `auto`, sets `GRAPH_BACKEND`).
- **MCP tools used:** none directly — it shells out to `uvx --from rag-reviewer reviewer index`.
- **Flow:** resolve inputs → check prerequisites (`uvx`, git repo, `reviewer check`, Docker up) →
  run indexing → optional `reviewer search` to verify → report chunks/nodes/edges and which graph
  backend was used.

### `reviewer_sync-tasks` — warm the task graph & vector store

A thin trigger over the server-side ETL tool `sync_board` — the reviewer enumerates the board over
REST itself, so the LLM passes no task text (O(1) tokens regardless of board size).

- **Arguments (all optional):** `--board <name>` (limit to one board/project), `--limit <N>` (smoke
  run; **disables purge and watermark advance**), `--purge-orphaned` (remove tasks no longer on the
  board; off by default), `--no-keep-with-prs` (with purge, also remove tasks that have PR history —
  protected by default).
- **MCP tools used:** `sync_board` (single call).
- **Flow:** map args → one `sync_board(...)` call → print a counts summary (enumerated/changed/
  embedded/unchanged/failed, purge, warnings). On `{"status":"error",...}` the board is not
  configured server-side — set `TASK_BOARD_*` in `~/.config/rag-reviewer/.env` and reconnect.

### `reviewer_performance-review` — performance-only review

Reviews a diff only for performance/efficiency risks (N+1 queries, repeated work, bad asymptotics,
missing batching/caching, blocking I/O, memory growth).

- **Arguments (standalone):** scope — `staged`, `unstaged`, uncommitted, branch-vs-base, a commit,
  a branch comparison, a file list, or a PR-like scope. If unclear, it asks. Inside
  `reviewer_review-pr` it runs as a dimension over the provided unit diffs.
- **MCP tools used (when run in the PR pipeline):** `search_code`, `read_file`, `find_callers`,
  `get_related_symbols`, `get_definition`, `get_changed_file_diff`.
- **Output:** JSON `{"findings":[{category:"performance", severity, file, line, side, code_quote,
  message, suggestion, fix, confidence}]}`.

### `reviewer_maintainability-review` — maintainability-only review

Reviews a diff only for maintainability risks (unnecessary complexity, poor readability,
duplication, weak separation of concerns, convention drift).

- **Arguments (standalone):** same scope options as the performance review. Inside
  `reviewer_review-pr` it runs as a dimension over the provided unit diffs.
- **MCP tools used (when run in the PR pipeline):** `search_code`, `get_related_symbols`,
  `read_file`, `get_definition`, `find_callers`, `get_changed_file_diff`.
- **Output:** JSON `{"findings":[{category:"maintainability", severity, file, line, side, code_quote,
  message, suggestion, fix, confidence}]}`.

### `reviewer_ask` — grounded codebase Q&A

Answers a free-text question about the codebase with citations (`path:line`), using RAG + the code
graph. For onboarding / explaining a subsystem — **not** for reviewing PRs. Requires a built base
index.

- **Arguments:** a free-text question (e.g. "where is authentication", "how does index freshness
  work", "explain the retrieval pipeline", "как устроено…").
- **MCP tools used:** `search_codebase`, `related_symbols`, `callers`, `definition`; plus harness
  `Read`/`Grep`/`Glob`.
- **Flow:** resolve repo/branch → `search_codebase` → optionally expand via the graph → answer with
  an Evidence list of `path:line` citations.

---

## MCP tools reference

The `reviewer-mcp` server exposes 20 tools. PR-session tools require an active `prepare_review` for
that `(repo, pr)` in the same running server; the rest are session-less.

### Review lifecycle

| Tool | Signature | Returns / does |
|---|---|---|
| `prepare_review` | `(repo: str, pr: int)` | Open a PR session: sync base index, build the PR overlay, load policy, assemble per-file units. Returns PR meta + policy + units (or `{"status":"skipped"}` for an untracked target branch). |
| `publish_review` | `(repo, pr, summary, findings: list[dict], dry_run=False, task_key=None)` | Deterministic tail: gate → grounding → dedup → inline/summary split → post to GitHub → history → overlay cleanup. `dry_run=true` returns the report without posting; `task_key` links the PR to a task on real publish. |

Each finding: `{category, severity(low|medium|high|critical), file, line, side(RIGHT|LEFT),
code_quote, message, suggestion, fix:{start_line,end_line,replacement}|null, confidence:0..1}`.

### PR-session analysis tools (require `prepare_review`)

| Tool | Signature | Returns / does |
|---|---|---|
| `search_code` | `(repo, pr, query: str)` | Hybrid semantic+lexical search over `base ∪ overlay`. |
| `get_related_symbols` | `(repo, pr, node_id: str)` | Graph neighbors (calls/implementations) of `node_id` = `path#fqn`. |
| `read_file` | `(repo, pr, path, start=1, end=400)` | Source of a file at the PR HEAD (1-based, inclusive). |
| `get_definition` | `(repo, pr, symbol: str)` | Definition of a symbol (graph → index → semantic fallback). |
| `find_callers` | `(repo, pr, node_id: str)` | Direct callers of `node_id` `path#fqn` (impact analysis). |
| `get_changed_file_diff` | `(repo, pr, path: str)` | Unified diff of another changed file in the same PR. |

### Session-less tools (Q&A, `solve-task`)

| Tool | Signature | Returns / does |
|---|---|---|
| `search_codebase` | `(repo, query, top_k=10, branch=None, include_tests=False)` | Hybrid search over a repo's base index; line-numbered, deduped, tests excluded by default. |
| `related_symbols` | `(repo, node_id, branch=None)` | Graph neighbors (calls/implements/tests) of a symbol. |
| `callers` | `(repo, node_id, branch=None)` | Incoming `CALLS` of `node_id` `path#fqn`. |
| `definition` | `(repo, symbol, branch=None)` | Symbol definition (graph → index → semantic fallback). |
| `get_pr_diff` | `(repo, number: int)` | Unified diff of any (historical) PR; capped, fail-soft. |

### Tasks / boards

| Tool | Signature | Returns / does |
|---|---|---|
| `sync_board` | `(board=None, limit=None, purge_orphaned=False, keep_with_prs=True)` | Server-side ETL: enumerate the board over REST, normalize to `TaskBrief`, index. Incremental via a per-board watermark; O(1) tokens. |
| `index_task` | `(task: dict)` | Index one normalized `TaskBrief` into the task graph + vector store (idempotent). |
| `index_tasks_batch` | `(tasks: list[dict])` | Same for a list, in one Voyage call. |
| `search_tasks` | `(query, top_k=5)` | Semantically similar tasks from the indexed corpus. |
| `get_task_context` | `(key: str)` | Graph context: the task, its PRs, linked tasks and their PRs, and the touched code. |
| `purge_orphaned_tasks` | `(active_keys: list[str], keep_with_prs=True)` | Remove tasks no longer on the board (PR-linked tasks protected by default). |
| `get_board_config` | `()` | Deploy-wide board config (`TASK_BOARD_*`); fallback for `sync-tasks`/`solve-task`. Credentials are **not** returned. |

---

## Plugin usage

With the plugin installed and Claude Code open at the repo root, call a skill:

```text
/rag-reviewer:reviewer_review-pr owner/repo#42        # review a PR (prepare → subagents → publish)
/rag-reviewer:reviewer_review-pr owner/repo#42 --dry-run   # assemble the report without posting
/rag-reviewer:reviewer_sync-codebase --ref main      # build/update vector store + code graph
/rag-reviewer:reviewer_sync-tasks                    # warm the task graph (server-side ETL)
/rag-reviewer:reviewer_solve-task PRI-4              # gather task context, then hand off to dev
/rag-reviewer:reviewer_ask how does index freshness work   # grounded codebase Q&A
```

A typical end-to-end run:

```bash
git clone https://github.com/ORG/REPO /tmp/REPO
reviewer index /tmp/REPO --ref main       # build base index + graph for main
reviewer index /tmp/REPO --ref master     # optionally index a second branch (REVIEW_BRANCHES=main,master)
# in Claude Code (from the repo root):  /rag-reviewer:reviewer_review-pr ORG/REPO#42
```

---

## Per-repo policy & task board

A `.review.yml` file in the **target (base) branch** overrides the env defaults (a PR cannot weaken
its own review — see *Caveats*):

```yaml
categories: { correctness: true, security: true, performance: true, style: false, requirements: true }
severity_threshold: medium
min_confidence: 0.5
paths: { ignore: ["**/migrations/**", "vendor/**"] }
max_comments: 25

# Optional task context: read the task from a board and check requirement compliance.
# The board (MCP) is connected by the user on the Claude Code side; the plugin does not bundle it.
task_board:
  type: yougile          # yougile | jira — selects the skill playbook
  mcp: yougile           # name of the connected board MCP server (tools are mcp__<mcp>__*)
  key_pattern: "[A-Z]+-\\d+"   # optional; matches Yougile PRI-34/ID-34 and Jira PROJ-123
```

**The `task_board` block is a deploy-wide default, not a per-repo requirement.** A board connection
is the same for every repo of one team, so configure it **once** in the reviewer `.env`
(`TASK_BOARD_TYPE` / `TASK_BOARD_MCP` / `TASK_BOARD_KEY_PATTERN` / `TASK_BOARD_URL_TEMPLATE`) and
every repo inherits it — no `.review.yml` needed just for the board. A `task_board` block in a repo's
`.review.yml` **overrides** that default for that repo; an explicit empty `task_board:` **disables**
the board for it. `review-pr` reads this through the policy; `solve-task` reads it via the
`get_board_config` MCP tool (and the board-MCP, LLM-side) as a fallback when the local `.review.yml`
has no block.

**Bulk task sync is server-side, not LLM (`sync_board`).** The `sync-tasks` skill is a thin trigger:
it calls one MCP tool, `sync_board(board, limit, purge_orphaned, keep_with_prs)`, and the reviewer
server enumerates the board over **REST** itself (`reviewer/tasks/boards/`, behind a
`TaskBoardProvider` interface — Yougile is the reference), normalizes each task into a `TaskBrief`
in Python, and indexes it via the existing batch indexer. The LLM passes no task text, so a sync
costs O(1) tokens regardless of board size. It is incremental via a per-board timestamp watermark in
`index_meta` (`ref="tasks:<board>"`): a repeat sync touches ~0 tasks; `--limit` disables purge and
the watermark advance. The board REST credentials live only in the reviewer-mcp environment
(`TASK_BOARD_API_KEY` / `TASK_BOARD_API_BASE`). This inverts the "reviewer Python never touches the
board" rule **for bulk sync only** — single-task reads in `solve-task` / `review-pr` still go through
the board-MCP on the LLM side. The task graph (`:Task`) is global, so one task can span PRs across
several microservice repos.

#### Контекст-слой (PRI-161)

- `paths.ignore` — список fnmatch-паттернов; перечисленные пути **не индексируются**
  (вектора и граф) и не комментируются. Голое имя папки (напр. `eval`) ловит всё поддерево;
  `eval/*` — то же явно; glob — напр. `*.gen.py`. Экономит квоту Voyage и убирает шум.
- `summary_cluster_depth_overrides` — карта `префикс → depth` для точечной глубины
  кластеризации сводок (longest-prefix-match по сегментам пути); дополняет глобальный
  `summary_cluster_depth`. Смена глубины пересобирает затронутые сводки.
- `summary_topk_threshold` — порог масштаба приора сводок подсистем. Если число сводок
  репо/ветки **превышает** порог, при запросе используется ANN top-k по близости к запросу;
  иначе все сводки передаются целиком (бэк-совместимое поведение для малых репо). Дефолт
  берётся из env (`SUMMARY_TOPK_THRESHOLD`, 20).

Все ключи берутся из `.review.yml` целевой ветки. Пример — в корневом `.review.yml`.

---

## Observability web admin

Every `publish_review` records the run in Postgres (`review_runs` / `review_findings`): repo/PR,
model, timings, status, findings with verdicts and whether they were posted. The write is
**fail-soft** (a logging failure never breaks the review) and gated by `REVIEW_HISTORY` (default
`true`). The web admin (FastAPI + React/Vite SPA) shows run history, aggregates (gate filter rate,
trends over time, findings by category/severity) and per-run details with finding drill-down.

```bash
# On the host — build the frontend, then serve the SPA + FastAPI:
pip install -e ".[web]"
(cd web/frontend && npm install && npm run build)
reviewer serve                 # http://127.0.0.1:8000 (options: --host / --port)
```

API: `GET /api/runs` (filterable list), `GET /api/runs/{id}` (run + findings),
`GET /api/runs/{id}/trace` (step trace, forward-only — empty for pre-feature runs),
`GET /api/stats?days=N` (aggregates).

---

## Known limitations & caveats

A factual list of what this does and does not do today.

- **No automatic trigger.** A review is not started on PR open/update. It is a manual skill
  invocation inside Claude Code — there is no GitHub App / webhook / CI integration out of the box.
- **Graph auto-reindex is incremental, not full-precision.** On `prepare_review`, when the base
  branch SHA drifts, the code graph is patched for the changed files (tree-sitter, repo-scoped) in
  the same step that self-heals vector chunks — incoming `CALLS` edges from unchanged callers are
  preserved. Not refreshed until the next manual `reviewer index`: `IMPLEMENTS` edges, outgoing
  `CALLS` into unchanged files, and new incoming `CALLS` from unchanged callers. Full SCIP precision
  is restored by `reviewer index`.
- **Multi-repo via a `repo` discriminator.** One deployment hosts N repositories isolated by a
  `repo` (`owner/name`) column/property across Postgres and Neo4j; each review is scoped to its PR's
  repo (no cross-repo retrieval). Index a repo with `reviewer index <path> --repo owner/name` (or let
  it derive `owner/name` from the git `origin` remote, or set `DEFAULT_REPO`). The task graph
  (`:Task`) is intentionally global, so one task can span PRs across several microservice repos.
  Within a repo, each tracked branch has its own isolated index (`ref="base:<branch>"` in Postgres;
  `branch` property on Neo4j `:Symbol` nodes, unique constraint `(repo, branch, id)`).
- **Language scope: Python only.** The chunker (tree-sitter) and the SCIP backend (`scip-python`)
  are Python-specific. Other languages would go behind the same chunker/`GraphIndexer` interfaces.
- **VCS scope: GitHub only.** Only GitHub implements `VCSProvider`; GitLab/Bitbucket are not
  implemented (the abstraction exists, the providers do not).
- **Graph backend trade-off.** A precise, type-aware graph (`CALLS` + `IMPLEMENTS` edges) requires
  `scip-python` in `PATH`. Without it, the tree-sitter fallback gives `CALLS`-by-name only (no
  `IMPLEMENTS`). Mode is chosen via `GRAPH_BACKEND=auto|scip|treesitter`; in `auto`, a SCIP failure
  silently falls back to tree-sitter with a warning, while `scip` propagates the error.
- **Review surface.** Inline comments are only possible on diff lines (the changed/context lines of a
  hunk); everything else goes into the summary. An applyable `suggestion` block is emitted only under
  safe invariants (`apply` mode, an exact replacement, the whole range inside the RIGHT side of the
  diff, no overlap with other fixes); otherwise the advice is plain text.
- **MCP session is in-process.** State between `prepare_review` and `publish_review` lives in the
  running `reviewer-mcp` process (`_Session` in `MCPReviewService`). Both calls for one PR must hit
  the **same** running server — a restart in between loses the session (mitigated by
  `REVIEW_SESSION_PERSIST`).
- **Voyage free tier** = 3 RPM / 10K TPM; TPM is the main blocker — a full `reviewer index` of a
  large repo throttles (there is retry/backoff with jitter). A single PR review (overlay + query
  embeddings) fits within the limit.
- **LLM cost.** A review fans out Claude subagents per file plus dimension passes — that is real
  token cost, not free.
- **Observability web admin auth is optional.** Basic auth is enabled only if `WEB_ADMIN_USER` /
  `WEB_ADMIN_PASSWORD` are set; by default it is not hardened for public exposure (`reviewer serve`
  binds to loopback by default).
- **GitHub API caps.** The PR file list is paginated by 100; the compare API used to re-sync the base
  index returns at most 300 files — very large diffs are truncated.
- **`.review.yml` comes from the base branch** (by design — a PR cannot weaken its own review), not
  from the PR head.

## Tests

```bash
.venv/bin/pytest -q                 # unit: fast, on fakes; never hit external APIs
.venv/bin/pytest -m integration     # integration: needs running Postgres/Neo4j + a Voyage key
.venv/bin/ruff check .              # lint (line-length 100, target py311)
```

`pytest` excludes integration tests by default (`addopts = -m 'not integration'`). External services
(GitHub, Voyage, Postgres, Neo4j) are isolated behind interfaces and mocked in unit tests; real calls
happen only in integration/E2E.

## Project layout

```
reviewer/
  config/      Settings (pydantic-settings): env → review thresholds, stores, branches, board
  vcs/         VCSProvider + github.py (httpx) · diff.py (lines available for inline)
  index/       chunker(tree-sitter) · embeddings(Voyage) · reranker · store(pgvector+pg_search/RRF) · freshness
  graph/       builder(tree-sitter call-graph) · scip(SCIP parser) · backend(backend orchestrator) · store(Neo4j)
  retrieval/   Retriever: hybrid + graph expansion + rerank → ContextPack
  llm/         _retry.py (retry/backoff for Voyage)
  tools/       agent tools (search_code, get_related_symbols, read_file, get_definition, …)
  tasks/       TaskBrief normalization · boards/ (TaskBoardProvider REST: yougile) · TaskService.index_batch
  agent/       state (ReviewUnit) · assemble · dedup
  mcp/         MCPReviewService: prepare / tool calls / publish; session management
  services/    ReviewService.prepare: ingest PR, overlay, units
  policy/      ReviewPolicy: env defaults + .review.yml + gating
  entrypoints/ cli.py (Click) · mcp_server.py (FastMCP, 20 tools)
  install.py   reviewer init / install / install-skills (cross-platform client wiring)
  web/         FastAPI + React/Vite SPA — observability web admin
  app.py       dependency assembly from Settings
plugin/        Claude Code plugin (7 skills /rag-reviewer:reviewer_*)
docker-compose.yml   ParadeDB (pgvector+pg_search) + Neo4j
```

---

## Contributing

Issues and PRs are welcome. To work on the project locally:

```bash
git clone https://github.com/mimfort/rag_for_git
cd rag_for_git
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
docker compose up -d            # Postgres/ParadeDB (:5433) + Neo4j (:7687)
.venv/bin/pytest -q             # unit tests — fast, on fakes, no external APIs
.venv/bin/ruff check .          # lint (line-length 100, target py311)
```

External services (GitHub, Voyage, Postgres, Neo4j) sit behind interfaces and are mocked in unit
tests; real calls happen only in integration/E2E. Commit messages follow Conventional Commits. The
architecture is documented in depth in [README.ru.md](README.ru.md) (Russian) and `CLAUDE.md`.

## License

[MIT](LICENSE) © rag_for_git contributors.
