Self-hosted GitHub App for AI pull request reviews
pr-agent Β· Node 22+ Β· Postgres Β· pg-boss
Async by design. Webhooks return
200after durable intake (Postgres + pg-boss enqueue). Reactions, progress comments, reviews, descriptions, and ask answers publish onROLE=workerand may appear seconds later.
PR Agent is a GitHub App that enqueues durable agent work items (reviews, descriptions, asks, triage, verification) from webhooks and slash commands, then runs LLM agent loops on workers using local PR workspaces or isolated writable checkouts and GitHub APIs for publish.
Domain terms: CONTEXT.md. Features: docs/features.md. Configuration: docs/configuration.md. Behaviour and deployment: docs/operations.md. Queue runbook: docs/agent-work-ops.md.
- Getting Started
- Configure the agent provider
- Why Use PR Agent?
- Features
- See It in Action
- How It Works
- Data Privacy
- Create a GitHub App.
- Set Webhook URL to
https://<host>/webhooksand Webhook secret to matchWEBHOOK_SECRET. - Subscribe to events:
pull_request,issue_comment,pull_request_review_comment,workflow_run(do not requirepull_request_review).workflow_runpowers CI-summary refresh when CI finishes after the review summary is posted. - Repository permissions (typical): Issues and Pull requests read/write, Contents read/write, Metadata read, Checks read/write, Actions read. Contents write is only needed for
/triage. Commit statuses read/write whenFEATURE_COMMIT_STATUS=true. Checks + Actions read power the CI summary gate (status facts + condensed job logs for LLM-authored failure explanations). - Install the app on target orgs or repos. Set
GITHUB_APP_IDandGITHUB_APP_PRIVATE_KEYin.env(see.env.example).
Full stack: Postgres, web intake, and worker consumers.
cp .env.example .env
# Set GITHUB_*, WEBHOOK_SECRET, and agent provider keys (see Configure the agent provider)
docker compose build
docker compose up- Webhook URL (default ports):
http://<host>:7224/webhooks GET /health: liveness (ok)GET /ready: readiness (Postgres reachable)- Both
pr-agent-web(ROLE=web) andpr-agent-worker(ROLE=worker) are required for reviews and asks.
More deployment detail: docs/operations.md.
DATABASE_URL is required for both roles (src/config.ts).
docker compose up postgres
cp .env.example .env
npm install -g --ignore-scripts=false @nubjs/nub
nub install
# terminal 1: enqueue only
ROLE=web DATABASE_URL=postgres://pr_agent:pr_agent@localhost:5432/pr_agent nub src/index.ts
# terminal 2: reviews, descriptions, asks, triage, verification, CI refresh
ROLE=worker DATABASE_URL=postgres://pr_agent:pr_agent@localhost:5432/pr_agent nub src/index.tsnub src/index.ts loads .env automatically. For auto-restart on source changes, use nub watch src/index.ts. Tunnel webhooks (e.g. smee.io) to /webhooks.
If you previously installed with pnpm, delete node_modules before your first nub install to avoid mixed virtual-store layouts (.pnpm/ vs .nub/).
Vercel site deploys still use pnpm via site/vercel.json until Vercel has native Nub support.
Minimal env:
DATABASE_URL=...
GITHUB_APP_ID=...
GITHUB_APP_PRIVATE_KEY=...
WEBHOOK_SECRET=...
# Agent provider: see Configure the agent providerDeveloper scripts: see docs/operations.md.
LLM runs happen on ROLE=worker only. Pick a runner with AGENT_PROVIDER, then set model and credentials.
| Runner | AGENT_PROVIDER |
Model env | Credentials |
|---|---|---|---|
| Pi (default) | pi |
PI_PROVIDER, PI_MODEL |
Provider API key env vars (see below) |
| Cursor SDK | cursor |
PI_MODEL (Cursor model id) |
CURSOR_API_KEY (required) |
Feature catalog: docs/features.md. Operator tunables: docs/configuration.md. Cursor integration: ADR 0013.
Uses @earendil-works/pi-ai and the Pi coding-agent session loop.
AGENT_PROVIDER=pi
PI_PROVIDER=openai
PI_MODEL=gpt-4o-mini
OPENAI_API_KEY=sk-...PI_PROVIDER: pi-ai provider slug (for exampleopenai,anthropic,google,deepseek,openrouter,amazon-bedrock,groq). Startup validates against the installed pi-ai provider list, or against project-rootmodels.jsonwhen that file is present.PI_MODEL: model id for that provider (for examplegpt-4o,claude-sonnet-4-5).- API keys: set the env var for your provider. pr-agent loads
OPENAI_API_KEY,ANTHROPIC_API_KEY, andGOOGLE_GENERATIVE_AI_API_KEYinto the worker at startup. Other Pi providers use their standard env vars in the worker process (for exampleDEEPSEEK_API_KEY,OPENROUTER_API_KEY,GROQ_API_KEY). See the Pi providers reference for the full key table, cloud setup (Azure, Bedrock, Vertex), and custom endpoints. - Custom providers: optional
models.jsonin Piβs native format. If the catalog is loaded, the worker uses it and still selects withPI_PROVIDER/PI_MODEL. If it is absent, behavior falls back to env + built-ins. Delivery options: putmodels.jsonat the repo root in the Docker build context (copied to/app/models.jsonwhen present), mount it at runtime, or setMODELS_JSON_PATH. See docs/operations.md (Docker) and docs/configuration.md.
Do not set PI_PROVIDER=cursor. Use AGENT_PROVIDER=cursor for Cursor models instead.
Uses the Cursor SDK local agent with an HTTP MCP bridge to pr-agent's workspace, Context7, and orchestrated review tools. Register at worker boot only.
AGENT_PROVIDER=cursor
CURSOR_API_KEY=...
PI_MODEL=composer-2.5CURSOR_API_KEY: required whenAGENT_PROVIDER=cursor.PI_MODEL: Cursor model id fromCursor.models.list(). The worker fetches the live catalog at boot and validates your choice. Common ids (first ten from a typical list):composer-2.5,composer-2,gpt-5.5,gpt-5.4-high,claude-opus-4-7,claude-opus-4-8,claude-4.6-sonnet-high-thinking,gpt-5.3-codex-high,gemini-3.1-pro,auto. Append-fastfor the fast tier when the SDK exposes afastparameter (for examplecomposer-2.5-fast,gpt-5.5-fast). Plain ids use the standard tier.PI_PROVIDERis ignored for Cursor runs.
Restart pr-agent-worker (or the ROLE=worker process) after changing provider env vars.
Webhook handlers validate, dedupe, and enqueue work in Postgres before responding. Workers scale independently; a review backlog does not block webhook acceptance.
File listing and patch caps (MAX_PR_FILES_LISTED, MAX_PR_FILES_PATCH_BYTES) plus Octokit throttling keep runs bounded. Truncation metadata is explicit when the change set is clipped.
User-facing behavior is controlled by the FEATURE_* settings (docs/features.md); operator tunables live in docs/configuration.md. Prompt prose stays in code; limits and shared strings live in src/settings/.
Run on your infrastructure with your GitHub App credentials and chosen LLM provider (Pi/OpenAI, Cursor SDK, and others via @earendil-works/pi-ai).
| Capability | Auto on PR | Slash command | Notes |
|---|---|---|---|
| Orchestrated review | opened | /review |
Four specialists feed one ## PR Agent Review; inline P0βP3 appear on the Files tab when anchored (check fails only on P0βP2) |
| PR description | opened (configurable) | /describe |
Merges under ## PR Agent Description |
| Triage | No | /triage |
Fixes earlier findings, commits and pushes to the PR branch; ## PR Agent Triage |
| Verification | synchronize | No | Re-checks open bot findings against the new head; edits verification stubs in place |
| Ask | No | /ask <question> |
Also @bot mentions; PR conversation or inline diff code anchor |
| Help | No | /help |
Worker-published guidance |
| Lightweight auto-review | docs-only trivial PRs | No | Skips full orchestrated review run; see ADR 0014 |
| Deployment | Supported |
|---|---|
| Docker Compose (web + worker + Postgres) | Yes |
| Bare Node + Postgres | Yes |
Cursor provider (AGENT_PROVIDER=cursor) |
Yes |
Slash commands are case-sensitive and must start the first non-empty line of a new comment. Full behaviour: docs/operations.md.
flowchart LR
GitHub[GitHub webhooks] --> Web["ROLE=web /webhooks"]
Web --> Dedupe[Postgres webhook_events dedupe]
Dedupe --> Items[agent_work_items]
Items --> Boss[pg-boss enqueue]
Boss --> AckQ[ack queue]
Boss --> CiRefQ[ci-refresh queue]
Boss --> RevQ[review queue]
Boss --> AskQ[ask queue]
Boss --> DescQ[description queue]
Boss --> TriageQ[triage queue]
Boss --> VerifQ[verification queue]
Boss --> RetQ[retention queue]
AckQ --> Worker["ROLE=worker executors"]
CiRefQ --> Worker
RevQ --> Worker
AskQ --> Worker
DescQ --> Worker
TriageQ --> Worker
VerifQ --> Worker
RetQ --> Worker
Worker --> Retention[retention cleanup]
Retention --> Dedupe
Retention --> Items
RevQ --> ReviewExec[Review executor]
ReviewExec --> Orchestrator[Review orchestrator]
Orchestrator --> Correctness[Correctness specialist]
Orchestrator --> Security[Security specialist]
Orchestrator --> Quality[Quality specialist]
Orchestrator --> Tests[Tests specialist]
Correctness --> Orchestrator
Security --> Orchestrator
Quality --> Orchestrator
Tests --> Orchestrator
Orchestrator --> Publish[GitHub PR-surface publish]
Worker --> Push[git push PR branch]
- Web (
processWebhookRequestEffect): verify signature, parse payload, durable dedupe, schedule agent work items (slash commands, or@botmentions promoted to ask). - Scheduler (
AgentWorkScheduler): write Postgres rows and enqueue pg-boss jobs (ack, ci-refresh, review, ask, description, triage, verification). - Ack worker: acknowledgement reaction and review progress comment stub before long runs. CI-refresh worker: after
workflow_runcompleted, surgically updates the CI cell on the matching review summary comment (no full re-review). - Worker maintenance (
AgentWorkerLive): owns pg-boss cron/supervision and the daily retention cleanup lane. - Review / ask / description / triage / verification workers (
executors/): installation token, local PR workspace or isolated writable checkout, agent harness, PR-surface I/O. Ask workers load the containing comment thread before the LLM turn. - Reviews (
runOrchestratedPrReview): reconnaissance, a specialist brief, four parallel specialists, incrementalCOMMENTthread batches, then the final summary.
Queue inspection and recovery: docs/agent-work-ops.md. Architecture ADR: docs/adr/0009-durable-agent-work.md. Conversational ask: docs/adr/0008-ask-command.md.
Postgres, pg-boss, and GitHub App credentials run on your infrastructure. Webhook bodies and workflow state stay in your database.
Review, description, and ask content is sent to your configured model provider during worker runs only (Pi/OpenAI, Cursor, or others per PI_PROVIDER / AGENT_PROVIDER). See your provider's data policy (for example OpenAI or Cursor).
When enabled, library lookup tools may call https://context7.com/api. Set CONTEXT7_API_KEY for higher limits; queries leave your network to Context7 when those tools run.
Structured logs use evlog on your hosts. LOG_REDACT (default true) redacts secret-shaped substrings; tune via docs/configuration.md.
/ask replies apply outbound redaction before posting. Probes for bot internals may receive an Ask meta refusal without an LLM call (ADR 0010).
More security detail: docs/operations.md.


