A cross-repo code-review bot for GitHub organisations. Peer reviews a pull request against relevant context from the other repositories in the org — catching API-contract breaks, duplicated logic, and org-pattern violations that a single-repo review would miss — then posts the result as a GitHub PR review with inline comments.
The review reasoning runs through Claude Code (subscription auth via claude login) — never a direct Anthropic API call.
- Cross-repo context — local mirror + SQLite symbol index, probe-based retrieval, budget-capped context pack
- CLI-first, bot-ready — one CLI plus a webhook listener sharing a single review pipeline
- Structured output — zod-validated JSON reviews, one repair retry, inline comments anchored to diff lines, deduped per head commit
- Zero-credential proof —
--localfixture mode runs the retrieval and context-packing pipeline against bundled fixtures, then uses a deterministic stub reviewer (no GitHub access or API key needed) - Tested — Deterministic regression tests; engine calls are dependency-injected, so tests never hit the network
- Mirror — shallow-clones the org's repositories and indexes their files and symbols into SQLite.
- Probe — parses the PR diff into a numbered diff and extracts probe terms: symbols, imports, routes, tables.
- Context pack - finds matching files in the other repos, ranks them, and stages the top files next to the numbered diff (capped by count and character budget), plus org pattern files (
AGENTS.md,README.md). - Review — Claude Code reviews the diff against the pack and returns a single JSON review: summary, verdict, strengths, and findings with severity, category, file, line, suggestion, and cross-repo evidence.
- Post — the JSON is schema-validated, findings are anchored to real new-file lines, and the review is posted as inline comments (or saved locally without
--post).
- Node.js 20+ (CI checks Node 20 and 22 on Linux and Windows)
git- Claude Code CLI, installed and logged in with your subscription (
claude login)
From a fresh checkout (Bash or PowerShell):
git clone https://github.com/sandeepannandi/Peer.git
cd Peer
npm ci
npm run demo:local
npm run ciThe compiled offline demo requires no .env, Claude login or GitHub credentials.
It uses synthetic fixtures and a deterministic stub reviewer, checks the saved
review, then removes its temporary data. It makes no AI-quality claim.
For live use, copy .env.example to .env and fill in your GitHub App settings:
cp .env.example .env in Bash, or Copy-Item .env.example .env in PowerShell.
Then run npm run dev -- doctor. Alternatively build with npm run build and
run node dist/index.js doctor. The package is private; setup does not install
peer globally. In the command table below, peer means either of those CLI
entry points.
The auth check uses claude auth status --json, requiring a zero exit status
and explicit first-party claude.ai login. Failed, empty, unknown or API-key
output is not a subscription login. This checks reported local auth, not paid
request success, subscription entitlement or commercial-use permission. Update
an older CLI if auth status is unsupported. No model call is made by doctor.
| Command | Description |
|---|---|
peer doctor |
Environment health check |
peer mirror --owner <org> |
Clone/refresh the org repositories |
peer index --owner <org> |
Build the SQLite symbol index |
peer context --owner <org> --repo <r> --pr <n> |
Build and inspect a PR's context pack |
peer review --owner <org> --repo <r> --pr <n> [--post] |
Review a PR — save locally, or post to GitHub with --post |
peer review --owner <org> --repo <r> --pr <n> --local |
Retrieval + context pack against bundled fixtures, then deterministic stub review (no credentials) |
peer webhook [--port <n>] |
Bot mode: auto-review PRs on opened/synchronize |
npm run demo:local
# Live authenticated Bash workflow, optional posting:
./scripts/demo.sh <org> <repo> <pr> [--post]| Variable | Purpose |
|---|---|
GITHUB_APP_ID, GITHUB_PRIVATE_KEY_PATH / GITHUB_PRIVATE_KEY |
GitHub App credentials (one key source only) |
GITHUB_ORG |
Org whose repositories are mirrored (defaults to the PR owner) |
GITHUB_WEBHOOK_SECRET, WEBHOOK_PORT |
Webhook bot mode |
CLAUDE_MODEL, CLAUDE_MAX_TURNS |
Review engine (default sonnet, max 30 turns) |
REVIEW_MAX_CONTEXT_FILES, CONTEXT_TOKEN_BUDGET |
Context-pack sizing (defaults 12 files / 40k chars) |
MAX_REVIEW_COMMENTS |
Inline-comment cap (default 20) |
DATA_DIR, LOG_LEVEL |
Paths and logging |
src/
├── index.ts CLI entry (commander)
├── config/env.ts zod env schema + ANTHROPIC_API_KEY guard
├── github/ GitHub App auth, PR fetch, review posting
├── mirror/ shallow mirror, SQLite symbol index
├── context/ probes → cross-repo search → context pack
├── claude/ Claude Code runner + reviewer prompt
├── review/ zod schema, line mapping, posting, shared pipeline
├── webhook/ signature verification + PR event dispatch
├── local/ fixture mode (deterministic reviewer)
├── store/db.ts SQLite schema + queries
└── util/ git wrapper (secret redaction), diff parser, doctor, logging
tests/ Vitest suites and bundled fixture repos
- GitHub App installation tokens are minted per run, injected only into the git clone URL, stripped from the stored remote, and scrubbed from error output — never persisted.
.env,*.pem, anddata/are gitignored.- Startup fails fast if
ANTHROPIC_API_KEYis set — reviews never use a direct Anthropic API call. - Webhook requests are verified with
X-Hub-Signature-256before processing.
- Context retrieval is lexical (symbol/basename/content matching), not semantic.
- Findings must map to new-file lines inside diff hunks; unanchored findings are rendered in the review body instead.
- Each review is a single model call (~1–3 min); no batching or queueing yet.
The Claude reviewer has only Read, Grep, and Glob available. A PreToolUse
hook checks every call before the SDK's automatic read permissions: paths must
stay inside the job's packed workspace, refer to existing regular files or
directories, and contain no symlinks. Recursive searches reject trees containing
symlinks or special files. Unknown tools and unsafe glob patterns fail closed.
The runner does not use bypassPermissions; filesystem settings, skills, plugins,
and ambient MCP server configuration are disabled for each attempt, including
JSON-repair retries. Subscription authentication is unchanged.
This is an SDK tool policy, not an operating-system sandbox. The host must own and protect the packed workspace from concurrent modification. Path checks cannot prevent a hostile local process from swapping a file between validation and use, or protect against SDK/CLI vulnerabilities. Do not run Peer alongside untrusted local processes or place secrets in the context pack. A dedicated process/container boundary is separate deployment hardening. The adversarial tests exercise the policy callbacks and option wiring without calling a live model.
Webhook mode persists verified opened/synchronize events to SQLite before
responding HTTP 202. The delivery ID and (owner, repo, PR, head SHA) are deduped.
Keep DATA_DIR on persistent local storage. The queue shares the existing database
and uses WAL with full synchronous commits; it does not survive deleting that data.
One leased worker drains jobs, including jobs left by a restart. It renews a
60-second lease every 10 seconds. Retryable pre-post failures get at most three
attempts with 30/60-second backoff. Expired leases are recovered; exhausted jobs
remain failed. Jobs for a changed head are superseded; the head is checked again
before posting. The GitHub review is pinned to the checked SHA, but a PR can still
change after that last check. Run peer jobs to inspect the latest 100 jobs.
Posting uses an atomic durable claim, shared by CLI and webhook callers, before
any GitHub review-creation request. Once that request may have started, Peer does
not automatically resend it after a timeout, crash, or response-loss. Such jobs
remain blocked (or become blocked when recovered), and the durable post claim
remains uncertain. This favors no duplicate bot reviews over automatic delivery:
GitHub review creation has no idempotency key, so Peer does not promise exactly-once
remote delivery. Automatic Octokit transport and rate-limit retries are disabled on the GitHub App
client; the queue owns bounded retries before posting.
For a blocked job, an operator must check the PR's reviews at the recorded head
and reconcile the review_posts claim with the actual GitHub result. Do not delete
an uncertain claim simply because no review is immediately visible: the original
request may still complete. Peer deliberately provides no blind retry/reset command.
Multiple hosts must not share this SQLite database over a network filesystem.
A worker lease fences posting, not hostile/stalled processes accessing mirror files;
host isolation and immutable context snapshots remain separate hardening.
Each job writes manifest.json with its checked PR head, diff hash, and every
packed source's repository/path, commit (when available), content hash and line counts. The host retains exact source text for validation. Committed context is read from pinned Git blobs, not mutable working-tree
bytes. Local fixture sources are explicitly unpinned. PR metadata is checked before
and after diff/files acquisition; a changed head or base aborts the snapshot.
The host validates both initial and repaired model replies: finding files/lines must exist in the diff; evidence must identify a packed source; line numbers must be positive integers within that source; quotes must match the exact text and cited line. Cross-repo findings need evidence. Invented citations fail the review, not merely fall back to body comments. Source identities/quotes are preserved without prose normalization, and staged input bytes are rechecked after the run.
Peer's current retrieval is heuristic, not an exhaustive consumer inventory. Coverage is therefore explicitly incomplete: output is comment-only, never an approval or request-changes verdict, with a coverage note. Missing mirrors, unpinned inputs, omitted patches and budget omissions add named limits. This prevents an empty findings list from pretending to prove org-wide compatibility. The manifest pins included evidence, not the completeness of the retrieval index. It also does not prove semantic correctness of model claims or authenticate source comments as instructions. Context code/docs are untrusted data. Stronger retrieval coverage is a later work item; no flag silently bypasses this verdict gate.
CONTEXT_TOKEN_BUDGET is a legacy name: its unit is JavaScript string characters,
not model tokenizer tokens. It now caps numbered diff plus packed source text and
source headers together. A diff exceeding that character budget aborts clearly,
never silently truncates. Source files get first use of the remaining space;
README/AGENTS supplements have a 30% cap but reserve no unused allowance. The
source file cap remains REVIEW_MAX_CONTEXT_FILES. Retrieval looks beyond that
cap (a bounded ranked pool) so one oversized top result cannot hide smaller matches.
Content search reads pinned committed blobs when Git provenance exists; local fixtures use deterministic sorted file walks. Missing mirrors, unreadable/binary inputs, oversized content, term/file/result limits and packing omissions appear in coverage diagnostics (up to 100 distinct notes plus an omitted-count note). The content scan remains capped at 1,000 files/repo, 512 KiB/source and 15 terms; it is not exhaustive. Index matches remain heuristic. The on-disk manifest carries hashes/commit identities and line counts, not duplicate source text; the host retains exact evidence privately for validation. Prompt, manifest metadata, system instructions and SDK overhead are outside this content character limit, so it is not a model context-window guarantee. Comment-only coverage gating remains in force.
On Windows the reviewer must use workspace-relative forward-slash paths. Native drive-letter/backslash tool paths are deliberately denied by the same conservative boundary; enabling the CI matrix does not relax that policy.