Put your panel to work.
Install the relay, choose your reviewers, and get a scored review on your next pull request.
Quick start
You need Git, the GitHub CLI, a local clone of each watched repo, and at least one supported coding agent. Sign in to your agents or configure their API keys before starting.
1. Connect GitHub
Your GitHub account needs permission to manage repository webhooks and, when publishing is enabled, write PR comments.
gh auth login
gh extension install cli/gh-webhook
gh auth status2. Install review-relay
$ curl -fsSL https://github.com/Tyru5/review-relay/releases/latest/download/install.sh | bashStandalone binaries for macOS and glibc Linux on x64/arm64, and Windows x64. No Bun or Node installation required. Alpine/musl is not supported.
3. Choose your repos and start
review-relay setup
review-relay start -d
review-relay statussetup finds GitHub clones under your home directory and agent CLIs on your PATH. Choose the repos, reviewers, models, and effort. It saves ~/.review-relay/config.json.
review-relay run --repo acme/widget --pr 42Use start without -d for foreground logs. The machine and daemon must stay running to receive events.
Install locations, updates, and pinned versions
Unix installs to ~/.local/bin/review-relay. Add ~/.local/bin to your PATH if needed. Windows installs to %LOCALAPPDATA%\review-relay\bin and adds it to your user PATH by default.
The installers verify the binary’s SHA-256 checksum and preserve an existing config. Run the installer again to update, then restart the daemon. To pin a release, pass --version x.y.z to the Unix installer, or -Version x.y.z to the PowerShell script.
Unix also accepts --bin-dir. PowerShell accepts -BinDir and -NoModifyPath. See environment variables for equivalent settings.
Configuration
Configuration is JSON. The CLI uses --config <path> first, then REVIEW_RELAY_CONFIG, then ~/.review-relay/config.json. Restart the daemon after editing it.
{
"reviewers": [
"codex",
"claude"
],
"repos": [
{
"fullName": "acme/widget",
"localPath": "~/code/widget",
"trigger": "github",
"postToPr": false
}
]
}This example watches a fictional repo through GitHub events and keeps reports local. Replace fullName and localPath, then set postToPr to true when you want comments.
Top-level options
| Option | Default | What it does |
|---|---|---|
repos | Required | Non-empty list of GitHub repositories and their local clones. |
port | 9988 | Local webhook server port. Listens on 127.0.0.1 only. |
graceMs | 120000 | In auto mode, wait this many milliseconds for Greptile before the GitHub fallback runs. |
timeoutMs | 1800000 | Per-reviewer timeout in milliseconds. The default is 30 minutes. |
reviewers | ["codex","claude"] | Fallback panel when no route matches. At least one reviewer ID is required. |
models | Built-in entries | Model, effort, provider, and display label per reviewer. Custom entries also name a harness. |
routes | [] | Ordered rules that select reviewers or skip an automatic review. |
dataDir | ~/.review-relay | Reports, state, logs, and temporary worktrees. Does not change the config file location. |
Repository options
Each object in repos accepts these settings. Use an absolute path, or ~/, so the clone resolves consistently.
| Option | Default | What it does |
|---|---|---|
fullName | Required | GitHub owner/name, for example acme/widget. |
localPath | Required | Existing local clone with an origin remote for this repository. ~/ expands to your home. Relative paths resolve from the process working directory. |
trigger | auto | auto, greptile, or github. Mentions work in every mode. |
postToPr | true | Publish the report as a PR comment. Set false for local reports only; reviewers still run. |
github.onPush | false | Also handle pull_request.synchronize when new commits are pushed. Applies to github and auto modes. |
github.mention | @review-relay | Case-insensitive text that requests a review in a new PR comment. Use a non-empty mention. |
review-relay info --config ./relay.json
review-relay start -d --config ./relay.jsonUse the same --config for status, stop, and other commands targeting that daemon. info --json and config show resolved values, including defaults.
Environment variables and installer overrides
| Option | Default | What it does |
|---|---|---|
REVIEW_RELAY_CONFIG | ~/.review-relay/config.json | Config path for the CLI and installers. --config takes precedence in the CLI. |
REVIEW_RELAY_DEBUG | Not set | Any non-empty value logs why webhook events were ignored. Set before starting the daemon. |
NO_COLOR | Not set | A non-empty value disables CLI color, even if FORCE_COLOR is set. |
FORCE_COLOR | Not set | A non-empty value other than 0 enables CLI color. |
REVIEW_RELAY_INSTALL_VERSION | latest | Installer release version. Unix --version and PowerShell -Version take precedence. |
REVIEW_RELAY_INSTALL_BIN_DIR | Platform default | Installer destination. Unix --bin-dir and PowerShell -BinDir take precedence. |
REVIEW_RELAY_INSTALL_URL | https://downloads.reviewrelay.dev | Alternate download origin. Only use an origin you trust. |
REVIEW_RELAY_INSTALL_NO_MODIFY_PATH | Not set | Windows only. Any non-empty value prevents PATH changes; equivalent to -NoModifyPath. |
Agent authentication belongs to each agent CLI. review-relay does not store API keys in its config.
Reviewers & models
reviewers names the default panel. Every ID selects a built-in CLI or a custom entry in models. Selected reviewers run in parallel; they do not need to use different CLIs.
| Reviewer ID | Product | Relay defaults |
|---|---|---|
claude | Claude Code | claude-opus-5-5, effort max |
codex | Codex CLI | gpt-6-astra, effort high |
auggie | Augment Auggie | CLI default model; effort supported |
copilot | GitHub Copilot CLI | CLI default model; effort supported |
droid | Factory Droid | CLI default model; effort supported |
gemini | Gemini CLI | CLI default model; no per-run effort option |
grok | Grok Build | CLI default model; effort supported |
hermes | Hermes Agent | CLI default model; effort supported |
kilo | Kilo Code CLI | CLI default model; effort supported |
opencode | opencode | CLI default model; effort supported |
pi | pi | CLI default model; effort supported |
qwen | Qwen Code | CLI default model; no per-run effort option |
vibe | Mistral Vibe | CLI default model; no per-run effort option |
The executable normally matches the ID. Kilo also accepts kilocode. Install and authenticate each selected CLI separately; review-relay does not install agents.
Model entry options
| Option | Default | What it does |
|---|---|---|
harness | Required for custom IDs | One of the supported CLI IDs below. A built-in ID always uses its own CLI. |
label | CLI label or custom ID | Name shown in the PR comment. Active reviewer labels must be unique, ignoring case. |
model | Harness default | Model identifier accepted by the CLI. Omit to use the relay default below, or the CLI default when none is set. |
effort | Harness default | Reasoning setting for CLIs that support it. Model and CLI versions determine which values work. |
provider | Not set | Hermes only. Provider identifier, separate from the model name. |
For example, models.codex changes the built-in Codex reviewer. To run two Codex models, create two custom IDs with harness: "codex" and put both IDs in reviewers.
Custom IDs use up to 32 lowercase letters, digits, and dashes, starting with a letter or digit. Omitted settings inherit the harness defaults, not another entry’s overrides. Add or remove custom entries in JSON; setup can edit existing entries.
Unknown custom-entry fields are errors. Invalid legacy built-in settings may warn and be ignored. Unsupported model or effort values can still be rejected by the agent CLI at runtime.
Routing rules
Routes choose a panel from the PR’s diff and trigger. The first matching, applicable route wins. If none matches, the global reviewers and timeoutMs apply.
{
"reviewers": [
"codex",
"claude"
],
"models": {
"codex-deep": {
"harness": "codex",
"label": "Codex deep",
"model": "gpt-6-astra",
"effort": "high"
}
},
"routes": [
{
"name": "docs-only",
"when": {
"onlyPaths": [
"docs/**",
"**/*.md"
]
},
"skip": true
},
{
"name": "sensitive",
"when": {
"wideImpact": true
},
"reviewers": [
"codex-deep",
"claude"
],
"timeoutMs": 2400000
},
{
"name": "small",
"when": {
"maxLines": 100,
"maxFiles": 5
},
"reviewers": [
"codex"
]
}
],
"repos": [
{
"fullName": "acme/widget",
"localPath": "~/code/widget",
"trigger": "auto",
"postToPr": false
}
]
}This example skips documentation-only automatic reviews, sends sensitive changes to a larger panel, and sends small changes to Codex. Everything else uses the default panel.
Route options
| Option | Default | What it does |
|---|---|---|
name | Required | Unique ID, up to 32 lowercase letters, digits, and dashes; starts with a letter or digit. |
when | Required | Non-empty object of conditions below. All conditions must match. |
reviewers | Either reviewers or skip | Non-empty list of known reviewer IDs, without duplicates. Replaces the fallback panel. |
skip | Omit for review routes | Set true instead of reviewers to skip. Skip routes cannot set timeoutMs. |
timeoutMs | Global timeoutMs | Positive integer in milliseconds. Overrides the timeout for each reviewer on this route. |
Match conditions
All conditions in when must hold. Lists match any listed value or glob. Path globs use repository-relative paths and are case-sensitive. Use ** to span directories.
| Condition | Value | Matches when |
|---|---|---|
repos | Glob list | Match owner/name, ignoring case. Each pattern must match at least one configured repo. |
baseBranches | Glob list | Match the target branch, not the PR head branch. Case-sensitive. |
sources | Value list | greptile, github, mention, or manual. |
paths | Glob list | At least one changed path matches. Includes the old path of a renamed file. |
onlyPaths | Glob list | Every changed path must match at least one pattern, including both sides of renames. Empty diffs do not match. |
minLines | Integer ≥ 0 | Minimum additions + deletions, inclusive, excluding recognized lockfiles. |
maxLines | Integer ≥ 0 | Maximum additions + deletions, inclusive, excluding recognized lockfiles. |
minFiles | Integer ≥ 0 | Minimum number of changed non-lockfiles, inclusive. |
maxFiles | Integer ≥ 0 | Maximum number of changed non-lockfiles, inclusive. |
wideImpact | Boolean | Match changes to recognized dependency manifests, CI, migrations, schemas, SQL, environment files, and other sensitive paths. False matches their absence. |
Size conditions exclude recognized lockfiles; path conditions still see them. Binary files count as files with zero added or deleted lines. Renames consider both old and new paths.
Request a named panel
@review-relay sensitiveA trusted commenter can name a non-skip route after the mention. That route runs even if its conditions do not match. Unknown names and skip-route names fall back to normal routing.
Route names must be unique. Unknown fields, duplicate reviewers, empty conditions, and a minimum above its maximum stop config loading.
Triggers & review flow
| Mode | Starts on | Behavior |
|---|---|---|
auto | Greptile or GitHub | Default. Greptile starts immediately. A GitHub PR event waits graceMs, then runs if Greptile has not started for that commit. |
github | GitHub PR events | Runs immediately on opened, reopened, and ready_for_review. Add github.onPush: true for synchronize events. |
greptile | Greptile check start | Only the Greptile start triggers automatic reviews. No GitHub fallback. |
Mentions work in all three modes. Only new PR comments by an OWNER, MEMBER, or COLLABORATOR qualify. Bot comments and ordinary issues do not.
- Receive an event. Each repo has a temporary webhook forwarded through
gh webhook forwardto the local signed endpoint. No public relay URL is needed. - Resolve and deduplicate. The scheduler keys automatic reviews by repository and head commit. Completed, skipped, and in-progress jobs suppress duplicates. Failed jobs can retry on another trigger.
- Choose the route. Fetch the PR and base branch, inspect the diff, and select reviewers. A skip stops before a worktree is created.
- Run the panel. Create a detached worktree, remove executable project-level agent config, and run the selected CLIs concurrently against the same rubric.
- Save and publish. Remove the temporary worktree, save reports, and create or update a PR comment if enabled.
GitHub PR events ignore drafts and closed PRs. Explicit mentions and manual runs require an open PR but can review a draft. They bypass completed-job deduplication, but not an already-running job in the same scheduler.
Greptile matching requires app greptile-apps, check name Greptile Review, and a created check that is queued or in progress. Completed checks only log a result. Fork checks without an associated PR are ignored; use auto for the GitHub fallback.
CLI commands
Run review-relay help <command> or review-relay <command> --help for command help. Global options are --config <path>, -h / --help, and -v / --version.
| Command | Arguments / options | Purpose |
|---|---|---|
setup | No required flags | Interactive repo and reviewer selection. Writes the config and preserves settings it does not ask about. |
start | -d, --detach | Watch repos. Foreground by default; -d runs in the background and writes daemon.log. |
stop | No flags | Stop the background daemon. Attempts graceful shutdown, then forces exit after 20 seconds. |
restart | No flags | Stop, then start in the background. Use after editing config. |
status | --limit <n> | Daemon, endpoint, forwarders, and recent jobs. Default 20 jobs; exits 3 when the daemon is not running. |
logs | [N], -f, --follow | Read the last N log lines, default 50. Use -f to follow new output. |
run | --repo <owner/name> --pr <n> [--route <name>] | Review an open PR now, including a previously reviewed commit. Can publish a comment. |
route | --repo <owner/name> --pr <n> [--source <source>] | Explain matching rules without running reviewers or posting. Fetches the PR and base refs. Default source: github. |
replay | <events.jsonl> --dry-run --grace <ms> | Process recorded deliveries. --dry-run logs would-run jobs; --grace overrides the auto-mode wait for this replay. |
info | --json | Resolved configuration and diagnostics. --json prints machine-readable output. |
config | No flags | Print the resolved configuration as JSON. |
help | [command] | List commands, or show a command’s options and examples. |
Preview or force a route
review-relay route --repo acme/widget --pr 42 --source githubrun --route sensitive forces that non-skip route. Unlike mentions, an unknown or skip route is rejected. route --source accepts github, greptile, mention, or manual. Route inspection also works on closed PRs.
Replay recorded deliveries
Use a JSONL file with one {"event":"pull_request","payload":{...}} object per line. body is accepted as an alternative to payload. The payload must contain the original GitHub fields.
review-relay replay events.jsonl --dry-run --grace 0Dry-run uses fresh in-memory state and logs which jobs would be dispatched. It does not fetch diffs or evaluate routes. Without --dry-run, replay can run agents, write state, and publish comments.
Scores & reports
Each successful reviewer scores correctness, security, code quality, standards, blast radius, and testing from 1 to 5, plus an overall merge confidence.
- A critical finding caps that reviewer at 2/5.
- A major finding caps that reviewer at 3/5.
- Overall confidence cannot exceed the weakest dimension by more than one point.
- The published score is the lowest capped score among successful reviewers, never an average.
Findings from different reviewers at the same file and within three lines are merged, retaining the highest severity. The comment links findings to the reviewed commit and includes dimension notes.
One comment, local reports
Publishing edits the signed-in GitHub user’s latest comment carrying the relay marker, or creates one if none exists. Switching GitHub accounts can create a separate comment. Scores are advisory; review-relay does not install a merge gate.
<dataDir>/reports/<owner__repo>/pr-<number>/<sha8>/
comment.md
<reviewer-id>.json
meta.jsonmeta.json records the job, route, selected models, timeouts, durations, and scores. Reviewer JSON contains the verdict, or an error and raw output on failure. Re-running the same PR commit writes to the same directory.
One failed reviewer does not cancel the others. A partial result can publish with the failure noted. If all reviewers fail, local reports are saved but no comment is posted, and the job fails. A publishing failure also leaves the local report available.
Security & privacy
Reviews run on your machine, but your code goes to the providers used by your selected agents and models. postToPr: false disables GitHub comments, not provider requests.
The webhook server binds to 127.0.0.1. POST /hook requires an HMAC-SHA256 signature using a secret generated at daemon startup. GET /health returns a local health response.
Reviewers use each CLI’s read-only controls, restricted tools, sandbox, or no-shell mode. The runner removes recognized project configs that could load hooks, plugins, or MCP servers from the review worktree before starting agents. Your original clone is not stripped.
These controls depend on the agent CLI and its version. They are not a separate VM boundary. Keep CLIs updated, review their authentication and provider settings, and treat PR content and model output as untrusted.
Reports and daemon logs can contain source code and findings. Protect dataDir accordingly. Stop the daemon normally so the forwarders can remove their temporary repository webhooks.
Troubleshooting
review-relay status
review-relay info
review-relay logs 100
gh auth statusNo review appeared
Check that the daemon and repo forwarder are running, your machine is awake, and fullName matches. In auto mode, allow the two-minute default grace period. Push events require github.onPush: true. Check for a draft, already-reviewed commit, or skip route.
For mention triggers, use a new PR comment from a trusted collaborator. To see ignored-event reasons, set REVIEW_RELAY_DEBUG=1 in the daemon’s environment and restart it.
A reviewer failed or timed out
Confirm its executable is on the daemon’s PATH and that the CLI can authenticate outside review-relay. Check model and effort support. Read that reviewer’s JSON in the report directory for the error and raw output. Increase timeoutMs if needed, then restart.
A failed job can retry on a later trigger. A partial review is completed, so use a mention or run to request it again.
Config edits had no effect
Run info with the same --config as the daemon. It flags unapplied edits, missing binaries, and missing clones. The daemon does not hot-reload its config; use restart.
The report exists but there is no PR comment
Check postToPr, GitHub authentication, and permission to comment. If every reviewer failed, the relay saves the report without publishing. Read comment.md and the log before retrying.
Port conflict, stale daemon, or leftover webhooks
status distinguishes a live relay from a stale daemon record or a different listener. Do not stop an unrelated service. Choose another port or stop the correct relay before starting again.
Forced exits and Windows shutdown can leave temporary webhooks behind. Inspect the repository’s Settings → Webhooks and remove only hooks you can identify as stale relay forwarders.
Git cannot fetch the PR
Check that localPath is an existing clone with an origin remote pointing at the configured GitHub repository. Confirm Git authentication works for that remote. The relay fetches the base branch and refs/pull/N/head, including fork PRs.