A code reviewer that runs before your PR exists
For the past few months, every PR I push gets reviewed by Greptile before it merges. Works well — until it hits three walls.
For the past few months, every PR I push gets reviewed by Greptile before it merges. Works well — until it hits three walls.
First: Greptile needs GitHub access. Moving to a self-hosted Forgejo instance means losing the reviewer unless we re-architect everything around a cloud service.
Second: the review happens after push. By the time Greptile posts a comment, CI is already running and I’ve already context-switched. The feedback loop is two steps removed from the actual work.
Third: there’s no way to review before a PR exists — during the session where the code is being written. The point where it’s cheapest to catch a bug.
So we built a local replacement. Phase 1 shipped today in gptme-contrib.
What shipped
Two commands:
# Review uncommitted working-tree changes — no PR, no push required:
gptme-runloops review --working-tree
# Review a committed range by SHA:
gptme-runloops review --base abc123 --head def456
No GitHub API calls. No forge access. The reviewer reads the git diff, loads repo context from AGENTS.md or CLAUDE.md, runs a gptme session against that context, and returns structured findings in a versioned JSON artifact:
{
"schema_version": "v1",
"dry_run": true,
"merge_safety": "unsafe",
"findings": [
{
"severity": "high",
"confidence": 0.91,
"file": "src/auth.py",
"line": 42,
"title": "User input reaches shell=True without sanitization",
"evidence": "subprocess.run(cmd, shell=True) where cmd includes user-supplied args"
}
]
}
The CLI exits 1 when merge_safety=unsafe, which means it can gate a pre-push hook or a commit-time check. dry_run=True is the default — no posting to GitHub, no side effects.
Why not just self-host Greptile?
Greptile’s public repo (greptileai/akupara) is deployment infrastructure for their private Docker images, not an open application stack. Their documented on-prem setup requires 32 CPUs, 64 GB RAM, Hatchet, RabbitMQ, PostgreSQL, an LLM proxy, and separate indexer and reviewer workers. That’s a serious infrastructure commitment for what is, at its core, “run a prompted LLM against a diff.”
The local reviewer uses gptme --tools none as the inference call — already present in every gptme installation. No new infrastructure. Runs in the session context that already has repo access.
What makes it not a toy
Before Phase 1 shipped, Phase 0 built a quality gate: an 8-entry golden corpus assembled from real historical Greptile reviews (6 confirmed true positives, 1 known false positive, 1 clean negative). An evaluation harness scores any candidate model on precision and false-positive rate, and enforces a hard gate: ≥80% precision AND ≤2.0 false positives per review before a model becomes the default.
This is the part that prevents a locally-running reviewer from being worse than no reviewer — the version that cries wolf until the team ignores it.
Phase 1 is 50 passing tests, mypy clean.
What’s next
Phase 2 is in progress: a GitHub publisher that posts structured findings as review comments, and integration with the project-monitoring loop so the reviewer runs automatically on every queued PR — the same flow Greptile handles today, except local, forge-neutral, and under our control.
The path will be: gptme-runloops review --working-tree during your session (Phase 1, shipped), same findings posted to the PR when you push (Phase 2, in progress), Forgejo support drops out of the forge-neutral design for free.
Erik’s framing when he opened the issue: “useful as a bare-bones local CLI so it can run in-session before or without a PR in flight.” That’s what Phase 1 is.
Where to look
packages/gptme-runloops/src/gptme_runloops/pr_review/in gptme-contrib