An autonomous teammate for your repositories. GitHub Review Mate watches your open pull requests and issues on a schedule, reads the actual code in a fresh checkout, and then acts on GitHub — it labels, comments, reviews, asks clarifying questions, and (when you let it) auto-merges. It is not a chat bot you ping; it just shows up and does the first pass for you, tick after tick.
On every pull request, it can:
- Read the diff and the surrounding code, then form a verdict.
- Label the PR
trivial,low,medium,high, orneeds-info. - Post a structured review with a summary, risks, and
file:linecitations. - Ask the author for clarification when intent or context is missing.
- Approve and auto-merge trivial / low-risk changes — only if you opt in.
- Escalate anything risky, divergent between the two analyzers, fork-based, or CODEOWNERS-protected to a human reviewer.
On every open issue, it can:
- Decide whether it is a bug, a feature request, or a usage question.
- Answer usage questions itself, citing files inside the repo.
- Ask the reporter for missing reproduction details when needed.
- Hand real bugs to maintainers with a plain-English summary and severity.
- Flag suspected duplicates of other issues it has seen.
Across runs, it remembers what happened:
- Tracks new commits, comments, labels, reviews, and CI status between ticks.
- Re-reviews a PR when a new commit lands, or when a new comment carries real signal — but not when it is just "ok" / "thanks" / "I'll think about it". A cheap classifier pass filters that noise before the full reviewer wakes.
- Recognizes its own previous comments by a hidden marker, so it never posts the same thing twice and same-account humans are still heard.
- After 48 hours of silence on a
needs-infoitem, it politely re-pokes the right person.
Two local agentic CLIs do the analysis in parallel — OpenAI Codex
drives the final action, Claude Code is the second opinion. The
default mode is safe: dry_run: true, no auto-merge, gitleaks scans
every diff before it reaches the analyzers, GitHub and LLM tokens are
scrubbed from analyzer subprocesses, and every outbound action is
de-duplicated through an SQLite outbox. The analyzer CLIs are also
started in locked-down modes: Claude gets only Read,Grep,Glob with
MCP/plugin skills disabled, and Codex runs as ephemeral/read-only with
apps, MCP apps, browser, plugins, multi-agent, image, and search
features disabled.
The bot picks an action from the merged verdict and posts it as a label
plus a comment/review on GitHub. Every comment carries a hidden
github-reviewer:v1 marker so the next tick recognizes its own output.
| Situation | Bot action | Driven by |
|---|---|---|
PR verdict trivial/low, green CI, not a fork, no CODEOWNERS hit |
Labels autoreview/<band>, posts approving review, auto-merges if auto_merge: true |
prompts/pr_review.md + prompts/risk_assessment.md |
PR verdict medium/high |
Labels autoreview/needs-human, posts review with summary and risks |
prompts/pr_review.md |
| Codex and Claude disagree by ≥ 2 risk bands | Labels autoreview/needs-human with divergence_escalated reason |
analyzers/merger.py |
PR verdict needs-info |
Comments the analyzer's questions to the author; 48 h grace before re-poking | prompts/pr_review.md |
Issue verdict answered (usage question) |
Posts suggested_reply verbatim as an issue comment |
prompts/issue_triage.md |
Issue verdict needs-info |
Asks the reporter for the missing items listed in missing_info[] |
prompts/issue_triage.md |
Issue verdict needs-human |
Labels autoreview/needs-human, posts a maintainer-facing summary |
prompts/issue_triage.md |
| New human comment on an open item | Cheap classifier pass decides if it is real signal; "ok"/"thanks" only updates the cursor | analyzers/comment_classifier.py |
- Draft PRs — skipped at the pre-analysis gate.
- Items labeled
autoreview/skip— manual opt-out per item. - Project dirs starting with
_or.—projects/_exampleis a template only; nothing in it is ever processed as a live project. - Low-signal comments — "ok", "thanks", "I'll think about it" do not trigger a re-review; only the activity cursor moves.
- Diffs that contain secrets —
gitleaksscans the PR checkout before any prompt is built; a positive hit blocks the analyzer call. - Items unchanged since the last tick — the actionable input fingerprint short-circuits the run, so the bot never re-posts.
- Bot output authored under a human's account — bot comments are
detected only by the hidden
github-reviewer:v1marker, so a human can post under the bot's GitHub account and still be heard.
- Never merges while
dry_run: true(the default inconfig/config.example.yaml). - Never auto-merges fork PRs unless you set
allow_fork_automerge: trueon that project. - Never auto-merges PRs touching
CODEOWNERS-covered paths. - Never sends
GITHUB_TOKEN,GH_TOKEN,GITHUB_AUTH_TOKEN,OPENAI_API_KEY, orANTHROPIC_API_KEYinto Codex/Claude subprocesses — they are scrubbed byanalyzers/base.scrubbed_env. - Never lets analyzer runs use configured MCP/apps/plugins/browser
integrations.
make security-doctorverifies the active command-line hardening and warns about cached MCP/plugin artifacts indata/bot-home. - Never posts the same comment twice — every outbound action goes through an SQLite outbox keyed by item + action + input fingerprint.
- Docker and Docker Compose.
- A GitHub personal access token for the bot account.
- Claude Code installed and logged in on the host.
- OpenAI Codex CLI installed and logged in on the host.
No LLM API keys are needed. The bot reuses your existing Claude Code and
Codex CLI OAuth sessions, copied into an isolated bot home under
./data/bot-home/ by make sync-auth.
- Create local config files:
cp .env.example .env
cp config/config.example.yaml config/config.yaml
cp -r projects/_example projects/my-repo- Generate a GitHub token
with the permissions listed in the GitHub Token
section, then edit
.env:
GITHUB_TOKEN=ghp_your_bot_token
GITHUB_BOT_LOGIN=your-bot-login
TZ=Europe/KyivTZ sets timestamps in the live UI and container logs.
- Point your project at the right repository:
# projects/my-repo/project.yaml
repo: https://github.com/your-org/your-repo.git
default_branch: main
auto_merge: false
enabled: trueYou do not normally need to edit docker-compose.yaml. Repositories are
cloned automatically into ./data/reference-repos/.
- Log in to both CLIs on the host, then copy their OAuth files into the isolated bot home:
claude /login
codex login
make sync-auth- Start the scheduled service:
make up
make logsThese three commands confirm the install end-to-end without touching GitHub state:
make security-doctor # local: analyzer hardening and bot-home isolation
make auth-doctor # read-only: CLIs, GitHub token, clone access, label perms
make run-once # one interactive tick with the Rich live UI; Ctrl+C exits
make logs # follow the scheduled service (after `make up`)What success looks like:
security-doctorprintsOKfor Claude/Codex hardening; warnings about cached MCP/plugin files are okay because the runtime flags ignore them.auth-doctorprintsOKfor every project and every check.run-onceshows projects fanning out, items processed, and "no actionable change" on subsequent runs against the same items.- With
dry_run: true, the bot writes "would have merged" to the PR thread instead of merging.
cp -r projects/_example projects/api
$EDITOR projects/api/project.yaml
$EDITOR projects/api/prompts/*.mdMinimal project.yaml:
repo: https://github.com/your-org/api.git
default_branch: main
auto_merge: false
enabled: truerepo must be a GitHub HTTPS clone URL. Directories whose names start
with _ or . are ignored, so projects/_example is only a template.
See projects/_example/project.yaml for every per-project knob
(auto_merge, allow_fork_automerge, merge_method,
risk_levels_for_automerge, triggers_workflow_dispatch, per-analyzer
model overrides, comment_classifier, and outbound_guard).
Rerun rules: a new commit on a PR always triggers review. A comment-only update triggers review only when the classifier sees a real answer, new technical information, a question for the bot, or an explicit rerun request.
Outbound guard: every bot-authored GitHub comment and PR review passes through
a last-line guard before publishing. It first redacts or blocks obvious secrets
locally, then asks a cheap Claude CLI pass to return allow, redact, or
block for the sanitized text. With fail_closed: true, invalid guard output
or a high-risk classification blocks publication and records an outbound error.
The bot's behavior is steered by five Markdown files under
projects/<name>/prompts/. Each is rendered with string.Template
substitution before being fed to Codex and Claude.
| Prompt file | When it runs | What it controls |
|---|---|---|
pr_review.md |
Every PR that survives the pre-analysis gate | The verdict (trivial..high/needs-info), the review body, the cited path/to/file.py:42 references |
risk_assessment.md |
Appended to every PR prompt | The rubric Codex/Claude use to pick a risk band; edit this to retune what counts as low vs medium |
issue_triage.md |
Every open issue on each tick | Category, severity, action, and the literal suggested_reply text the bot posts |
contributor_comment.md |
Tone guide for author-facing comments | Wording style only; not auto-injected today |
maintainer_comment.md |
Tone guide for maintainer-facing comments | Wording style only; not auto-injected today |
Variables you can interpolate: ${repo}, ${pr_number}, ${pr_title},
${pr_author}, ${base_branch}, ${head_branch}, ${head_sha},
${pr_body}, ${issue_number}, ${issue_title}, ${issue_author},
${issue_body} (see src/github_reviewer/analyzers/prompts.py).
make sync-auth # copy Claude/Codex OAuth files into ./data/bot-home
make auth-doctor # read-only check of CLIs, config, clone access, GitHub reads
make security-doctor # check analyzer MCP/plugin isolation and hardening flags
make run-once # one interactive tick with the Rich live UI
make watch # foreground watch loop, Ctrl+C to stop
make up # build and start the scheduled service
make logs # follow container logs
make down # stop the service
make test # run unit tests
make lint # run ruffDirect CLI commands inside the container:
github-reviewer run --mode=full
github-reviewer run --mode=fasttick
github-reviewer run --mode=once
github-reviewer run --mode=watch --watch-interval=300
github-reviewer auth-doctor
github-reviewer security-doctor
github-reviewer ensure-labels # optional manual label repair
github-reviewer statusThe container ships with two scheduled modes plus two interactive ones:
| Mode | When it runs | What it does |
|---|---|---|
full |
Hourly (cron) | Lists all open PRs + issues, runs analyzers, decides, acts |
fasttick |
Every 5 minutes (cron) | Re-polls only items in awaiting_tests / tests_failed; analyzers re-run only when CI just turned green |
once |
Manual | One tick with the Rich live UI |
watch |
Manual / foreground | Loop with the Rich live UI; sleeps --watch-interval seconds between ticks |
flowchart TD
A["supercronic / make run-once / make watch"] --> B["github-reviewer run"]
B --> C["Load config, projects, SQLite, EventBus"]
C --> D["Start per-tick budget"]
D --> E{"For each enabled project"}
E --> F["List open PRs and issues"]
F --> G["Sync bot labels"]
G --> H["Prepare managed reference clone"]
H --> I{"Process items with a semaphore"}
I --> I1["PR worker"]
I1 --> I2["Fetch PR details, activity, checks"]
I2 --> I3["Classify new human comments"]
I3 --> I4["Compute actionable input fingerprint"]
I4 --> I5{"Unchanged actionable input?"}
I5 -->|yes| I6["No-op or grace-period escalation"]
I5 -->|no| I7["Quick gates: skip / draft"]
I7 --> I8["Ephemeral PR checkout"]
I8 --> I9["gitleaks scan, files, CODEOWNERS, CI gates"]
I9 --> I10["Run Codex and Claude in parallel"]
I10 --> I11["Merge reports and decide action"]
I --> J1["Issue worker"]
J1 --> J2["Fetch comments and activity"]
J2 --> J3["Classify new human comments"]
J3 --> J4["Compute actionable input fingerprint"]
J4 --> J5{"Unchanged actionable input?"}
J5 -->|yes| J6["No-op or grace-period escalation"]
J5 -->|no| J7["Run Codex and Claude in reference repo"]
J7 --> J8["Merge reports and decide action"]
I11 --> K["Outbox and hidden marker dedupe"]
J8 --> K
I6 --> K
J6 --> K
K --> L["GitHub labels, comments, reviews, merge"]
L --> M["Persist state in SQLite"]
Runtime state lives in data/reviewer.db. Managed repository clones
live in data/reference-repos/, and ephemeral PR checkouts live in
data/worktrees/.
The bot's safety guarantees are described inline in What It Ignores and What It Never Does Without You. Two more details worth knowing:
- Every outbound comment/review goes through the SQLite
outbox_actionstable keyed by item + action + input fingerprint, so a restart in the middle of a tick cannot produce a duplicate. ensure-labelsis optional; normal runs already sync the canonical bot labels at project startup. Use it only to repair labels manually.
Create a token from GitHub's personal access token settings. Use a fine-grained personal access token when possible:
- Resource owner: the user or organization that owns the repositories.
- Repository access: Only selected repositories, then select every repo in
projects/*/project.yaml. - Repository permissions:
| Permission | Access | Why Review Mate needs it |
|---|---|---|
| Metadata | Read | Required by GitHub for repository API access. |
| Contents | Read | Clone over HTTPS and read CODEOWNERS; use Write only if auto_merge: true. |
| Pull requests | Write | List/read PRs and create PR reviews. |
| Issues | Write | Read issues, add issue/PR comments, and manage labels. |
| Checks | Read | Read check runs before reviewing or merging. |
| Actions | Read | Read workflow runs; use Write only if triggers_workflow_dispatch is enabled or you want the bot to approve first-time-contributor fork workflows. |
The token cannot grant permissions the token owner does not already have. If
your organization blocks fine-grained tokens, or if the Checks API fails for
your setup, use a classic PAT with the repo scope as the fallback.
GitHub's permission references: fine-grained PAT permissions, labels, PR reviews, PR merge, workflow dispatch, check runs.
Use the Make targets. make up, make run-once, and make watch all
include --build, so the image is rebuilt after source changes.
Run make security-doctor first, then make auth-doctor.
security-doctor verifies that Codex/Claude are launched without
MCP/apps/plugins/browser access. auth-doctor is intentionally
read-only: it checks the
paths the bot uses for reading repositories, PRs, issues, checks,
workflow runs, comments, and clone access, but it does not post test
comments, create PR reviews, dispatch workflows, or merge anything. If
only the CLI checks fail, log in again on the host and run
make sync-auth. If a repository check fails, fix GITHUB_TOKEN
access or the matching projects/<name>/project.yaml.
Check that GITHUB_TOKEN can read the repository and that repo: is a
GitHub HTTPS clone URL such as https://github.com/owner/repo.git.
Managed clones live under data/reference-repos/.
MIT License. See LICENSE.
