Automated code review bot deployed as a GitHub App. Listens to pull request webhooks, analyzes diffs with static analysis tools and Claude AI, and posts inline comments directly on the PR.
- Automatic trigger — runs on every PR
openedorsynchronizeevent - Inline comments — posts comments on the exact lines with issues, not just a summary
- Static analysis
- Python: Ruff for linting and style issues
- JavaScript/TypeScript: ESLint for code quality checks
- Semantic analysis with Claude — detects logic bugs, unhandled edge cases, security issues (injection, exposed secrets, missing validation), and poorly designed async patterns
- Multi-language support —
.py,.js,.ts,.jsx,.tsx - HMAC signature verification — validates every webhook payload with
sha256=signature - Non-blocking pipeline — webhook responds immediately; analysis runs in background via
asyncio - Health endpoint —
GET /healthreturns{"status": "ok"}for uptime monitoring
GitHub PR event
│
▼
POST /webhook (FastAPI)
│
├── verify_signature (HMAC-SHA256)
├── parse_pr_event (filter opened/synchronize)
│
▼
run_review_pipeline (background task)
│
├── get_installation_token (JWT → GitHub API)
├── get_pr_files (list changed files)
├── extract_file_contexts (filter + parse diffs)
│
└── for each file:
├── run_static_analysis (ruff / eslint)
├── analyze_semantically (Claude Sonnet)
└── post_review (inline comments on PR)
| Layer | Technology |
|---|---|
| Web framework | FastAPI + Uvicorn |
| GitHub integration | PyGithub + custom JWT auth |
| AI analysis | Anthropic Claude Sonnet (claude-sonnet-4-6) |
| Static analysis (Python) | Ruff |
| Static analysis (JS/TS) | ESLint |
| HTTP client | httpx |
| Deploy | Railway (Docker) |
Go to GitHub → Settings → Developer settings → GitHub Apps → New GitHub App
Required permissions:
- Contents: Read-only
- Metadata: Read-only
- Pull requests: Read and write
Subscribe to events:
- Pull request
Set the Webhook URL to your server URL + /webhook.
GITHUB_APP_ID=your_app_id
GITHUB_APP_PRIVATE_KEY=-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----
GITHUB_WEBHOOK_SECRET=your_webhook_secret
ANTHROPIC_API_KEY=your_anthropic_keyFor local development, you can use
GITHUB_APP_PRIVATE_KEY_PATH=private-key.pemand place the.pemfile in the project root instead.
pip install -r requirements.txt
cp .env.example .env # fill in your values
uvicorn app.main:app --reload --port 8000To expose your local server to GitHub webhooks:
ngrok http 8000- Connect your GitHub repo in Railway → New Project
- Set the environment variables (use
GITHUB_APP_PRIVATE_KEYwith the full key content — Railway doesn't support.pemfiles) - Railway auto-deploys on every push to
main - Update the Webhook URL in your GitHub App settings to
https://your-app.up.railway.app/webhook
To get your private key as a single-line string for Railway:
awk 'NF {printf "%s\\n", $0}' private-key.pemGo to your GitHub App → Install App → select the repositories where you want automated reviews.
├── app/
│ ├── main.py # FastAPI app, webhook endpoint
│ ├── webhook_handler.py # Signature verification, event parsing
│ ├── pipeline.py # Review orchestration
│ ├── diff_parser.py # PR diff parsing, line mapping
│ ├── static_analyzer.py # Ruff + ESLint runners
│ └── semantic_analyzer.py # Claude AI analysis
├── config/
│ └── settings.py # Environment config, private key loader
├── Dockerfile
├── docker-compose.yml # Local Docker dev
└── requirements.txt
- A PR is opened or updated in a repo where the App is installed
- GitHub sends a
POST /webhookevent signed with HMAC-SHA256 - The bot verifies the signature, parses the event, and responds
200 OKimmediately - In the background, the bot fetches the list of changed files from the GitHub API
- For each supported file, it extracts the diff and runs static analysis
- The diff and static issues are sent to Claude, which identifies real bugs and security problems
- Comments are mapped to their exact line numbers in the diff
- The bot posts a single PR review with all inline comments via the GitHub API