asepharyana 055501aed1 feat(server): Bun/TypeScript re-implementation of PR review engine
Reimplements the pr_agent review pipeline (previously Python package) in
Bun/TypeScript, verified end-to-end against a real GitHub App + 9router:

- src/diff.ts: git patch processing (extend_patch, line-numbered hunks,
  token-budget generate_full_patch, generated/invalid file filters)
- src/review.ts: orchestrator (fetch PR/diff -> render prompts -> LLM ->
  YAML parse -> markdown render -> publish), fallback models
- src/github.ts: GitHub App auth via @octokit/auth-app (JWT -> installation
  token, custom authStrategy for Bun), PR/diff/languages/comments REST
- src/prompts.ts: verbatim pr_reviewer_prompts.toml port (nunjucks templates)
- src/yaml.ts: resilient load_yaml with 9 fallback fixers
- src/markdown.ts: convert_to_markdown_v2 port ('PR Reviewer Guide' comment)
- src/llm.ts: 9router OpenAI-compatible chat completions (non-stream,
  temp omitted for claude-opus-5), timeout 600s
- src/token.ts: js-tiktoken o200k counting
- src/index.ts: webhook server (HMAC verify, segment analytics, /health,
  /api/metrics) + cli.ts one-shot review
- e2e.ts: real-world harness (bun e2e --repo o/r --pr N [--publish])

Verified: bun test 13/13, tsc clean, E2E published a real review comment
(## PR Reviewer Guide) on asepharyana/nextjs-template#19 via GitHub App
MythEclipseBotReview + claude-opus-5 through 9router.

Python run_server.py + pr_agent remain for the live queue worker; server/
is the replacement path.
2026-09-21 12:50:10 +07:00
2026-08-25 18:13:08 +07:00

PR-Agent Server

Nix-deployed GitHub App server for automated PR review + auto-merge using a custom LLM endpoint (9router/Omniroute).

Architecture

GitHub webhook → Caddy (reverse proxy, :4002)
    → pr-agent-server (Nix profile, uvicorn on :4002)
        → PR-Agent github_app.py (FastAPI)
            → 9router API (custom OpenAI-compatible endpoint)

Project Layout

pr-agent-server/
├── src/                    # Main application modules
│   ├── run_server.py       # FastAPI server + analytics/metrics + Discord webhook
│   ├── auto_merge_bot.py   # Periodic PR review→approve→merge bot
│   ├── trivial_merge.py    # Trivial PR fast-path (docs/dependabot/tiny diffs)
│   ├── health-check.py     # Model health watchdog (tests primary + fallbacks)
│   ├── sync-key.py         # Auto-syncs BWS router key to disk on service start
│   ├── callback_server.py  # Dev callback server for GitHub App manifest
│   ├── start_server.py     # Legacy server start script
│   └── config/             # Runtime config (gitignored at deploy time)
├── scripts/                # Setup and deployment helpers
│   ├── setup_all.py        # Full setup: manifest + config + systemd service
│   ├── setup_app.py        # App-specific setup
│   ├── generate_manifest.py  # GitHub App manifest URL generator
│   └── generate_manifest_domain.py
├── templates/
│   └── manifest.json       # GitHub App manifest template
├── .github/workflows/
│   ├── deploy.yml          # CI: syntax → build → deploy → GC
│   ├── flakehub-publish-rolling.yaml
│   └── mirror-gitea.yml
├── flake.nix               # Nix build (creates venv + binary wrappers)
├── flake.lock              # Pinned Nix dependencies
├── .editorconfig           # Editor formatting rules
├── .gitignore
└── README.md

Development

Prerequisites

  • Nix (for builds)
  • Python 3.12+
  • GitHub App credentials (App ID, private key, webhook secret)
  • BWS (Bitwarden Secrets Manager) access token

Local testing

# Syntax check
python3 -m py_compile src/run_server.py src/auto_merge_bot.py src/health-check.py src/sync-key.py src/trivial_merge.py src/callback_server.py

# Nix build
nix build .#default

# Run server (after setting up secrets)
export BWS_ACCESS_TOKEN="<your-bws-token>"
nix run .#pr-agent-server-sync-key  # syncs the router key
nix run .#pr-agent-server            # starts uvicorn on :3000

# Health check
nix run .#pr-agent-server-health-check

Deployment

Deploy is fully automated via GitHub Actions on push to main:

# .github/workflows/deploy.yml
1. syntax-check  → python3 py_compile all modules
2. build-and-deploy → nix build → SSH to VPS → update profile → restart service
3. cleanup → nix-gc-vps.sh (with profile link repair)

Secrets required in GitHub Actions:

  • VPS_HOST — VPS IP address
  • VPS_USER — SSH user
  • SSH_PRIVATE_KEY — SSH private key for deploy user
  • GITEA_TOKEN — for Gitea mirror (if using mirror workflow)

Ops

  • Health watchdog: cron pr-agent-health-watchdog (every 10 min) → ~/.hermes/scripts/pr-agent-health-check.sh → Nix binary pr-agent-health-check
  • Key auto-sync: systemd ExecStartPre=/usr/local/bin/bws-exec pr-agent -- <profile>/bin/pr-agent-sync-key
  • Prometheus: GET /api/metrics → pr_agent_requests_total, pr_agent_model_failures
  • Analytics: GET /api/analytics → JSON summary (unwrap "record" field)
  • Discord: POST /api/v1/notify_review → pr-agent-ops webhook

Nix Profile Integrity

⚠️ See the devops/pr-agent-deployment skill for troubleshooting broken -link profile symlinks after nix store gc. The GC script (/usr/local/bin/nix-gc-vps.sh) now includes a repair step.

License

MIT — see LICENSE if present at deploy.

S
Description
PR-Agent GitHub App webhook server (auto-review + auto-merge)
Readme
456 KiB
Languages
TypeScript 53.1%
Python 46.9%