Daan Schouteden 6ebd04a60b Normalize Cursor verdict values and post PR comment on review failures.
Coerce common verdict/event variants before validation, skip retries on schema errors, and leave a failure comment plus reviewer removal for any failed run.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-03 14:02:57 +02:00
2026-06-02 11:39:41 +02:00
2026-06-02 11:39:41 +02:00
2026-06-02 11:39:41 +02:00
2026-06-02 11:39:41 +02:00
2026-06-02 11:39:41 +02:00
2026-06-02 11:39:41 +02:00

gitea-pr-review-bot

Central webhook service that reviews Gitea pull requests using the Cursor SDK and posts a full Gitea review (summary + inline comments).

This bot is designed to run once for many repositories in Bram/* instead of duplicating workflows in every repo.

What This Bot Does

  • Listens to Gitea webhook events.
  • Triggers on:
    • pull_request with action opened
    • pull_request or pull_request_review_request with action review_requested when requested reviewer is the bot user
  • Loads PR metadata and changed files from Gitea.
  • Builds a review prompt (including optional repo-specific rule files).
  • Calls Cursor Cloud Agent (Agent.prompt) for structured review output.
  • Validates and posts a single consolidated Gitea review.
  • Removes itself from requested reviewers after successful review.
  • Prevents duplicate processing by dedupe key:
    • {owner}/{repo}#{pr_number}#{head_sha}

High-Level Flow

  1. src/server.ts receives webhook payload.
  2. src/webhook/verify-signature.ts validates HMAC (WEBHOOK_SECRET).
  3. src/webhook/event-router.ts accepts or ignores event by type/action.
  4. src/run/review-runner.ts orchestrates full review run.
  5. src/config/load-repo-config.ts loads optional per-repo overrides.
  6. src/gitea/client.ts fetches PR + files and writes reviews.
  7. src/prompt/build-review-prompt.ts assembles review instructions/context.
  8. src/cursor/review-agent.ts invokes Cursor SDK in cloud mode.
  9. src/cursor/review-schema.ts validates structured JSON response.
  10. src/gitea/review-api.ts posts review and handles inline comment fallback.
  11. src/gitea/reviewer-api.ts removes bot reviewer from PR.

Architecture Diagram

flowchart TD
  A[Gitea Webhook] --> B[src/server.ts]
  B --> C[verify-signature.ts]
  C --> D[event-router.ts]
  D --> E[review-runner.ts]

  E --> F[dedupe-store.ts]
  E --> G[should-process-event.ts]
  E --> H[load-repo-config.ts]
  E --> I[gitea/client.ts]
  E --> J[build-review-prompt.ts]
  E --> K[review-agent.ts]
  K --> L[Cursor Cloud Agent.prompt]
  K --> M[review-schema.ts]

  E --> N[review-api.ts]
  N --> O[POST /pulls/{index}/reviews]
  N --> P[DELETE prior bot reviews]

  E --> Q[reviewer-api.ts]
  Q --> R[PATCH reviewers remove bot]

Project Structure

Server and Webhook Layer

  • src/server.ts
    • HTTP server, /healthz, /webhooks/gitea
    • Signature check + event routing + response codes
  • src/webhook/verify-signature.ts
    • SHA256 HMAC validation with timing-safe compare
  • src/webhook/event-router.ts
    • Converts raw webhook headers/payload into supported routed events

Domain Logic

  • src/domain/should-process-event.ts
    • Loop guard (skip bot-originated events)
    • Repo enable/disable check
    • Label skip logic
    • Base branch filtering
  • src/domain/dedupe-store.ts
    • In-memory TTL dedupe store for idempotency

Gitea Integration

  • src/gitea/client.ts
    • Typed wrapper around Gitea REST endpoints
    • Pull details/files/reviews, create/delete review, patch pull
    • Optional content-file loading from repository
  • src/gitea/review-api.ts
    • Delete prior bot reviews
    • Post one consolidated review
    • Validate inline comments against changed-file paths and positions
    • Summary-only fallback if inline set is invalid
  • src/gitea/reviewer-api.ts
    • Remove bot from requested reviewers list

Cursor Integration

  • src/cursor/review-agent.ts
    • Calls Cursor SDK Agent.prompt in cloud runtime
    • Enforces review timeout
    • Extracts text from SDK response formats
  • src/cursor/review-schema.ts
    • Zod schema for strict review JSON contract:
    • verdict, event, body, comments[]

Prompting and Config

  • src/prompt/build-review-prompt.ts
    • Builds complete PR review prompt from title/body/files/patches/rules
  • src/config/load-repo-config.ts
    • Reads .gitea/pr-review-bot.yml when present
    • Loads optional repo-local rule files:
      • AGENTS.md
      • docs/pr-review.md
      • .cursor/skills/pr-review/SKILL.md
      • .cursor/skills/pr-review/gitea.md

Orchestration and Utilities

  • src/run/review-runner.ts
    • Main end-to-end review execution
    • Dedupe, retry wrappers, posting, reviewer self-removal
  • src/run/retry.ts
    • Exponential backoff retry helper for transient failures
  • src/types/events.ts
    • Gitea webhook payload typings

Scripts and Deployment

  • scripts/dry-run.ts
    • Manual run by owner/repo/pr
  • Dockerfile
    • Production container image
  • docker-compose.yml
    • Local/hosted compose deployment
  • docs/setup.md
    • Installation and webhook setup
  • docs/operations.md
    • Runtime behavior, failure handling, rollback

Environment Variables

Required:

  • CURSOR_API_KEY
  • GITEA_TOKEN
  • GITEA_BASE_URL
  • GITEA_BOT_LOGIN
  • WEBHOOK_SECRET
  • PORT

Optional:

  • DEFAULT_BASE_BRANCH (default: main)
  • MAX_INLINE_COMMENTS (default: 5)
  • REVIEW_TIMEOUT_MS (default: 300000, 5 minutes)
  • DEDUPE_TTL_SECONDS (default: 1800)
  • LOG_LEVEL (default: info; options: debug, info, warn, error)

See .env.example.

Review Output Contract

The Cursor response must be strict JSON:

  • verdict: approve | request_changes | comment
  • event: APPROVE | REQUEST_CHANGES | COMMENT
  • body: string
  • comments: array of:
    • path (changed file path)
    • new_position (integer >= 1)
    • body (comment text)

Post-validation rules:

  • Invalid path/position in inline comments => post summary-only review.
  • Inline comments are clamped to configured maximum.

Getting Started

  1. Copy env template:
    • cp .env.example .env
  2. Fill secrets/tokens.
  3. Install:
    • npm install
  4. Run locally:
    • npm run dev
  5. Verify health:
    • curl http://localhost:8787/healthz

Useful Commands

  • Type check: npm run check
  • Build: npm run build
  • Start built app: npm run start
  • Dry run: npm run dry-run -- <owner> <repo> <pr_number> [head_sha]

Operational Notes

  • Keep bot credentials scoped to least privilege.
  • Do not log tokens or raw auth headers.
  • Dedupe is in-memory (resets on restart).
  • Best deployment model is a central service with org-level webhook and per-repo opt-out config.
  • Logs are structured JSON to simplify filtering in Docker/log collectors.
S
Description
AI agent that auto reviews new PRs and can be added as 'reviewer' to review again.
Readme 122 KiB
Languages
TypeScript 98.9%
Dockerfile 1.1%