Production-quality AI-powered GitHub Pull Request reviewer. Automatically reviews PRs for bugs, security issues, performance problems, error handling gaps, and logic errors — then posts inline comments back to GitHub.
Built with Clean Architecture in a Turborepo monorepo so new review agents, RAG context, and multi-model support can be added without major refactoring.
- Overview
- Architecture
- System Workflow
- Repository Structure
- Module Responsibilities
- Tech Stack
- Prerequisites
- Getting Started
- Environment Variables
- GitHub App Setup
- Running Locally
- Testing
- Data Contracts
- Error Handling
- Project Status
- Roadmap
| Step | What happens |
|---|---|
| 1 | User installs the GitHub App on a repository |
| 2 | A pull request is opened or synchronized |
| 3 | GitHub sends a webhook to apps/github-app |
| 4 | The app verifies the webhook signature |
| 5 | A review job is pushed to Redis (review-queue) |
| 6 | apps/worker picks up the job |
| 7 | The worker downloads the PR diff via GitHub API |
| 8 | The diff is sent to OpenRouter (openai/gpt-5.3-codex) |
| 9 | The AI returns structured JSON |
| 10 | The worker posts inline review comments on the PR |
The webhook responds immediately. All heavy work runs asynchronously in the worker.
The project follows Clean Architecture. Dependencies always point inward. Business logic never imports Fastify, Redis, Octokit, Prisma, or the OpenAI/OpenRouter SDK directly.
┌─────────────────────────────────────────────────────────────┐
│ PRESENTATION LAYER │
│ │
│ apps/github-app apps/worker │
│ (Fastify webhook) (BullMQ consumer) │
│ │
│ Receives HTTP webhooks Processes review jobs │
│ Verifies signatures Orchestrates the review flow │
│ Enqueues jobs Posts results to GitHub │
└──────────────────────────┬──────────────────────────────────┘
│ depends on
▼
┌─────────────────────────────────────────────────────────────┐
│ APPLICATION LAYER │
│ │
│ Use cases, dependency injection, job handlers │
│ Wires domain services to infrastructure implementations │
└──────────────────────────┬──────────────────────────────────┘
│ depends on
▼
┌─────────────────────────────────────────────────────────────┐
│ DOMAIN LAYER │
│ │
│ packages/review-engine packages/parser │
│ │
│ ReviewEngine Diff parsing │
│ Prompt builder Patch → line-annotated hunks │
│ AI output validation │
│ │
│ No knowledge of HTTP, queues, GitHub, or AI SDKs │
└──────────────────────────┬──────────────────────────────────┘
│ depends on
▼
┌─────────────────────────────────────────────────────────────┐
│ INFRASTRUCTURE LAYER │
│ │
│ packages/github packages/ai │
│ │
│ GitHubService (Octokit) AIProvider (OpenRouter) │
│ Auth, PR fetch, reviewPullRequest() │
│ post review comments │
└──────────────────────────┬──────────────────────────────────┘
│ depends on
▼
┌─────────────────────────────────────────────────────────────┐
│ SHARED CORE │
│ │
│ packages/shared │
│ │
│ Domain types, errors, Zod schemas │
│ Innermost layer — no external dependencies │
└─────────────────────────────────────────────────────────────┘
| Layer | Can depend on | Cannot depend on |
|---|---|---|
Presentation (apps/*) |
Application, Domain, Infrastructure, Shared | — |
| Application | Domain, Infrastructure interfaces, Shared | Fastify internals in domain |
Domain (review-engine, parser) |
Shared only | GitHub, AI, Redis, Prisma, Fastify |
Infrastructure (github, ai) |
Shared, external SDKs | Apps, Fastify |
| Shared | Zod only | Everything else |
- Interfaces over implementations —
AIProvider,GitHubServiceare interfaces. Swapping OpenRouter for another model or mocking in tests requires no domain changes. - Validation at boundaries — Webhooks, queue payloads, and AI responses are validated with Zod before entering business logic.
- Async by default — Webhooks enqueue jobs and return
200fast. Reviews run in BullMQ workers. - Typed errors — Every external failure throws a
DomainErrorsubclass (GitHubApiError,AiProviderError, etc.), never a rawError.
Developer GitHub github-app Redis worker OpenRouter
│ │ │ │ │ │
│── open PR ───────────►│ │ │ │ │
│ │── webhook POST ───►│ │ │ │
│ │ │── verify sig │ │ │
│ │ │── enqueue job ──►│ │ │
│ │◄── 200 OK ─────────│ │ │ │
│ │ │ │── dequeue ────►│ │
│ │ │ │ │── get PR diff ────►│
│ │◄──────────────────────────────────────────────────────│ │
│ │ │ │ │── review request ─►│
│ │ │ │ │◄── JSON response ─│
│ │ │ │ │── validate output │
│ │◄── inline comments ──────────────────────────────────│ │
│◄── review on PR ──────│ │ │ │ │
ai-pr-reviewer/
├── apps/
│ ├── web/ # React + Tailwind marketing site
│ ├── github-app/ # Fastify server — webhook receiver
│ └── worker/ # BullMQ consumer — review processor
│
├── packages/
│ ├── shared/ # Domain types, errors, Zod schemas
│ ├── parser/ # Unified diff parser
│ ├── ai/ # AIProvider interface + OpenRouter client
│ ├── review-engine/ # Prompt builder, ReviewEngine
│ └── github/ # GitHubService (Octokit wrapper)
│
├── prisma/ # PostgreSQL schema and migrations
├── docker/ # Redis + PostgreSQL for local dev
├── .cursor/rules/ # AI coding standards
├── .env.example # Environment template
├── package.json # Root scripts
├── pnpm-workspace.yaml
├── turbo.json
└── tsconfig.base.json
| Responsibility | Detail |
|---|---|
| Receive webhooks | POST /webhooks/github |
| Verify signature | HMAC-SHA256 with GITHUB_WEBHOOK_SECRET |
| Parse payload | Validate pull_request events via Zod |
| Enqueue job | Push to review-queue in Redis |
| Respond fast | Return 200 without running AI review |
| Responsibility | Detail |
|---|---|
| Consume jobs | BullMQ worker on review-queue |
| Fetch PR data | Authenticate GitHub App, download changed files |
| Run review | Call ReviewEngine with parsed diff |
| Post results | Submit inline review comments to GitHub |
The only package allowed to use Octokit.
| Method | Purpose |
|---|---|
| Authenticate | Create installation access token |
| Get Pull Request | Fetch PR metadata |
| Get changed files | List files with patches |
| Download file contents | Read file content when needed |
| Post review comments | Submit PR review with inline comments |
The only package allowed to call OpenRouter/OpenAI SDK.
| Method | Purpose |
|---|---|
reviewPullRequest(input) |
Send prompt, return structured JSON |
Pure domain logic. No infrastructure imports.
| Responsibility | Detail |
|---|---|
| Build prompt | Staff-engineer review instructions |
| Call AI | Via AIProvider interface |
| Validate output | Zod schema validation |
| Return result | ReviewResult with summary + comments |
| Responsibility | Detail |
|---|---|
| Parse patches | Convert unified diff to line-annotated hunks |
| Map line numbers | Align AI comments to correct PR lines |
| Export | Purpose |
|---|---|
DomainError hierarchy |
Typed errors for all layers |
ReviewInput, ReviewResult |
Review workflow types |
reviewResponseSchema |
AI output validation |
reviewJobPayloadSchema |
Queue job validation |
parsePullRequestWebhook |
Webhook payload validation |
| Category | Technology |
|---|---|
| Language | TypeScript |
| Runtime | Node.js 22+ |
| Package manager | pnpm |
| Monorepo | Turborepo |
| API framework | Fastify |
| Database | PostgreSQL |
| ORM | Prisma |
| Queue | BullMQ |
| Cache / broker | Redis |
| GitHub API | Octokit |
| AI | OpenRouter (openai/gpt-5.3-codex) |
| Validation | Zod |
| Logging | Pino |
| Testing | Vitest |
| Linting | ESLint |
| Formatting | Prettier |
- Node.js 22 or higher
- pnpm 10+
- Docker (for Redis and PostgreSQL locally)
- A GitHub account with permission to create GitHub Apps
- An OpenRouter API key
git clone <your-repo-url>
cd ai-pr-reviewer
pnpm installcp .env.example .envEdit .env with your credentials (see Environment Variables).
pnpm build
pnpm testdocker compose -f docker/docker-compose.yml up -dpnpm dev| Variable | Required | Description |
|---|---|---|
GITHUB_APP_ID |
Yes | GitHub App numeric ID |
GITHUB_APP_PRIVATE_KEY |
Yes | PEM private key from GitHub App |
GITHUB_WEBHOOK_SECRET |
Yes | Secret used to verify webhook signatures |
OPENROUTER_API_KEY |
Yes | OpenRouter API key |
OPENROUTER_BASE_URL |
No | Default: https://openrouter.ai/api/v1 |
OPENROUTER_MODEL |
No | Default: openai/gpt-5.3-codex |
REDIS_URL |
Yes | Redis connection string for BullMQ |
DATABASE_URL |
Yes | PostgreSQL connection string |
PORT |
No | Webhook server port. Default: 3000 |
LOG_LEVEL |
No | Pino log level. Default: info |
Example .env:
GITHUB_APP_ID=123456
GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n..."
GITHUB_WEBHOOK_SECRET=your-strong-secret
OPENROUTER_API_KEY=sk-or-v1-...
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
OPENROUTER_MODEL=openai/gpt-5.3-codex
REDIS_URL=redis://localhost:6379
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ai_pr_reviewer
PORT=3000
LOG_LEVEL=infoNever commit .env. It is listed in .gitignore.
Go to Create a new GitHub App.
| Field | Value |
|---|---|
| GitHub App name | AI PR Reviewer (or your choice) |
| Homepage URL | Your app or repo URL |
| Webhook URL | https://your-domain.com/webhooks/github |
| Webhook secret | Strong random string → GITHUB_WEBHOOK_SECRET |
| Active | Checked |
Permissions:
| Permission | Access |
|---|---|
| Pull requests | Read & write |
| Contents | Read |
| Metadata | Read |
Subscribe to events:
- Pull request
After creation:
- Copy App ID →
GITHUB_APP_ID - Generate and download a private key →
GITHUB_APP_PRIVATE_KEY
The install option is on the GitHub App page, not in repository settings.
- Go to https://github.com/settings/apps
- Click your app name
- Click Install App in the left sidebar
- Select your account or organization
- Choose All repositories or Only select repositories
- Click Install
Direct link format (replace slug):
https://github.com/apps/your-app-slug
GitHub must reach your machine. Use a tunnel:
ngrok http 3000Set the ngrok URL as your GitHub App webhook:
https://abc123.ngrok-free.app/webhooks/github
pnpm dev:webOpens at http://localhost:5173
pnpm --filter @ai-pr-reviewer/web build
pnpm --filter @ai-pr-reviewer/web previewpnpm install
pnpm build
pnpm testCore review backend is implemented: webhook app, worker, GitHub integration, OpenRouter AI, and Redis queue.
pnpm docker:up
pnpm dev:api
pnpm dev:worker| Service | Port | Command |
|---|---|---|
| Marketing site | 5173 | pnpm dev:web |
| Webhook API | 3000 | pnpm dev:api |
| Worker | — | pnpm dev:worker |
| Redis | 6379 | pnpm docker:up |
| PostgreSQL | 5432 | pnpm docker:up |
Webhook URL for GitHub App:
https://your-tunnel-domain/webhooks/github
Health check (includes Redis connectivity):
GET http://localhost:3000/health
Example response:
{ "status": "ok", "redis": true }- Install the GitHub App on a test repository
- Open or update a pull request
- Check worker logs for job processing
- Inline comments should appear on the PR
pnpm testRun tests for a single package:
pnpm --filter @ai-pr-reviewer/shared test
pnpm --filter @ai-pr-reviewer/parser test
pnpm --filter @ai-pr-reviewer/review-engine test| Module | What is tested |
|---|---|
shared |
AI response validation, webhook parsing |
parser |
Unified diff parsing, line mapping |
review-engine |
Prompt builder, review orchestration |
ai |
Response parsing (OpenRouter mocked) |
{
"installationId": 12345,
"owner": "acme",
"repo": "my-repo",
"pullNumber": 42
}{
"summary": "Found two potential issues.",
"comments": [
{
"file": "src/auth.ts",
"line": 51,
"severity": "high",
"category": "bug",
"title": "Possible null dereference",
"comment": "user may be undefined before accessing profile."
}
]
}| Field | Values |
|---|---|
severity |
low, medium, high, critical |
category |
bug, security, performance, error-handling, logic |
Invalid AI responses are rejected by Zod validation before posting to GitHub.
The AI reviews for:
- Bugs
- Security issues
- Performance problems
- Error handling issues
- Logic issues
The AI ignores:
- Formatting
- Naming
- Style
- Personal preference
All external failures use typed domain errors from @ai-pr-reviewer/shared:
| Error | When thrown |
|---|---|
WebhookVerificationError |
Invalid webhook signature |
WebhookValidationError |
Malformed webhook payload |
QueueError |
Redis/BullMQ failure |
GitHubApiError |
GitHub API request failed |
AiProviderError |
OpenRouter request failed |
AiResponseValidationError |
AI returned invalid JSON |
DiffParseError |
Could not parse PR diff |
ReviewEngineError |
Unrecoverable review logic error |
Secrets are never logged.
| Component | Status |
|---|---|
| Monorepo scaffolding | Done |
@ai-pr-reviewer/web |
Done |
@ai-pr-reviewer/shared |
Done |
@ai-pr-reviewer/parser |
Done |
@ai-pr-reviewer/ai |
Done |
@ai-pr-reviewer/review-engine |
Done |
@ai-pr-reviewer/github |
Done |
@ai-pr-reviewer/queue |
Done |
apps/github-app |
Done |
apps/worker |
Done |
docker/ |
Done |
prisma/ |
Planned |
| Phase | Features |
|---|---|
| Phase 1 | Webhook → queue → AI review → inline comments |
| Phase 2 | Multiple review agents, configurable review rules |
| Phase 3 | Repository context retrieval (RAG) |
| Phase 4 | Multi-model support, per-repo model selection |
| Phase 5 | SaaS dashboard, billing, usage analytics |
| Command | Description |
|---|---|
pnpm install |
Install all dependencies |
pnpm build |
Build all packages |
pnpm dev |
Start apps in development mode |
pnpm test |
Run all tests |
pnpm lint |
Lint all packages |
pnpm typecheck |
Type-check all packages |
pnpm format |
Format code with Prettier |
Private — all rights reserved.