AI-powered code review service built with TypeScript + NestJS. This repository is now a pnpm monorepo: the backend service lives in backend/, and the Next.js frontend lives in frontend/.
- Beta Status
- Features
- Review Triggers
- Roadmap & Process Assets
- Architecture
- Tech Stack
- Project Structure
- Getting Started
- Configuration
- Deployment
- Platform Integration
- Commands
- Repository Policy
- Observability
- Testing
- License
- PR Agent is currently in beta.
- The only officially supported integration mode is GitHub App.
- Plain GitHub Webhook and GitLab code paths may still exist in the repository, but they are not part of the supported deployment surface right now.
Core Review
- Automatic AI code review on PR open / update / merge
- Inline comments with GitHub Suggested Changes
- Report-mode summaries with risk assessment
- Incremental review (only new commits since last review)
- Mermaid change-structure diagrams in reports (directory/file visualization)
- Secret leak detection in diffs (lightweight regex-based scanning)
- Auto-labeling (bugfix / feature / refactor / docs / security)
Interactive Commands
/ai-review— trigger manual review (comment or report mode)/ask <question>— multi-turn Q&A about the code/checks [question]— diagnose CI failures/generate_tests [focus]— generate test code/changelog [--apply]— generate or commit changelog/describe [--apply]— generate or update PR description/improve [focus]— force improvement-focused review output/add_doc [focus](/add-doc) — documentation-only review suggestions/reflect [goal]— ask AI to generate requirement/acceptance clarifying questions/similar_issue [query](/similar-issue) — find related issues in the same repository/custom_prompt <prompt>— run a custom AI prompt against PR changes/help_docs <question>— ask questions about project documentation (/docsdirectory)/analyze— list all changed components with risk assessment/compliance [focus]— compliance and security review/improve_component <component>— improvement review scoped to a specific component/generate_labels— auto-generate and apply PR labels from diff content/similar_code [query]— detect duplicate or similar code patterns in diff/auto_approve— conditional auto-approve based on AI risk assessment (default off)/scan_repo_discussions— extract best practices from recent merged PR discussions/feedback— provide learning signals to improve future reviews
Process Guardrails
.pr-agent.ymlper-repo policy (remind or enforce mode)- Issue & PR template completeness checks
- GitHub Check integration for branch protection (
enforcemode) - Process guideline detection (
.github/.gitlabworkflows, templates, CODEOWNERS, CONTRIBUTING) with compliance suggestions - Issue creation/edit pre-check and PR pre-merge validation (GitHub)
- Default issue auto-triage (feasibility + suggestions + similar issues + labels) when
.pr-agent.ymlis absent
Current Support
- GitHub App only
- Plain GitHub Webhook — unsupported in the current beta
- GitLab Webhook — unsupported in the current beta
- GitHub Actions — not supported
Multi-Provider AI
- OpenAI
- OpenAI-compatible (DeepSeek, etc.)
- Anthropic (Claude)
- Google Gemini
| Trigger | Mode | Dedup Window |
|---|---|---|
PR opened (opened) |
comment / report (per config) | 5 min |
PR updated (synchronize) |
comment / report | 5 min (per new commit SHA) |
PR edited (edited) |
comment / report | 5 min |
PR merged (closed + merged) |
report | 24h (configurable) |
/ai-review comment command |
comment / report | 5 min |
/ai-review report |
report | 5 min |
/ai-review comment |
comment | 5 min |
Webhook header x-ai-mode (GitLab only, unsupported in current beta) |
report / comment | 5 min |
| Issue created / edited | pre-check (GitHub) + default auto-triage when policy file is missing | 5 min |
| PR created / edited / synced | pre-merge check (GitHub) | — |
- Competitive gap backlog:
docs/roadmap/2026-02-19-competitive-gap-backlog.md - GitHub issue templates:
.github/ISSUE_TEMPLATE/bug_report.md,.github/ISSUE_TEMPLATE/feature_request.md - GitHub PR template:
.github/pull_request_template.md - GitLab issue templates:
.gitlab/issue_templates/Bug.md,.gitlab/issue_templates/Feature.md - GitLab MR template:
.gitlab/merge_request_templates/default.md
Recommended GitHub Flow baseline:
- Enable branch protection — require
PR Agent Policycheck and CI to pass. - Set
mode: enforcein.pr-agent.yml(start with core repositories, then roll out). - Use standardized issue/PR templates to prevent missing requirements and test plans.
graph LR
ROOT[pr-agent monorepo]
ROOT --> BE[backend/<br/>NestJS + Probot + tests]
ROOT --> FE[frontend/<br/>Next.js marketing / console]
ROOT --> OPS[Docker / Compose<br/>backend deployment]
ROOT --> NP[Nixpacks<br/>frontend deployment]
ROOT --> DOCS[docs/<br/>roadmaps and process assets]
graph TB
subgraph Platforms
GH_APP[GitHub App<br/>Probot]
GH_WH[GitHub Webhook]
GL_WH[GitLab Webhook]
end
subgraph NestJS Application
direction TB
CTRL[Controllers<br/>Route & Validate]
SVC[Services<br/>Signature Verify & Dispatch]
INT[Integration Layer<br/>GitHub / GitLab Adapters]
RE[Review Engine<br/>AI Reviewer + Patch Parser]
RR[Report Renderer<br/>Markdown + Suggestions]
end
subgraph External
AI[AI Providers<br/>OpenAI / Anthropic / Gemini]
GH_API[GitHub API]
GL_API[GitLab API]
NOTIFY[Notifications<br/>Slack / Discord / WeChat]
end
GH_APP -->|Webhook Event| CTRL
GH_WH -->|Webhook Event| CTRL
GL_WH -->|Webhook Event| CTRL
CTRL --> SVC
SVC --> INT
INT --> RE
RE -->|LLM Call| AI
RE --> RR
RR -->|Post Comment / Report| GH_API
RR -->|Post Comment / Report| GL_API
INT -.->|Optional| NOTIFY
sequenceDiagram
participant P as Platform (GitHub/GitLab)
participant C as Controller
participant S as Service
participant D as Deduplication
participant I as Integration
participant A as AI Reviewer
participant R as Report Renderer
P->>C: Webhook Event
C->>S: Validate signature & parse payload
S->>D: Check dedup (FNV hash, 5min window)
alt Duplicate
D-->>S: Skip (already processed)
else New request
D-->>S: Proceed
S->>I: Dispatch to platform handler
I->>I: Fetch diff, file contents, policy
I->>A: Send context + diff to AI model
A-->>I: Structured review result (Zod-validated)
I->>R: Format report / inline comments
R->>P: Post review via API
I-->>P: Optional notification webhook
end
graph LR
subgraph NestJS Modules
APP[AppModule]
GH[GithubModule]
GL[GitlabModule]
GHA[GithubAppModule]
end
subgraph Integration Layer
GHR[github-review]
GHW[github-webhook]
GHP[github-policy]
GHC[github-content]
GLR[gitlab-review]
NTF[notification]
end
subgraph Core
REV[ai-reviewer]
PAT[patch parser]
RPT[report-renderer]
POL[review-policy]
DDP[dedupe]
HTTP[http client]
STA[runtime-state]
SEC[secret-patterns]
CACHE[cache]
end
APP --> GH
APP --> GL
APP --> GHA
GH --> GHR
GH --> GHW
GHA --> GHR
GHR --> GHP
GHR --> GHC
GHR --> REV
GHR --> RPT
GHR --> NTF
GLR --> REV
GLR --> RPT
GLR --> NTF
GL --> GLR
REV --> PAT
REV --> POL
REV --> HTTP
GHR --> DDP
GLR --> DDP
DDP --> STA
GHR --> SEC
GLR --> SEC
GHP --> CACHE
The following diagram traces every request path — from webhook entry through event routing, command parsing, the common review engine, and output delivery.
flowchart LR
subgraph E["Entry Endpoints (NestJS + Probot)"]
E1["/api/github/webhooks<br/>GithubAppBootstrapService"]
E2["/github/trigger<br/>GithubWebhookController"]
E3["/gitlab/trigger<br/>GitlabWebhookController"]
E4["/github/replay/:eventId<br/>/gitlab/replay/:eventId"]
E5["/health /metrics /webhook/events"]
end
subgraph GHA["GitHub App Event Routing (backend/src/app.ts)"]
A1["issues.opened, issues.edited<br/>runGitHubIssuePolicyCheck"]
A2["pull_request.opened, edited, synchronize<br/>runGitHubPullRequestPolicyCheck"]
A3["resolveGitHubPullRequestAutoReviewPolicy"]
A4["pull_request.closed (merged)<br/>resolveGitHubReviewBehaviorPolicy"]
A5["issue_comment.created (PR + Issue)<br/>handleGitHubIssueCommentCommand"]
A6["pull_request_review_thread<br/>recordGitHubFeedbackSignal"]
end
subgraph GHW["Plain GitHub Webhook (backend/src/integrations/github/github-webhook.ts)"]
B1["handlePlainGitHubWebhook"]
B2["verifyWebhookSignature + payload schema"]
B3["pull_request / issues / issue_comment / review_thread dispatch"]
B4["runGitHubPullRequestPolicyCheck + runGitHubIssuePolicyCheck"]
B5["handleGitHubIssueCommentCommand"]
end
subgraph GLW["GitLab Webhook (backend/src/integrations/gitlab/gitlab-review.ts)"]
C1["runGitLabWebhook"]
C2["verify token + payload schema"]
C3["handleGitLabMergeRequestWebhook"]
C4["handleGitLabNoteWebhook"]
C5["resolveGitLabReviewPolicy (.pr-agent.yml)"]
end
subgraph CMD["Command Parsing Surface"]
D1["parseReviewCommand"]
D2["parseAsk / parseMention / parseChecks / parseGenerateTests"]
D3["parseDescribe / parseChangelog"]
D4["parseImprove / parseAddDoc / parseReflect / parseSimilarIssue / parseFeedback"]
end
subgraph CORE["Common Review Engine"]
R1["runGitHubReview / runGitLabReview"]
R2["dedupe + rate-limit + incremental head + feedback signals"]
R3["collect context: diff/files/ci/policy"]
R4["analyzePullRequest / answerPullRequestQuestion"]
R5["patch parser + hunk prioritize + line mapping"]
R6["secret scan + auto label"]
end
subgraph OUT["Output + Ops"]
O1["buildReportCommentMarkdown"]
O2["buildIssueCommentMarkdown"]
O3["createComment / notes / managed upsert"]
O4["publishNotification (optional)"]
O5["pr_agent_* metrics + replay store"]
end
E1 --> A1
E1 --> A2 --> A3 --> R1
E1 --> A4 --> R1
E1 --> A5
E1 --> A6
E2 --> B1 --> B2 --> B3
B3 --> B4 --> R1
B3 --> B5
E3 --> C1 --> C2
C2 --> C3 --> C5 --> R1
C2 --> C4 --> C5
A5 --> D1
A5 --> D2
A5 --> D3
A5 --> D4
B5 --> D1
B5 --> D2
B5 --> D3
B5 --> D4
C4 --> D1
C4 --> D2
C4 --> D3
C4 --> D4
D1 --> R1
D2 --> R4
D3 --> R4
D4 --> R1
R1 --> R2 --> R3 --> R4 --> R5
R4 --> R6
R5 --> O1 --> O3
R5 --> O2 --> O3
R6 --> O3
O3 --> O4
E4 --> B1
E4 --> C1
E5 --> O5
E2 --> O5
E3 --> O5
O3 -. feedback signals .-> R2
| Layer | Technology |
|---|---|
| Runtime | Node.js 22 (Alpine) |
| Language | TypeScript 5.7 (ES2022, strict mode) |
| Framework | NestJS 11 |
| HTTP Server | Express 4 |
| GitHub App | Probot 13 |
| AI Client | OpenAI SDK 4 (also used for compatible providers) |
| Validation | Zod 3 |
| State Store | In-memory / JSON file / SQLite (Node.js built-in node:sqlite) |
| Container | Docker (multi-stage build) |
pr-agent/
├── backend/
│ ├── src/
│ │ ├── main.ts # NestJS bootstrap & server startup
│ │ ├── app.ts # Probot event handlers (GitHub App)
│ │ ├── app.module.ts # Root NestJS module
│ │ ├── common/ # Filters and shared request types
│ │ ├── core/ # Runtime state, cache, env, i18n, HTTP
│ │ ├── integrations/ # GitHub / GitLab / notify adapters
│ │ ├── modules/ # NestJS webhook, health, replay modules
│ │ └── review/ # AI review engine and report rendering
│ ├── tests/ # Backend test suite
│ ├── package.json
│ └── tsconfig.json
├── frontend/
│ ├── app/ # Next.js App Router pages and layouts
│ ├── components/ # Frontend UI components
│ ├── public/ # Static assets
│ ├── package.json
│ └── next.config.ts
├── docs/ # Product docs, roadmaps, process assets
├── data/ # Local/runtime persisted state mount
├── secrets/ # Local private key mount (not committed)
├── Dockerfile # Backend container image
├── docker-compose.yml # Backend deployment and local ops
├── nixpacks.toml # Root Nixpacks entry for frontend
├── package.json # Workspace scripts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── turbo.json
├── README-zh.md
└── README.md
- The repository root
.envis shared by local backend development and Docker Compose. backend/is the deployable review service.frontend/is deployed separately when using rootnixpacks.toml.- Persistent backend state defaults to
/data/pr-agent/...inside containers anddata/pr-agent/...on the host.
The backend package uses Node.js ESM subpath imports (configured in backend/tsconfig.json and backend/package.json):
| Alias | Maps to |
|---|---|
#core |
backend/src/core/index.ts |
#review |
backend/src/review/index.ts |
#integrations/github |
backend/src/integrations/github/index.ts |
#integrations/gitlab |
backend/src/integrations/gitlab/index.ts |
#integrations/notify |
backend/src/integrations/notify/index.ts |
- Node.js >= 22
- pnpm >= 10
# Install workspace dependencies
pnpm install
# Prepare env file at repository root
cp .env.example .env
# Start backend locally (reads ../.env from backend package)
pnpm dev:backend
# Start frontend locally
pnpm dev:frontend
# Build backend for production
pnpm --filter backend build
# Start built backend
pnpm --filter backend startThe backend listens on port 3000 by default (configurable via PORT). The frontend also defaults to 3000 when run separately.
curl http://localhost:3000/health
curl http://localhost:3000/health?deep=true # Tests AI provider connectivity
curl http://localhost:3000/github/health # GitHub config statusCopy .env.example and fill in the required values. The supported minimal configuration is:
.env.github-app.min.example— GitHub App minimum
Legacy minimal examples for plain GitHub Webhook and GitLab remain in the repository for reference only; they are not part of the supported beta deployment path.
graph LR
ENV[AI_PROVIDER env var]
ENV -->|openai| OAI[OpenAI<br/>OPENAI_API_KEY + OPENAI_MODEL]
ENV -->|openai-compatible| OC[OpenAI-Compatible<br/>OPENAI_BASE_URL + OPENAI_COMPATIBLE_MODEL]
ENV -->|anthropic| ANT[Anthropic<br/>ANTHROPIC_API_KEY + ANTHROPIC_MODEL]
ENV -->|gemini| GEM[Gemini<br/>GEMINI_API_KEY + GEMINI_MODEL]
OpenAI (default)
AI_PROVIDER=openai
OPENAI_API_KEY=sk-...
OPENAI_MODEL=gpt-4.1-miniOpenAI-Compatible (e.g., DeepSeek)
AI_PROVIDER=openai-compatible
OPENAI_BASE_URL=https://api.deepseek.com/v1
OPENAI_COMPATIBLE_API_KEY=...
OPENAI_COMPATIBLE_MODEL=deepseek-chatAnthropic
AI_PROVIDER=anthropic
ANTHROPIC_API_KEY=sk-ant-...
ANTHROPIC_MODEL=claude-3-5-haiku-latest
ANTHROPIC_MAX_TOKENS=8192Gemini
AI_PROVIDER=gemini
GEMINI_API_KEY=...
GEMINI_MODEL=gemini-2.0-flash| Backend | Env Value | Use Case |
|---|---|---|
| Memory | memory (default) |
Stateless containers, dev |
| JSON File | file |
Simple single-instance |
| SQLite | sqlite (recommended) |
Production single-instance |
RUNTIME_STATE_BACKEND=sqlite
RUNTIME_STATE_SQLITE_FILE=/data/pr-agent/runtime-state.sqlite3Supports WeChat (WeCom), Slack, Discord, and generic JSON webhooks:
NOTIFY_WEBHOOK_URL=https://hooks.slack.com/services/...
NOTIFY_WEBHOOK_FORMAT=slack # wecom | slack | discord | genericAI provider tuning
| Variable | Default | Description |
|---|---|---|
AI_HTTP_TIMEOUT_MS |
30000 (30s) |
HTTP timeout for AI review requests |
AI_ASK_HTTP_TIMEOUT_MS |
60000 (60s) |
HTTP timeout for /ask command (longer for multi-turn Q&A) |
AI_HTTP_RETRIES |
2 |
Number of retries on transient AI provider failures |
AI_HTTP_RETRY_BACKOFF_MS |
400 |
Retry backoff base delay for AI provider requests (ms) |
AI_MAX_CONCURRENCY |
4 |
Max concurrent AI requests |
AI_MAX_QUEUE_SIZE |
200 |
Max queued AI requests before rejecting new work |
AI_SHUTDOWN_DRAIN_TIMEOUT_MS |
15000 (15s) |
Graceful shutdown drain timeout for in-flight AI tasks |
AI_DEBUG_LOGS |
false |
Log AI request/response payloads for debugging |
HEALTHCHECK_AI_TIMEOUT_MS |
5000 (5s) |
Timeout for deep health check AI probe |
HEALTHCHECK_AI_LIVE_PROBE |
false |
Perform actual API call for GET /health?deep=true instead of config-only check |
AI_CONCURRENCY_LIMIT is no longer used. Use AI_MAX_CONCURRENCY and AI_MAX_QUEUE_SIZE instead.
Global runtime and command limits
| Variable | Default | Description |
|---|---|---|
WEBHOOK_BODY_LIMIT |
1mb |
Global Express body-parser limit for webhook requests (returns 413 when exceeded) |
COMMAND_RATE_LIMIT_MAX |
10 |
Max command invocations per user per PR/MR per window |
COMMAND_RATE_LIMIT_WINDOW_MS |
3600000 (1h) |
Command rate-limit window |
GitHub-specific
| Variable | Default | Description |
|---|---|---|
GITHUB_MERGED_DEDUPE_TTL_MS |
86400000 (24h) |
Dedup window for merged + report events |
GITHUB_FEEDBACK_SIGNAL_TTL_MS |
2592000000 (30d) |
Feedback signal retention window |
GITHUB_INCREMENTAL_STATE_TTL_MS |
604800000 (7d) |
Incremental review SHA cache lifetime |
GITHUB_POLICY_CONFIG_CACHE_TTL_MS |
300000 (5min) |
.pr-agent.yml cache for policy/review behavior |
GITHUB_POLICY_COMMENT_DEDUPE_TTL_MS |
600000 (10min) |
Dedup window for policy reminder comments |
GITHUB_ISSUE_AUTO_TRIAGE_ENABLED |
true |
Enable default issue auto-triage on issues.opened/edited when no .pr-agent.yml exists |
GITHUB_ISSUE_AUTO_TRIAGE_AUTO_LABELS |
true |
Add inferred labels for default issue auto-triage |
GITHUB_ISSUE_AUTO_TRIAGE_DEDUPE_TTL_MS |
300000 (5min) |
Dedup window for issue auto-triage comments |
GITHUB_ISSUE_AUTO_TRIAGE_SIMILAR_LIMIT |
5 |
Max number of similar issues shown in auto-triage output |
GITHUB_ISSUE_TRIAGE_DISABLE_AI |
false |
Skip AI call and use fallback triage summary only |
GITHUB_WEBHOOK_MAX_BODY_BYTES |
10485760 (10MB) |
Extra hard cap for /github/trigger payload size |
GITHUB_WEBHOOK_SKIP_SIGNATURE |
false |
Debug-only signature bypass; forbidden when NODE_ENV=production |
GitLab-specific
| Variable | Default | Description |
|---|---|---|
GITLAB_MERGED_DEDUPE_TTL_MS |
86400000 (24h) |
Merged + report event dedup window |
GITLAB_INCREMENTAL_STATE_TTL_MS |
604800000 (7d) |
Incremental review SHA cache |
GITLAB_FEEDBACK_SIGNAL_TTL_MS |
2592000000 (30d) |
Feedback learning signal retention |
GITLAB_POLICY_CONFIG_CACHE_TTL_MS |
300000 (5min) |
.pr-agent.yml policy cache |
GITLAB_CHANGELOG_PATH |
CHANGELOG.md |
File path for /changelog --apply |
GITLAB_WEBHOOK_MAX_BODY_BYTES |
10485760 (10MB) |
Extra hard cap for /gitlab/trigger payload size |
GITLAB_REQUIRE_WEBHOOK_SECRET |
false |
Require GITLAB_WEBHOOK_SECRET, otherwise reject unsigned webhooks |
ALLOW_INSECURE_GITLAB_HTTP |
false |
Allow http:// GitLab base URL for local testing only |
Tuning guidance:
MERGED_DEDUPE_TTL_MS— Controls the dedup window formerged + reportevents to prevent duplicate reviews on webhook retries. Increase to48h–72hif you experience frequent webhook redelivery; decrease to1h–6hif you need faster re-trigger. This only affects merged auto-triggers; manual/ai-reviewcommands use the short (5 min) dedup window.FEEDBACK_SIGNAL_TTL_MS— Controls how long/feedbackand review-thread resolved/unresolved signals are retained. Increase to60–90dfor teams with slower review cadence; decrease to7–14dif review rules change frequently.COMMAND_RATE_LIMIT_*— Controls abuse protection for comment commands. Increase limits for very active repos; lower limits if bots/users spam command threads.
See .env.example for the baseline variable reference; advanced runtime knobs are listed above.
graph LR
subgraph Docker Build
B1[Stage 1: Build<br/>node:22-alpine<br/>pnpm install + backend build]
B2[Stage 2: Runtime<br/>node:22-alpine<br/>prod deps only]
B1 -->|COPY dist/| B2
end
B2 -->|Expose 3000| SRV[pr-agent container]
Build & Run
docker build -t pr-agent:latest .
docker run -d \
--name pr-agent \
-p 3000:3000 \
--env-file .env \
-v ./data:/data \
pr-agent:latest# Start
docker compose up -d --build
# View logs
docker compose logs -f pr-agent
# Restart
docker compose restart pr-agent
# Stop
docker compose downThe docker-compose.yml mounts:
./data:/data— persistent state (SQLite, event store)./secrets/github-app.private-key.pem:/run/secrets/github-app-private-key.pem:ro— GitHub App private key
The root nixpacks.toml is for deploying frontend/ from the repository root. It does not build or start the backend service.
- Install:
pnpm install --frozen-lockfile - Build:
pnpm --filter frontend build - Start:
pnpm --filter frontend start
graph TD
A[Choose Platform Mode] -->|GitHub App| B1[Create GitHub App]
A -->|Plain Webhook| B2[Generate PAT Token]
A -->|GitLab| B3[Generate API Token]
B1 --> C[Configure AI Provider]
B2 --> C
B3 --> C
C --> D[Set State Backend = sqlite]
D --> E[Deploy Container]
E --> F[Configure Webhook URL]
F --> G[Verify Health Endpoints]
G --> H[Test with /ai-review Command]
- Use GitHub App integration mode — this is the only supported integration in the current beta
- Configure AI provider — Set
AI_PROVIDERand the corresponding API key / model - Enable state persistence — Use
RUNTIME_STATE_BACKEND=sqlitefor production - Deploy the container — Docker, Docker Compose, Render, Railway, Fly.io, or K8s
- Set webhook URL to
https://<domain>/api/github/webhooks - Verify via health endpoints and a test
/ai-reviewcomment
This is a compact deployment path for Render/Railway/Fly.io/Docker/K8s.
- Use GitHub App mode
- Use minimal environment variables
GitHub App minimum (.env.github-app.min.example)
APP_ID=123456
PRIVATE_KEY_PATH=/run/secrets/github-app-private-key.pem
# PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\\n...\\n-----END RSA PRIVATE KEY-----\\n"
WEBHOOK_SECRET=replace-with-webhook-secret
AI_PROVIDER=openai
OPENAI_API_KEY=replace-with-openai-key
OPENAI_MODEL=gpt-4.1-miniRecommended single-instance extras
RUNTIME_STATE_BACKEND=sqlite
RUNTIME_STATE_SQLITE_FILE=/data/pr-agent/runtime-state.sqlite3
WEBHOOK_EVENT_STORE_ENABLED=false
WEBHOOK_REPLAY_ENABLED=falseTemporary replay settings for debugging
WEBHOOK_EVENT_STORE_ENABLED=true
WEBHOOK_REPLAY_ENABLED=true
WEBHOOK_REPLAY_TOKEN=replace-with-strong-random-token- Start backend application
pnpm install --frozen-lockfile
pnpm --filter backend build
pnpm --filter backend startThe backend listens on PORT (default 3000).
- Configure webhook address
- GitHub App:
https://<domain>/api/github/webhooks
- GitHub App:
- Docker one-liner
docker build -t pr-agent:latest .
docker run -d --name pr-agent -p 3000:3000 --env-file .env pr-agent:latest- Verification checklist
GET /healthGET /github/healthGET /metrics- Test PR/MR commands:
/ai-review,/ai-review comment - Webhook failure response includes structured fields:
error/type/status/path/method/timestamp
If replay is enabled:
GET /webhook/eventsPOST /github/replay/:eventId- Header:
x-pr-agent-replay-token: <WEBHOOK_REPLAY_TOKEN>
There is no committed deploy/nginx/ sample in this repository; use the snippet below as a baseline:
server {
listen 443 ssl;
server_name pr-agent.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}- Create a GitHub App with the following permissions:
- Pull requests: Read & write
- Issues: Read & write
- Contents: Read & write (required for
/changelog --apply) - Checks: Read & write (required for
mode=enforce) - Metadata: Read-only
- Subscribe to events:
Pull request(opened/edited/synchronize/closed)Issues(opened/edited)Issue commentPull request review thread(resolved/unresolved, for feedback learning)
- Set webhook URL to
https://<domain>/api/github/webhooks - Configure environment variables:
APP_ID=123456
PRIVATE_KEY_PATH=/run/secrets/github-app-private-key.pem
# PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----\n"
WEBHOOK_SECRET=your-webhook-secret- GitHub App enablement condition (see
backend/src/modules/github-app/github-app.bootstrap.service.ts):APP_ID+ (PRIVATE_KEYorPRIVATE_KEY_PATH) +WEBHOOK_SECRET. docker-compose.ymlmounts the private key at./secrets/github-app.private-key.pem:/run/secrets/github-app-private-key.pem:ro. Ifsecrets/or the PEM file is missing,docker compose upwill fail.- Recommended local prep:
mkdir -p secrets data. Docker usually auto-createsdata, but pre-creating it is more predictable.
If
APP_ID+PRIVATE_KEY(+WEBHOOK_SECRET) are not configured, GitHub App mode is disabled and the beta deployment is considered incomplete.
- Plain GitHub Webhook is not a supported deployment target right now.
- GitLab Webhook is not a supported deployment target right now.
All commands are triggered via PR/MR comments. Commands marked with (PR only) require Pull Request context (diff, CI checks) and will not work in Issue comments.
| Command | Description |
|---|---|
/ai-review (PR only) |
Trigger AI review (defaults to report when no mode is provided) |
/ai-review report (PR only) |
Force report mode (summary comment) |
/ai-review comment (PR only) |
Force comment mode (inline comments) |
/ai-review --mode=report / --mode=comment (PR only) |
Flag-style mode override (same effect as positional mode) |
/ask <question> |
Ask about the PR or Issue (multi-turn conversation) |
@bot-name <question> |
@mention the bot to ask a question (set GITHUB_APP_SLUG or GITHUB_BOT_LOGIN) |
/checks [question] (PR only) |
Analyze CI check failures |
/generate_tests [focus] (PR only) |
Generate test code for changes |
/changelog [focus] (PR only) |
Generate changelog entry |
/changelog --apply [focus] (PR only) |
Generate and commit changelog to repo |
/describe (PR only) |
Generate PR/MR description |
/describe --apply (PR only) |
Generate and update PR/MR description |
/improve [focus] (PR only) |
Run review in improvement-only mode |
/add_doc [focus] / /add-doc [focus] (PR only) |
Generate documentation-only suggestions |
/reflect [goal] (PR only) |
Generate clarifying requirement/acceptance questions |
/similar_issue [query] / /similar-issue [query] |
Search related issues in the same repository |
/custom_prompt <prompt> (PR only) |
Run a custom AI prompt against PR changes |
/help_docs <question> |
Ask questions about project documentation (/docs directory) |
/analyze (PR only) |
List all changed components with risk assessment |
/compliance [focus] (PR only) |
Compliance and security review |
/improve_component <component> (PR only) |
Improvement review scoped to a specific component |
/generate_labels (PR only) |
Auto-generate and apply PR labels from diff content |
/similar_code [query] (PR only) |
Detect duplicate or similar code patterns in diff |
/auto_approve (PR only) |
Conditional auto-approve based on AI risk assessment (default off) |
/scan_repo_discussions (PR only) |
Extract best practices from recent merged PR discussions |
/feedback resolved|dismissed|up|down [note] |
Provide review quality feedback |
Additional aliases are supported, for example: /ai-review ask ..., /ai-review checks ..., /ai-review generate-tests ..., /ai-review add-doc ....
Policy toggles in .pr-agent.yml exist for /describe, /ask, /checks, /generate_tests, /changelog, /feedback, /improve, /add_doc, /custom_prompt, /help_docs, /analyze, /compliance, /similar_code, /auto_approve, /scan_repo_discussions; /reflect depends on askCommandEnabled; /improve_component depends on improveCommandEnabled; /generate_labels depends on autoLabelEnabled.
Note:
/ask,/feedback,/similar_issue,/help_docs, and@mentionwork in both Issue and PR comments. For Issue comments, no diff or CI context is provided, but the Issue title and body are passed to the AI as context. SetGITHUB_APP_SLUG(GitHub App slug) orGITHUB_BOT_LOGIN(exact bot login) to enable@mentiondetection. Mentions with and without the[bot]suffix are both supported.
Add .pr-agent.yml to the repository root to configure per-repo behavior:
mode: remind # remind = comment only, enforce = fail GitHub Check
issue:
enabled: true
minBodyLength: 20
requiredSections:
- Summary
- Steps to Reproduce
- Expected Behavior
pullRequest:
enabled: true
minBodyLength: 20
requireLinkedIssue: false
requiredSections:
- Summary
- Test Plan
review:
enabled: true
mode: comment # comment | report
onOpened: true # Review on PR open
onEdited: false
onSynchronize: true # Review on new commits
describeEnabled: true
describeAllowApply: false
checksCommandEnabled: true
includeCiChecks: true
askCommandEnabled: true
generateTestsCommandEnabled: true
changelogCommandEnabled: true
changelogAllowApply: false
feedbackCommandEnabled: true
improveCommandEnabled: true
addDocCommandEnabled: true
customPromptCommandEnabled: true
helpDocsCommandEnabled: true
analyzeCommandEnabled: true
complianceCommandEnabled: true
similarCodeCommandEnabled: true
autoApproveCommandEnabled: false # default off for safety
scanRepoDiscussionsCommandEnabled: true
secretScanEnabled: true
autoLabelEnabled: true
secretScanCustomPatterns: # optional extra secret regex patterns
- "(?i)my_internal_token_[a-z0-9]{16,}"
customRules:
- All public APIs must have type annotations
- No new `any` types allowedGitLab currently reads only the review: section from .pr-agent.yml. Top-level mode, issue, and pullRequest checks are enforced in GitHub policy flows.
Modes:
remind— posts a comment noting missing fields; does not block mergeenforce— creates a failing GitHub Check (PR Agent Policy) that can be required in branch protection rules
Template fallback behavior:
- When
requiredSectionsis not configured, section headings are automatically extracted from repository templates:- Issue:
.github/ISSUE_TEMPLATE/*or.github/ISSUE_TEMPLATE.md - PR:
.github/pull_request_template.mdor.github/PULL_REQUEST_TEMPLATE.md
- Issue:
- If no templates exist, built-in defaults are used (Issue:
Summary / Steps to Reproduce / Expected Behavior; PR:Summary / Test Plan / Related Issue).
Review field reference:
| Field | Description |
|---|---|
enabled / mode |
Enable AI review and set default mode (comment / report) |
onOpened / onEdited / onSynchronize |
Control which PR events auto-trigger review |
describeEnabled |
Enable /describe command |
describeAllowApply |
Allow /describe --apply to directly update PR description |
checksCommandEnabled |
Enable /checks command |
includeCiChecks |
Include CI check results in AI context |
askCommandEnabled |
Enable /ask command |
generateTestsCommandEnabled |
Enable /generate_tests command |
changelogCommandEnabled |
Enable /changelog command |
changelogAllowApply |
Allow /changelog --apply to commit directly to the repo |
feedbackCommandEnabled |
Enable /feedback command |
improveCommandEnabled |
Enable /improve command |
addDocCommandEnabled |
Enable /add_doc (/add-doc) command |
customPromptCommandEnabled |
Enable /custom_prompt command |
helpDocsCommandEnabled |
Enable /help_docs command |
analyzeCommandEnabled |
Enable /analyze command |
complianceCommandEnabled |
Enable /compliance command |
similarCodeCommandEnabled |
Enable /similar_code command |
autoApproveCommandEnabled |
Enable /auto_approve command (default off for safety) |
scanRepoDiscussionsCommandEnabled |
Enable /scan_repo_discussions command |
secretScanEnabled |
Scan diffs for potential secret leaks and post security warnings |
secretScanCustomPatterns |
Additional repository-specific secret regex patterns |
autoLabelEnabled |
Auto-add PR labels based on change content |
customRules |
Team-specific review rules (natural language); enforced in both reviews and Q&A |
Prometheus-format metrics are exposed at GET /metrics:
pr_agent_webhook_requests_total— webhook request count by platform & eventpr_agent_webhook_results_total— results by platform & outcome (ok/error)pr_agent_webhook_replay_total— replay execution outcomespr_agent_webhook_store_writes_total— persisted debug-event write countpr_agent_webhook_store_trim_total— debug-event store trim operationspr_agent_health_checks_total— health endpoint call countpr_agent_http_errors_total— global HTTP error countpr_agent_process_uptime_seconds— process uptimepr_agent_ai_requests_active— active AI request countpr_agent_ai_wait_queue_size— queued AI request countpr_agent_ai_shutdown_requested— AI shutdown flag (0/1)pr_agent_runtime_state_backend_info{backend=...}— selected runtime-state backend info gauge
| Endpoint | Purpose |
|---|---|
GET /health |
Liveness probe |
GET /health?deep=true |
Deep check (AI provider connectivity) |
GET /github/health |
GitHub webhook config validation |
GET /gitlab/health |
Legacy GitLab webhook config validation (unsupported in current beta) |
Webhook errors return structured JSON. Known webhook errors (e.g. BadWebhookRequestError, WebhookAuthError) include a descriptive message; unrecognized internal errors are sanitized to "internal server error" to prevent information leakage.
{
"ok": false,
"error": "webhook secret verification failed",
"type": "WebhookAuthError",
"status": 401,
"path": "/github/trigger",
"method": "POST",
"timestamp": "2026-01-01T00:00:00.000Z"
}Payloads exceeding WEBHOOK_BODY_LIMIT (default 1mb) return 413 Payload Too Large.
Enable temporarily for debugging:
WEBHOOK_EVENT_STORE_ENABLED=true
WEBHOOK_REPLAY_ENABLED=true
WEBHOOK_REPLAY_TOKEN=your-secret-tokenEndpoints (require x-pr-agent-replay-token header):
GET /webhook/events— list stored eventsPOST /github/replay/:eventId— replay a GitHub eventPOST /gitlab/replay/:eventId— replay a GitLab event
- HTTP Retry: exponential backoff (2 retries, 400ms base delay) on 408/429/5xx
- Deduplication: FNV hash with 5-min window (24h for merged events)
- Rate Limiting: per-scope limits to prevent API abuse
- Graceful Shutdown: drains in-flight requests on SIGTERM/SIGINT
# Backend tests
pnpm test:backend
# Backend coverage
pnpm --filter backend test:coverage
# Backend type check
pnpm --filter backend check
# Frontend production build check
pnpm --filter frontend buildBackend tests use the Node.js built-in test runner (node:test). Recommended regression coverage includes:
- AI concurrency control
- Cache & dedup behavior
- Diff parsing & hunk prioritization
- GitHub/GitLab integration logic
- HTTP retry & error handling
- Policy validation
- Notification adapters
- NestJS module wiring
Private — see repository for details.