diff --git a/.github/workflows/pr-review.yml b/.github/workflows/pr-review.yml index f7b1e5913..70df5b5ef 100644 --- a/.github/workflows/pr-review.yml +++ b/.github/workflows/pr-review.yml @@ -50,7 +50,7 @@ jobs: # signs the cascade's escalation comments, and is filtered out of the # candidate queue (it can't approve its own PRs). Human reviewers are # routed via CODEOWNERS at escalation time, not pinned via env. - BOT_USER: ${{ vars.BOT_USER || 'don-petry-bot' }} + BOT_USER: ${{ vars.BOT_USER || 'donpetry-bot' }} TARGET_ORG: ${{ vars.TARGET_ORG || 'petry-projects' }} AGENT_REPO: ${{ github.repository }} # Review engine: "claude" or "copilot". Controls which CLI and models are used. @@ -58,6 +58,8 @@ jobs: CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} # Copilot engine reuses GH_PAT (a separate user PAT with Copilot subscription). COPILOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }} + # Gemini engine auth — requires GOOGLE_API_KEY repo secret. + GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} # Default to dry-run. workflow_dispatch can override; repository_dispatch # (mention) always runs live; vars.LIVE_MODE=true switches scheduled runs live. DRY_RUN: ${{ github.event_name == 'repository_dispatch' && 'false' || inputs.dry_run || (vars.LIVE_MODE == 'true' && 'false' || 'true') }} @@ -107,13 +109,23 @@ jobs: case "$REVIEW_ENGINE" in claude) install_claude + # Best-effort install of fallbacks for duck/fallback support + npm install -g @google/gemini-cli || true if ! env GH_TOKEN="$COPILOT_GITHUB_TOKEN" gh copilot --version > /dev/null 2>&1; then echo "::warning::gh copilot built-in unavailable with user token — rate-limit fallback will be unavailable." fi ;; + gemini) + npm install -g @google/gemini-cli + install_claude || true + ;; copilot) - gh extension install github/gh-copilot + # Best-effort install of copilot if built-in is missing + if ! gh copilot --version > /dev/null 2>&1; then + gh extension install github/gh-copilot || true + fi install_claude || true + npm install -g @google/gemini-cli || true ;; *) echo "::error::Unknown REVIEW_ENGINE=$REVIEW_ENGINE" diff --git a/README.md b/README.md index 1ab9219ca..255364572 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,6 @@ Mention trigger: Comment `@petry-review-bot` on any org PR for immediate review. ## Documentation -- [Architecture & Capabilities](AGENT.md) -- [Setup Guide](SETUP.md) -- [Machine User Setup](MACHINE_USER_SETUP.md) +- [Architecture & Capabilities](docs/pr-review-agent/pr-review-agent.md) +- [Setup Guide](docs/pr-review-agent/setup.md) +- [Machine User Setup](docs/pr-review-agent/machine-user-setup.md) diff --git a/BOT_SETUP.md b/docs/pr-review-agent/bot-setup.md similarity index 100% rename from BOT_SETUP.md rename to docs/pr-review-agent/bot-setup.md diff --git a/DOCUMENTATION.md b/docs/pr-review-agent/documentation-index.md similarity index 54% rename from DOCUMENTATION.md rename to docs/pr-review-agent/documentation-index.md index 566041640..5ac1967eb 100644 --- a/DOCUMENTATION.md +++ b/docs/pr-review-agent/documentation-index.md @@ -3,20 +3,20 @@ This repository contains several documentation files describing the PR Review Agent system. Start here to understand which document you need. ## Quick Start -- **[SETUP.md](SETUP.md)** ⭐ Start here! +- **[setup.md](setup.md)** ⭐ Start here! - Quick reference for configuration and usage - Lists required secrets and variables - Shows how to run manually or check logs - Includes troubleshooting for common issues ## Understanding the System -- **[AGENT.md](AGENT.md)** — Full system documentation +- **[pr-review-agent.md](pr-review-agent.md)** — Full system documentation - Architecture and design philosophy - How PR reviews are performed - Agent capabilities and limitations - Configuration options -- **[IMPLEMENTATION.md](IMPLEMENTATION.md)** — Technical deep dive +- **[implementation.md](implementation.md)** — Technical deep dive - Authentication: machine user with PAT - How PR enumeration works - Review pipeline architecture @@ -25,7 +25,7 @@ This repository contains several documentation files describing the PR Review Ag - Stuck PR cleanup explained ## Setting Up Authentication -- **[MACHINE_USER_SETUP.md](MACHINE_USER_SETUP.md)** — Machine user and PAT setup +- **[machine-user-setup.md](machine-user-setup.md)** — Machine user and PAT setup - Create machine user account and org team - Configure CODEOWNERS for code owner approvals - Generate fine-grained PAT @@ -35,49 +35,49 @@ This repository contains several documentation files describing the PR Review Ag ``` .github/workflows/ -├── pr-review.yml # Main hourly review workflow -└── fix-stuck-prs.yml # Cleanup for stuck PRs + ├── pr-review.yml # Main hourly review workflow + └── fix-stuck-prs.yml # Cleanup for stuck PRs scripts/ -├── list-prs.sh # Find candidate PRs -├── review-one-pr.sh # Orchestrate review -├── post-pr-review.sh # Post approval and auto-merge -└── fix-stuck-prs.sh # Fix old stuck PRs + ├── list-prs.sh # Find candidate PRs + ├── review-one-pr.sh # Orchestrate review + ├── post-pr-review.sh # Post approval and auto-merge + └── fix-stuck-prs.sh # Fix old stuck PRs prompts/ -├── shared.md # Shared context -├── triage.md # Quick assessment -├── deep-review.md # Code analysis -├── security-audit.md # Vulnerability check -└── synthesize.md # Final verdict - -docs/ -├── SETUP.md # Quick start (read first) -├── AGENT.md # Full documentation -├── IMPLEMENTATION.md # Technical details -├── MACHINE_USER_SETUP.md # Machine user and PAT setup -└── DOCUMENTATION.md # This file + ├── shared.md # Shared context + ├── triage.md # Quick assessment + ├── deep-review.md # Code analysis + ├── security-audit.md # Vulnerability check + └── synthesize.md # Final verdict + +docs/pr-review-agent/ + ├── setup.md # Quick start (read first) + ├── pr-review-agent.md # Full documentation + ├── implementation.md # Technical details + ├── machine-user-setup.md # Machine user and PAT setup + └── documentation-index.md # This file ``` ## Common Tasks ### I want to understand what this agent does -→ Read [AGENT.md](AGENT.md) +→ Read [pr-review-agent.md](pr-review-agent.md) ### I need to set up the agent in a new organization -→ Follow [MACHINE_USER_SETUP.md](MACHINE_USER_SETUP.md) +→ Follow [machine-user-setup.md](machine-user-setup.md) ### The agent isn't working, help! -→ Check [SETUP.md#troubleshooting](SETUP.md#troubleshooting) +→ Check [setup.md#troubleshooting](setup.md#troubleshooting) ### I want to understand the architecture -→ Read [IMPLEMENTATION.md](IMPLEMENTATION.md) +→ Read [implementation.md](implementation.md) ### I need to update configuration -→ See [SETUP.md#repository-variables](SETUP.md#repository-variables) +→ See [setup.md#repository-variables](setup.md#repository-variables) ### I want to run a manual review -→ See [SETUP.md#running-manually](SETUP.md#running-manually) +→ See [setup.md#running-manually](setup.md#running-manually) ## Authentication Method @@ -97,9 +97,9 @@ All secrets must be set in the repository (`Settings → Secrets and variables | Secret | Purpose | |--------|---------| -| `CLAUDE_CODE_OAUTH_TOKEN` | Claude Code API access | -| `DON_PETRY_BOT_PETRY_PROJECT_PAT` | Machine user fine-grained PAT | -| `COPILOT_GITHUB_TOKEN` | Optional fallback engine | +| `CLAUDE_CODE_OAUTH_TOKEN` | Claude Code authentication | +| `DON_PETRY_BOT_GH_PAT` | Machine user fine-grained PAT | +| `GH_PAT` | User PAT with Copilot subscription | ## Review Workflow @@ -121,17 +121,17 @@ All secrets must be set in the repository (`Settings → Secrets and variables ## Support and Troubleshooting -- **Workflow failing to authenticate**: Check [SETUP.md#troubleshooting](SETUP.md#troubleshooting) -- **Questions about design**: See [IMPLEMENTATION.md](IMPLEMENTATION.md) -- **Setup instructions**: Follow [MACHINE_USER_SETUP.md](MACHINE_USER_SETUP.md) -- **Agent capabilities**: Read [AGENT.md](AGENT.md) +- **Workflow failing to authenticate**: Check [setup.md#troubleshooting](setup.md#troubleshooting) +- **Questions about design**: See [implementation.md](implementation.md) +- **Setup instructions**: Follow [machine-user-setup.md](machine-user-setup.md) +- **Agent capabilities**: Read [pr-review-agent.md](pr-review-agent.md) ## Document Maintenance These documents are kept in sync with the actual implementation. When updating the system: 1. Update the relevant scripts/workflows 2. Update the corresponding documentation -3. Ensure SETUP.md reflects current state -4. Update IMPLEMENTATION.md if architecture changes +3. Ensure setup.md reflects current state +4. Update implementation.md if architecture changes -Last updated: April 26, 2026 +Last updated: May 11, 2026 diff --git a/PR_REVIEW_FAILURE_INVESTIGATION.md b/docs/pr-review-agent/failure-investigation.md similarity index 100% rename from PR_REVIEW_FAILURE_INVESTIGATION.md rename to docs/pr-review-agent/failure-investigation.md diff --git a/PR_REVIEW_AGENT_FAILURE_REPORT.md b/docs/pr-review-agent/failure-report.md similarity index 100% rename from PR_REVIEW_AGENT_FAILURE_REPORT.md rename to docs/pr-review-agent/failure-report.md diff --git a/IMPLEMENTATION.md b/docs/pr-review-agent/implementation.md similarity index 86% rename from IMPLEMENTATION.md rename to docs/pr-review-agent/implementation.md index 738d7d8f2..58800f469 100644 --- a/IMPLEMENTATION.md +++ b/docs/pr-review-agent/implementation.md @@ -20,13 +20,13 @@ This document describes the current implementation of the PR Review Agent and ke ### Token Usage -All workflows use the `DON_PETRY_BOT_PETRY_PROJECT_PAT` org secret directly: +All workflows use the `DON_PETRY_BOT_GH_PAT` org secret directly: ```yaml env: - GH_TOKEN: ${{ secrets.DON_PETRY_BOT_PETRY_PROJECT_PAT }} + GH_TOKEN: ${{ secrets.DON_PETRY_BOT_GH_PAT }} ``` -The PAT is a fine-grained token scoped to the `petry-projects` organization with 90-day expiry. See [MACHINE_USER_SETUP.md](MACHINE_USER_SETUP.md) for rotation instructions. +The PAT is a fine-grained token scoped to the `petry-projects` organization with 90-day expiry. See [MACHINE_USER_SETUP.md](machine-user-setup.md) for rotation instructions. ## PR Enumeration @@ -162,17 +162,22 @@ This script runs with machine user PAT credentials that can approve PRs across t ## Rate Limiting and Fallback -The `pr-review.yml` workflow includes fallback logic: +The `scripts/review-batch.sh` script includes fallback logic: ```bash +# Fallback chain: claude -> gemini -> copilot if [ "$rc" -eq 2 ] && [ "${REVIEW_ENGINE:-claude}" = "claude" ]; then - echo "Claude rate limit hit — switching to Copilot engine for remaining PRs" + export REVIEW_ENGINE=gemini + # ... retry this PR with gemini ... +fi + +if [ "$rc" -eq 2 ] && [ "${REVIEW_ENGINE}" = "gemini" ]; then export REVIEW_ENGINE=copilot - bash scripts/review-one-pr.sh "$pr_url" || rc=$? + # ... retry this PR with copilot ... fi ``` -If Claude hits rate limits, the workflow switches to GitHub Copilot for remaining PRs in the batch. This ensures reviews continue even under high load. +If Claude hits rate limits, the batch switches to Gemini, and then to GitHub Copilot if needed. This ensures reviews continue even under high load. ## Metrics and Monitoring @@ -180,7 +185,7 @@ Key metrics tracked in workflow logs: - **Reviews posted:** Number of PRs actually approved - **No-ops skipped:** PRs already reviewed, not re-reviewed - **Failures:** PRs that had errors during review -- **Engine fallbacks:** Times Claude rate limit triggered Copilot fallback +- **Engine fallbacks**: Cumulative count and specific engines used (e.g., "gemini, copilot") View recent runs: ```bash @@ -198,7 +203,7 @@ gh run view --repo don-petry/pr-review-agent --log | Variable | Default | Purpose | |----------|---------|---------| -| `REVIEW_ENGINE` | `claude` | Primary review engine | +| `REVIEW_ENGINE` | `claude` | Primary review engine: `claude`, `gemini`, or `copilot` | | `DRY_RUN` | `true` | If true, don't post reviews | | `MAX_PRS` | `10` | Max reviews per run | | `CANDIDATE_LIMIT` | `100` | Max candidates scanned | @@ -209,9 +214,10 @@ gh run view --repo don-petry/pr-review-agent --log | Secret | Purpose | |--------|---------| +| `DON_PETRY_BOT_GH_PAT` | Machine user PAT for GitHub API access (BOT_USER) | +| `GH_PAT` | User PAT with Copilot subscription (fallback source) | | `CLAUDE_CODE_OAUTH_TOKEN` | Claude Code authentication | -| `DON_PETRY_BOT_PETRY_PROJECT_PAT` | Machine user PAT for GitHub API access | -| `COPILOT_GITHUB_TOKEN` | GitHub Copilot fallback token | +| `GOOGLE_API_KEY` | Gemini API authentication | ## File Structure @@ -234,9 +240,9 @@ gh run view --repo don-petry/pr-review-agent --log │ ├── synthesize.md # Combine findings, make decision │ └── cascade-action.md # Review coordination (legacy) │ -├── SETUP.md # Quick start guide -├── MACHINE_USER_SETUP.md # Machine user and PAT setup -├── IMPLEMENTATION.md # This file +├── setup.md # Quick start guide +├── machine-user-setup.md # Machine user and PAT setup +├── implementation.md # This file └── README.md # Overview ``` diff --git a/MACHINE_USER_SETUP.md b/docs/pr-review-agent/machine-user-setup.md similarity index 77% rename from MACHINE_USER_SETUP.md rename to docs/pr-review-agent/machine-user-setup.md index a759c1bdd..71483a460 100644 --- a/MACHINE_USER_SETUP.md +++ b/docs/pr-review-agent/machine-user-setup.md @@ -1,6 +1,6 @@ # Machine User Setup for PR Review Agent -This project authenticates via a **machine user account** with a fine-grained PAT. This replaces the previous GitHub App approach, which could not satisfy CODEOWNERS approval requirements (see [issue #27](https://github.com/don-petry/pr-review-agent/issues/27)). +This project authenticates via a **machine user account** with a fine-grained PAT. This replaces the previous GitHub App approach, which could not satisfy CODEOWNERS approval requirements (see [issue #27](https://github.com/petry-projects/.github-private/issues/27)). ## Why Machine User? @@ -11,7 +11,7 @@ This project authenticates via a **machine user account** with a fine-grained PA ## Step 1: Create the Machine User Account -1. Create a new GitHub account (e.g., `petry-pr-bot`) with a shared org email alias +1. Create a new GitHub account (e.g., `donpetry-bot`) with a shared org email alias 2. Add the account to the `petry-projects` organization 3. Create an org team (e.g., `petry-projects/pr-reviewers`) and add the machine user @@ -45,31 +45,31 @@ Or use path-specific rules as needed. ## Step 4: Store the PAT as an Org Secret ```bash -gh secret set DON_PETRY_BOT_PETRY_PROJECT_PAT --org petry-projects --body "" +gh secret set DON_PETRY_BOT_GH_PAT --org petry-projects --body "" ``` Or store at repo level if preferred: ```bash -gh secret set DON_PETRY_BOT_PETRY_PROJECT_PAT --repo don-petry/pr-review-agent --body "" +gh secret set DON_PETRY_BOT_GH_PAT --repo petry-projects/.github-private --body "" ``` ### Verify the secret is set: ```bash -gh secret list --repo don-petry/pr-review-agent +gh secret list --repo petry-projects/.github-private ``` -Should show `DON_PETRY_BOT_PETRY_PROJECT_PAT` in the list. +Should show `DON_PETRY_BOT_GH_PAT` in the list. ## Step 5: Test the Setup ```bash # Test dry-run -gh workflow run fix-stuck-prs.yml --repo don-petry/pr-review-agent -f dry_run=true +gh workflow run fix-stuck-prs.yml --repo petry-projects/.github-private -f dry_run=true # Check the logs -gh run view --repo don-petry/pr-review-agent --log | grep "Logged in" +gh run view --repo petry-projects/.github-private --log | grep "Logged in" ``` Should show the machine user account name. @@ -79,9 +79,9 @@ Should show the machine user account name. Once the migration is validated: ```bash -gh secret delete APP_ID --repo don-petry/pr-review-agent -gh secret delete APP_INSTALLATION_ID --repo don-petry/pr-review-agent -gh secret delete APP_PRIVATE_KEY --repo don-petry/pr-review-agent +gh secret delete APP_ID --repo petry-projects/.github-private +gh secret delete APP_INSTALLATION_ID --repo petry-projects/.github-private +gh secret delete APP_PRIVATE_KEY --repo petry-projects/.github-private ``` Optionally uninstall/delete the GitHub App from [org settings](https://github.com/organizations/petry-projects/settings/apps). @@ -92,7 +92,7 @@ Fine-grained PATs have a configurable expiry (90-day recommended). To rotate: 1. Sign in as the machine user 2. Generate a new fine-grained PAT with the same scopes -3. Update the secret: `gh secret set DON_PETRY_BOT_PETRY_PROJECT_PAT --repo don-petry/pr-review-agent --body ""` +3. Update the secret: `gh secret set DON_PETRY_BOT_GH_PAT --repo petry-projects/.github-private --body ""` 4. Revoke the old token ## Trade-offs vs GitHub App @@ -110,7 +110,7 @@ Fine-grained PATs have a configurable expiry (90-day recommended). To rotate: ### Issue: "Resource not accessible by integration" - Verify the PAT has the required scopes - Check the machine user has access to the target repos -- Ensure `DON_PETRY_BOT_PETRY_PROJECT_PAT` secret is set correctly +- Ensure `DON_PETRY_BOT_GH_PAT` secret is set correctly ### Issue: Approval doesn't satisfy code owner requirement - Verify the machine user is in the team listed in CODEOWNERS @@ -118,10 +118,10 @@ Fine-grained PATs have a configurable expiry (90-day recommended). To rotate: - Ensure branch protection has `require_code_owner_review: true` ### Issue: Token expired -- Generate a new fine-grained PAT and update the `DON_PETRY_BOT_PETRY_PROJECT_PAT` secret +- Generate a new fine-grained PAT and update the `DON_PETRY_BOT_GH_PAT` secret ## References - [GitHub: Managing CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners) - [GitHub: Fine-grained PATs](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#fine-grained-personal-access-tokens) -- [Issue #27: Why GitHub Apps can't be in CODEOWNERS](https://github.com/don-petry/pr-review-agent/issues/27) +- [Issue #27: Why GitHub Apps can't be in CODEOWNERS](https://github.com/petry-projects/.github-private/issues/27) diff --git a/AGENT.md b/docs/pr-review-agent/pr-review-agent.md similarity index 75% rename from AGENT.md rename to docs/pr-review-agent/pr-review-agent.md index 8ee9f0d6c..59a4a997c 100644 --- a/AGENT.md +++ b/docs/pr-review-agent/pr-review-agent.md @@ -4,19 +4,19 @@ A scheduled GitHub Action that reviews open PRs on don-petry's behalf. Runs hourly, classifies risk, auto-approves low/medium-risk PRs that pass all quality gates, and escalates high-risk or gated PRs for human review. -Supports two LLM engines via the `REVIEW_ENGINE` repo variable: **Claude** (default) +Supports three LLM engines via the `REVIEW_ENGINE` repo variable: **Claude** (default), **Gemini**, and **Copilot**. ## How it works 1. **Cron** — `.github/workflows/pr-review.yml` runs at `:07` every hour (and on `workflow_dispatch`). -1a. **@mention** — comment `@petry-review-bot` on any PR to trigger an immediate +1a. **@mention** — comment `@donpetry-bot` on any PR to trigger an immediate review, bypassing the hourly schedule. See [Mention-triggered reviews](#mention-triggered-reviews). 2. **Enumerate** — `scripts/list-prs.sh` queries GitHub for open PRs across every repo the PAT can see (the bot's personal account plus `TARGET_ORG`), **excluding PRs authored by `BOT_USER`** (the workflow's authenticated - identity, default `don-petry-bot`). GitHub's GraphQL API rejects + identity, default `donpetry-bot`). GitHub's GraphQL API rejects self-approval unconditionally, so self-authored PRs are unreviewable and would otherwise starve the queue. Output: one URL per line. 3. **Per-PR review** — `scripts/review-one-pr.sh` runs a cascading review @@ -46,8 +46,12 @@ and **Copilot**. model tiers. Different model families have different blind spots — running both catches issues that either alone would miss. - The rubber duck is **always the opposite engine**: if `REVIEW_ENGINE=claude`, - the duck is Copilot (GPT-5.4), and vice versa. No extra configuration needed. + The rubber duck is **always a diverse engine**: + - If `REVIEW_ENGINE=claude`, the duck is Copilot (GPT-5.4). + - If `REVIEW_ENGINE=gemini`, the duck is Claude (Sonnet 4.6). + - If `REVIEW_ENGINE=copilot`, the duck is Claude (Sonnet 4.6). + + No extra configuration needed. **Graceful degradation:** if the rubber duck fails (missing credentials, CLI not installed, timeout), the cascade continues with the primary deep @@ -55,14 +59,14 @@ and **Copilot**. ### Engine model mapping - | Tier | Claude primary | Copilot primary | - |---|---|---| - | Triage | Haiku 4.5 | GPT-5-mini | - | Deep review | Sonnet 4.6 | GPT-5.2 | - | Rubber duck | GPT-5.4 (cross) | Sonnet 4.6 (cross) | - | Synthesis | Sonnet 4.6 | GPT-5.2 | - | Security audit | Opus 4.6 | GPT-5.4 | - | Action / single review | Sonnet 4.6 / Opus 4.6 | GPT-5.2 / GPT-5.4 | + | Tier | Claude primary | Gemini primary | Copilot primary | + |---|---|---|---| + | Triage | Haiku 4.5 | Gemini 2.0 Flash | GPT-5-mini | + | Deep review | Sonnet 4.6 | Gemini 1.5 Pro | GPT-5.2 | + | Rubber duck | GPT-5.4 (cross) | Sonnet 4.6 (cross) | Sonnet 4.6 (cross) | + | Synthesis | Sonnet 4.6 | Gemini 1.5 Pro | GPT-5.2 | + | Security audit | Opus 4.6 | Gemini 1.5 Pro | GPT-5.4 | + | Action / single review | Sonnet 4.6 / Opus 4.6 | Gemini 1.5 Pro | GPT-5.2 / GPT-5.4 | **Cost profile:** - ~80% of PRs: triage + single confirm (2 calls, ~30s) @@ -114,19 +118,19 @@ same account both opens PRs (via Claude automation) and tries to approve them, every approval will silently fail. The recommended pattern: create a dedicated **reviewer bot account** -(e.g. `petry-review-bot`) whose token is stored as `GH_PAT`. PRs are +(e.g. `donpetry-bot`) whose token is stored as `GH_PAT`. PRs are authored by `don-petry`; the bot approves them. ### 1. Create the reviewer bot account 1. Sign out of GitHub (or use a private browser window). 2. Go to and create a new account. - - **Username:** e.g. `petry-review-bot` - - **Email:** a dedicated alias works well (e.g. `you+petry-review-bot@gmail.com`) + - **Username:** e.g. `donpetry-bot` + - **Email:** a dedicated alias works well (e.g. `you+donpetry-bot@gmail.com`) 3. Verify the email address. 4. Sign back in as `don-petry`. 5. Go to **github.com/organizations/petry-projects/settings/members** → - **Invite member** → enter `petry-review-bot` → Role: **Member**. + **Invite member** → enter `donpetry-bot` → Role: **Member**. 6. Accept the invite from the bot account. ### 2. Create a classic PAT for the bot @@ -134,7 +138,7 @@ authored by `don-petry`; the bot approves them. A classic PAT is required — fine-grained PATs cannot satisfy the GitHub rulesets bypass that org-admin approval requires. -1. Sign in as `petry-review-bot`. +1. Sign in as `donpetry-bot`. 2. Go to **Settings → Developer settings → Personal access tokens → Tokens (classic)** → **Generate new token (classic)**. 3. Settings: @@ -145,10 +149,10 @@ rulesets bypass that org-admin approval requires. 5. Sign back in as `don-petry` and store the token: ``` -gh secret set GH_PAT --repo don-petry/pr-review-agent +gh secret set GH_PAT --repo petry-projects/.github-private ``` -> **Branch protection / rulesets:** add `petry-review-bot` as an allowed +> **Branch protection / rulesets:** add `donpetry-bot` as an allowed > approver on each protected repo. In the repo ruleset or branch protection > settings, ensure the bot is not excluded from the reviewer pool. @@ -165,7 +169,15 @@ claude setup-token Store as a repo secret: ``` -gh secret set CLAUDE_CODE_OAUTH_TOKEN --repo don-petry/pr-review-agent +gh secret set CLAUDE_CODE_OAUTH_TOKEN --repo petry-projects/.github-private +``` + +#### Gemini engine + +Requires a Google API Key. Store as a repo secret: + +``` +gh secret set GOOGLE_API_KEY --repo petry-projects/.github-private ``` #### Copilot engine @@ -173,30 +185,33 @@ gh secret set CLAUDE_CODE_OAUTH_TOKEN --repo don-petry/pr-review-agent Create a GitHub PAT with Copilot scope. Store as a repo secret: ``` -gh secret set COPILOT_GITHUB_TOKEN --repo don-petry/pr-review-agent +gh secret set GH_PAT --repo petry-projects/.github-private ``` ### 4. Choose your engine ``` +# Use Gemini: +gh variable set REVIEW_ENGINE --body gemini --repo petry-projects/.github-private + # Use Copilot (GPT models): -gh variable set REVIEW_ENGINE --body copilot --repo don-petry/pr-review-agent +gh variable set REVIEW_ENGINE --body copilot --repo petry-projects/.github-private # Use Claude (default — no variable needed, or set explicitly): -gh variable set REVIEW_ENGINE --body claude --repo don-petry/pr-review-agent +gh variable set REVIEW_ENGINE --body claude --repo petry-projects/.github-private ``` ### 5. Test with a dry run ``` -gh workflow run pr-review.yml --repo don-petry/pr-review-agent -f dry_run=true -gh run watch --repo don-petry/pr-review-agent +gh workflow run pr-review.yml --repo petry-projects/.github-private -f dry_run=true +gh run watch --repo petry-projects/.github-private ``` To review a single PR ad-hoc: ``` -gh workflow run pr-review.yml --repo don-petry/pr-review-agent \ +gh workflow run pr-review.yml --repo petry-projects/.github-private \ -f pr_url=https://github.com/owner/repo/pull/123 \ -f dry_run=true ``` @@ -208,24 +223,24 @@ but never posts reviews, comments, labels, or reviewer requests. To enable live mode: ``` -gh variable set LIVE_MODE --body true --repo don-petry/pr-review-agent +gh variable set LIVE_MODE --body true --repo petry-projects/.github-private ``` To go back to dry-run: ``` -gh variable delete LIVE_MODE --repo don-petry/pr-review-agent +gh variable delete LIVE_MODE --repo petry-projects/.github-private ``` A specific run can always be forced either way via the `dry_run` workflow input: ``` -gh workflow run pr-review.yml --repo don-petry/pr-review-agent -f dry_run=false +gh workflow run pr-review.yml --repo petry-projects/.github-private -f dry_run=false ``` ## Tuning -- **Review engine** — `REVIEW_ENGINE` repo variable: `claude` (default) or +- **Review engine** — `REVIEW_ENGINE` repo variable: `claude` (default), `gemini`, or `copilot`. Controls which CLI and model family is used. - **Risk rules** — edit `prompts/shared.md` (taxonomy), or the per-tier prompts (`prompts/deep-review.md`, `prompts/security-audit.md`). @@ -234,31 +249,31 @@ gh workflow run pr-review.yml --repo don-petry/pr-review-agent -f dry_run=false PRs from a specific org, or to exclude certain repos). - **AI delegation** — set `DELEGATION_ORGS` to a comma-separated list of GitHub orgs where AI-assisted fix delegation is enabled: - `gh variable set DELEGATION_ORGS --body "petry-projects,don-petry" --repo don-petry/pr-review-agent` + `gh variable set DELEGATION_ORGS --body "petry-projects,don-petry" --repo petry-projects/.github-private` - **Max review cycles** — how many times the agent delegates to AI before escalating to human (default 3): - `gh variable set MAX_REVIEW_CYCLES --body 5 --repo don-petry/pr-review-agent` + `gh variable set MAX_REVIEW_CYCLES --body 5 --repo petry-projects/.github-private` - **Models** — change model IDs in `scripts/engine.sh`. The cascade tiers map to: triage → deep → audit → action. - **Max PRs per run** — defaults to 10 per cron tick to stay within the 60-min job timeout. Override: - `gh variable set MAX_PRS --body 15 --repo don-petry/pr-review-agent` + `gh variable set MAX_PRS --body 15 --repo petry-projects/.github-private` ## Mention-triggered reviews -Comment `@petry-review-bot` on any PR to start an immediate, on-demand review +Comment `@donpetry-bot` on any PR to start an immediate, on-demand review without waiting for the next scheduled run. The bot posts an acknowledgement within seconds and the full cascade result appears in a few minutes. **How it flows:** ``` -PR comment "@petry-review-bot please review" +PR comment "@donpetry-bot please review" → petry-projects/.github: pr-review-mention.yml (listens org-wide) → validates commenter trust (OWNER/MEMBER/COLLABORATOR only) → posts ack comment "I'm on it..." → gh workflow run pr-review.yml --field pr_url= --field force_review=true - → don-petry/pr-review-agent: pr-review.yml (per-PR concurrency slot) + → petry-projects/.github-private: pr-review.yml (per-PR concurrency slot) → scripts/review-one-pr.sh (FORCE_REVIEW=true bypasses idempotency) → cascade as normal → posts review ``` @@ -275,19 +290,19 @@ PR comment "@petry-review-bot please review" 1. Copy [`templates/mention-listener.yml`](templates/mention-listener.yml) to `petry-projects/.github` as `.github/workflows/pr-review-mention.yml`. -2. Add the `DON_PETRY_BOT_PETRY_PROJECT_PAT` secret to `petry-projects/.github` +2. Add the `DON_PETRY_BOT_GH_PAT` secret to `petry-projects/.github` (org-level secret or repo secret on `.github`). The PAT needs: - **Pull requests: write** — to post the ack comment across petry-projects repos - - **Contents: write** (scoped to `don-petry/pr-review-agent`) — to send the + - **Contents: write** (scoped to `petry-projects/.github-private`) — to send the `repository_dispatch` event (does **not** require `Actions: write`) -3. Ensure `petry-review-bot` has at least **Read** collaborator access on - `don-petry/pr-review-agent`. +3. Ensure `donpetry-bot` has at least **Read** collaborator access on + `petry-projects/.github-private`. ## Architecture ``` -scripts/engine.sh ← LLM abstraction (claude/copilot dispatch) +scripts/engine.sh ← LLM abstraction (claude/gemini/copilot dispatch) scripts/review-one-pr.sh ← Cascade orchestrator (sources engine.sh) scripts/list-prs.sh ← PR enumeration @@ -302,8 +317,9 @@ prompts/shared.md ← Shared risk taxonomy and decision gates ## Cost Uses the configured engine's billing: -- **Claude:** Max plan via OAuth token — no per-token API billing. -- **Copilot:** Included in GitHub Copilot subscription. +- **Claude**: Max plan via OAuth token — no per-token API billing. +- **Gemini**: API-based billing via `GOOGLE_API_KEY`. +- **Copilot**: Included in GitHub Copilot subscription. GitHub Actions cost is ~720 runs/month (hourly × 30 days). Runs with zero candidate PRs finish in ~10s. Each PR reviewed costs ~2-5 min of runner time diff --git a/SETUP.md b/docs/pr-review-agent/setup.md similarity index 77% rename from SETUP.md rename to docs/pr-review-agent/setup.md index c6d85f053..1899d9c08 100644 --- a/SETUP.md +++ b/docs/pr-review-agent/setup.md @@ -1,12 +1,13 @@ # PR Review Agent Setup -This repository automates PR reviews for the `petry-projects` organization using Claude Code or GitHub Copilot. +This repository automates PR reviews for the `petry-projects` organization using +Claude Code, Google Gemini, or GitHub Copilot. ## Quick Start ### Prerequisites - GitHub organization: `petry-projects` -- Machine user account (e.g., `petry-pr-bot`) added to org team in CODEOWNERS +- Machine user account (e.g., `donpetry-bot`) added to org team in CODEOWNERS - Secrets configured in the repository ### Repository Secrets Required @@ -15,15 +16,16 @@ Store these in the repository settings (`Settings → Secrets and variables → | Secret | Description | Source | |--------|-------------|--------| +| `DON_PETRY_BOT_GH_PAT` | Machine user PAT for GitHub API access | Generated from machine user account settings | +| `GH_PAT` | User PAT with Copilot subscription (for fallback) | GitHub PAT with Copilot scope | | `CLAUDE_CODE_OAUTH_TOKEN` | Claude Code authentication token | `claude setup-token` | -| `DON_PETRY_BOT_PETRY_PROJECT_PAT` | Machine user fine-grained PAT | Generated from machine user account settings | -| `COPILOT_GITHUB_TOKEN` | GitHub Copilot token (optional, for fallback) | GitHub PAT with Copilot scope | +| `GOOGLE_API_KEY` | Gemini API authentication token | Google AI Studio / Vertex AI | ### Repository Variables (Optional) | Variable | Default | Purpose | |----------|---------|---------| -| `REVIEW_ENGINE` | `claude` | Primary review engine: `claude` or `copilot` | +| `REVIEW_ENGINE` | `claude` | Primary review engine: `claude`, `gemini`, or `copilot` | | `LIVE_MODE` | `false` | If `true`, reviews are posted live; if `false`, dry-run only | | `DELEGATION_ORGS` | Empty | Comma-separated orgs where AI can auto-fix: `petry-projects,don-petry` | | `MAX_REVIEW_CYCLES` | `3` | Max review iterations before escalating to human | @@ -60,14 +62,14 @@ When the agent approves a PR: ### Dry-run (test without posting) ```bash gh workflow run pr-review.yml \ - --repo don-petry/pr-review-agent \ + --repo petry-projects/.github-private \ -f dry_run=true ``` ### Review a specific PR ```bash gh workflow run pr-review.yml \ - --repo don-petry/pr-review-agent \ + --repo petry-projects/.github-private \ -f pr_url=https://github.com/petry-projects/ContentTwin/pull/123 \ -f dry_run=false ``` @@ -76,7 +78,7 @@ gh workflow run pr-review.yml \ If PRs have agent comments but no approval reviews (from an older bug), use: ```bash gh workflow run fix-stuck-prs.yml \ - --repo don-petry/pr-review-agent \ + --repo petry-projects/.github-private \ -f dry_run=true ``` @@ -88,13 +90,13 @@ The `pr-review.yml` workflow runs hourly at `:07` to avoid the top-of-hour cron To check recent runs: ```bash -gh run list --repo don-petry/pr-review-agent -w pr-review.yml -L 5 +gh run list --repo petry-projects/.github-private -w pr-review.yml -L 5 ``` ## Troubleshooting ### Workflow fails: "Resource not accessible by integration" -- Verify the `DON_PETRY_BOT_PETRY_PROJECT_PAT` secret is set and the token hasn't expired +- Verify the `DON_PETRY_BOT_GH_PAT` secret is set and the token hasn't expired - Check the machine user has access to the target repos - Ensure the PAT has the required scopes (Contents: read, Pull requests: read/write, Checks: read) @@ -110,9 +112,9 @@ gh run list --repo don-petry/pr-review-agent -w pr-review.yml -L 5 ## Machine User Details -The bot authenticates as a machine user account with a fine-grained PAT stored as the `DON_PETRY_BOT_PETRY_PROJECT_PAT` secret. The machine user is added to an org team listed in CODEOWNERS, so its approvals satisfy code owner review requirements. +The bot authenticates as a machine user account with a fine-grained PAT stored as the `DON_PETRY_BOT_GH_PAT` secret. The machine user is added to an org team listed in CODEOWNERS, so its approvals satisfy code owner review requirements. -For full setup instructions, see [MACHINE_USER_SETUP.md](MACHINE_USER_SETUP.md). +For full setup instructions, see [machine-user-setup.md](machine-user-setup.md). ## Architecture @@ -140,9 +142,9 @@ prompts/ - **Fine-grained PAT** scoped to only the permissions needed (Contents: read, Pull requests: read/write, Checks: read) - **90-day expiry** on PAT — set a calendar reminder to rotate - **PAT** stored only in GitHub Secrets, never logged or committed -- **Rotation** — generate a new PAT, update `DON_PETRY_BOT_PETRY_PROJECT_PAT` secret, revoke old token +- **Rotation** — generate a new PAT, update `DON_PETRY_BOT_GH_PAT` secret, revoke old token ## Related Documentation -- [AGENT.md](AGENT.md) — Full agent capabilities and design -- [MACHINE_USER_SETUP.md](MACHINE_USER_SETUP.md) — Machine user creation, PAT setup, and rotation +- [pr-review-agent.md](pr-review-agent.md) — Full agent capabilities and design +- [machine-user-setup.md](machine-user-setup.md) — Machine user creation, PAT setup, and rotation diff --git a/STATUS.md b/docs/pr-review-agent/status.md similarity index 75% rename from STATUS.md rename to docs/pr-review-agent/status.md index d37a17287..0cb2ec3ca 100644 --- a/STATUS.md +++ b/docs/pr-review-agent/status.md @@ -6,23 +6,23 @@ ## What Was Done ### 1. Machine User Authentication ✅ -- Machine user account with fine-grained PAT stored as `DON_PETRY_BOT_PETRY_PROJECT_PAT` org secret +- Machine user account with fine-grained PAT stored as `DON_PETRY_BOT_GH_PAT` org secret - Added to org team listed in CODEOWNERS — approvals satisfy code owner review requirements - Configured permissions: Contents (read), Pull requests (read/write), Checks (read) -- All workflows use `${{ secrets.DON_PETRY_BOT_PETRY_PROJECT_PAT }}` directly (no token generation step needed) +- All workflows use `${{ secrets.DON_PETRY_BOT_GH_PAT }}` directly (no token generation step needed) -**Why Machine User?** (migrated from GitHub App — see [issue #27](https://github.com/don-petry/pr-review-agent/issues/27)) +**Why Machine User?** (migrated from GitHub App — see [issue #27](https://github.com/petry-projects/.github-private/issues/27)) - GitHub Apps cannot be listed in CODEOWNERS (platform limitation) - Machine user approvals satisfy `require_code_owner_review` branch protection - Simpler auth: direct PAT, no JWT generation step ### 2. Comprehensive Documentation ✅ Created in repository: -- **SETUP.md** — Quick reference guide with configuration, secrets, commands, troubleshooting -- **IMPLEMENTATION.md** — Technical deep dive: architecture, design decisions, stuck PR cleanup -- **DOCUMENTATION.md** — Index linking all documentation +- **setup.md** — Quick reference guide with configuration, secrets, commands, troubleshooting +- **implementation.md** — Technical deep dive: architecture, design decisions, stuck PR cleanup +- **documentation-index.md** — Index linking all documentation - **Updated README.md** — Status badge and quick links -- **MACHINE_USER_SETUP.md** — Machine user account and PAT setup instructions +- **machine-user-setup.md** — Machine user account and PAT setup instructions ### 3. Stuck PR Cleanup ✅ (Approvals Posted) Fixed 24 stuck PRs across three repositories: @@ -83,65 +83,65 @@ This separation makes the system more robust and easier to debug. Agent decision ### Machine User over GitHub App Previously used: GitHub App (`petry-projects-pr-review-agent[bot]`) with auto-expiring JWT tokens. -**Why we switched:** GitHub Apps cannot be listed in CODEOWNERS files, so repos with `require_code_owner_review: true` were permanently blocked. Machine user accounts can be added to org teams listed in CODEOWNERS, resolving this limitation. See [issue #27](https://github.com/don-petry/pr-review-agent/issues/27). +**Why we switched:** GitHub Apps cannot be listed in CODEOWNERS files, so repos with `require_code_owner_review: true` were permanently blocked. Machine user accounts can be added to org teams listed in CODEOWNERS, resolving this limitation. See [issue #27](https://github.com/petry-projects/.github-private/issues/27). ## Configuration ### Required Secrets ```bash -gh secret set DON_PETRY_BOT_PETRY_PROJECT_PAT --repo don-petry/pr-review-agent --body "" -gh secret set CLAUDE_CODE_OAUTH_TOKEN --repo don-petry/pr-review-agent +gh secret set DON_PETRY_BOT_GH_PAT --repo petry-projects/.github-private --body "" +gh secret set CLAUDE_CODE_OAUTH_TOKEN --repo petry-projects/.github-private ``` ### Optional Variables ```bash # Engine: claude (default) or copilot -gh variable set REVIEW_ENGINE --repo don-petry/pr-review-agent --body "claude" +gh variable set REVIEW_ENGINE --repo petry-projects/.github-private --body "claude" # Live mode: true to post reviews, false for dry-run -gh variable set LIVE_MODE --repo don-petry/pr-review-agent --body "true" +gh variable set LIVE_MODE --repo petry-projects/.github-private --body "true" # Max reviews per run -gh variable set MAX_PRS --repo don-petry/pr-review-agent --body "10" +gh variable set MAX_PRS --repo petry-projects/.github-private --body "10" ``` ## Usage ### Check Recent Reviews ```bash -gh run list --repo don-petry/pr-review-agent -w pr-review.yml -L 5 -gh run view --repo don-petry/pr-review-agent --log +gh run list --repo petry-projects/.github-private -w pr-review.yml -L 5 +gh run view --repo petry-projects/.github-private --log ``` ### Trigger Manual Review ```bash # Dry-run (no changes) -gh workflow run pr-review.yml --repo don-petry/pr-review-agent -f dry_run=true +gh workflow run pr-review.yml --repo petry-projects/.github-private -f dry_run=true # Review specific PR -gh workflow run pr-review.yml --repo don-petry/pr-review-agent \ +gh workflow run pr-review.yml --repo petry-projects/.github-private \ -f pr_url=https://github.com/petry-projects/ContentTwin/pull/123 \ -f dry_run=false # Fix stuck PRs -gh workflow run fix-stuck-prs.yml --repo don-petry/pr-review-agent -f dry_run=false +gh workflow run fix-stuck-prs.yml --repo petry-projects/.github-private -f dry_run=false ``` ## Known Limitations 1. **Auto-merge requires repo settings**: Auto-merge must be enabled in each repo's settings for `gh pr merge --auto` to work. -2. **PAT rotation**: Machine user PATs expire (90-day recommended). Set a calendar reminder to rotate. See [MACHINE_USER_SETUP.md](MACHINE_USER_SETUP.md). +2. **PAT rotation**: Machine user PATs expire (90-day recommended). Set a calendar reminder to rotate. See [machine-user-setup.md](machine-user-setup.md). ## Support & Troubleshooting -See **[SETUP.md](SETUP.md)** for: +See **[setup.md](setup.md)** for: - Permission errors and fixes - Authentication issues - Workflow failures - Rate limiting and fallback engines -See **[IMPLEMENTATION.md](IMPLEMENTATION.md)** for: +See **[implementation.md](implementation.md)** for: - Architecture details - Design rationale - How each component works @@ -157,5 +157,5 @@ See **[IMPLEMENTATION.md](IMPLEMENTATION.md)** for: --- **Created by:** Claude Code Agent -**Repository:** don-petry/pr-review-agent +**Repository:** petry-projects/.github-private **Organization:** petry-projects diff --git a/scripts/engine.sh b/scripts/engine.sh index 1c6ecc473..8256aebfc 100644 --- a/scripts/engine.sh +++ b/scripts/engine.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # Engine abstraction layer for LLM invocations. -# Supports: claude, copilot +# Supports: claude, gemini, copilot # # Sourced by review-one-pr.sh — provides: # run_triage — no-tool tier (stdout capture) @@ -41,6 +41,18 @@ case "$REVIEW_ENGINE" in DUCK_ENGINE="copilot" DUCK_MODEL="gpt-5.4" ;; + gemini) + ENGINE_TRIAGE_MODEL="gemini-2.0-flash" + ENGINE_DEEP_MODEL="gemini-1.5-pro" + ENGINE_AUDIT_MODEL="gemini-1.5-pro" + ENGINE_ACTION_MODEL="gemini-1.5-pro" + ENGINE_SINGLE_MODEL="gemini-1.5-pro" + ENGINE_LABEL="triage: gemini-2.0-flash → deep: gemini-1.5-pro + duck: sonnet 4.6 → audit: gemini-1.5-pro" + ENGINE_SINGLE_LABEL="single-reviewer mode: gemini-1.5-pro" + # Cross-engine rubber duck: use Claude for diversity + DUCK_ENGINE="claude" + DUCK_MODEL="claude-sonnet-4-6" + ;; copilot) ENGINE_TRIAGE_MODEL="gpt-5-mini" ENGINE_DEEP_MODEL="gpt-5.2" @@ -54,7 +66,7 @@ case "$REVIEW_ENGINE" in DUCK_MODEL="claude-sonnet-4-6" ;; *) - echo "::error::Unknown REVIEW_ENGINE='$REVIEW_ENGINE' (expected: claude or copilot)" + echo "::error::Unknown REVIEW_ENGINE='$REVIEW_ENGINE' (expected: claude, gemini, or copilot)" exit 1 ;; esac @@ -67,11 +79,11 @@ export DUCK_ENGINE DUCK_MODEL echo " engine: $REVIEW_ENGINE ($ENGINE_LABEL)" # is_rate_limited -# Returns 0 (true) if the text looks like a Claude or Copilot rate-limit message. +# Returns 0 (true) if the text looks like a Claude, Gemini, or Copilot rate-limit message. # review-one-pr.sh exits with code 2 when this fires so the caller can switch engines. is_rate_limited() { local text="$1" - echo "$text" | grep -qiE "(hit your limit|rate[ -]?limit|resets [0-9]+(am|pm)|usage limit|quota exceeded|too many requests|exceeded.*quota)" + echo "$text" | grep -qiE "(hit your limit|rate[ -]?limit|resets [0-9]+(am|pm)|usage limit|quota exceeded|too many requests|exceeded.*quota|([^0-9]|^)429([^0-9]|$)|exhausted)" } # is_transient_failure @@ -112,6 +124,13 @@ run_triage() { --disallowed-tools "Bash,Read,Write,Edit,Grep,Glob,WebFetch,WebSearch,Task,TodoWrite,NotebookEdit" \ < "$prompt_file" || rc=$? ;; + gemini) + timeout "$TRIAGE_TIMEOUT_SEC" gemini --prompt "" \ + --model "$ENGINE_TRIAGE_MODEL" \ + --approval-mode auto_edit \ + --output-format text \ + < "$prompt_file" || rc=$? + ;; copilot) # gh copilot is now a built-in; auth via GH_PAT (user token with Copilot subscription). # Model selection is not supported by gh copilot suggest — uses Copilot's default. @@ -155,6 +174,13 @@ run_agentic() { --allowed-tools "Bash,Read,Grep,Glob" \ < "$prompt_file" ;; + gemini) + timeout "$DEEP_TIMEOUT_SEC" gemini --prompt "" \ + --model "$model" \ + --approval-mode auto_edit \ + --output-format text \ + < "$prompt_file" + ;; copilot) # gh copilot is now a built-in; auth via GH_PAT (user token with Copilot subscription). # Model selection is not supported by gh copilot suggest — uses Copilot's default. @@ -201,7 +227,7 @@ sys.exit(1) # run_duck # Cross-engine adversarial "rubber duck" review. -# Always uses the OPPOSITE engine from REVIEW_ENGINE. Output to stdout. +# Always uses a different model family from REVIEW_ENGINE. Output to stdout. # Strips the opposing engine's credentials to prevent cross-engine leakage. run_duck() { local prompt_file="$1" @@ -209,6 +235,7 @@ run_duck() { case "$DUCK_ENGINE" in claude) unset COPILOT_GITHUB_TOKEN 2>/dev/null || true + unset GOOGLE_API_KEY 2>/dev/null || true timeout "$DUCK_TIMEOUT_SEC" claude --print \ --model "$model" \ --permission-mode acceptEdits \ @@ -216,8 +243,18 @@ run_duck() { --max-turns 25 \ < "$prompt_file" ;; + gemini) + unset CLAUDE_CODE_OAUTH_TOKEN 2>/dev/null || true + unset COPILOT_GITHUB_TOKEN 2>/dev/null || true + timeout "$DUCK_TIMEOUT_SEC" gemini --prompt "" \ + --model "$model" \ + --approval-mode auto_edit \ + --output-format text \ + < "$prompt_file" + ;; copilot) unset CLAUDE_CODE_OAUTH_TOKEN 2>/dev/null || true + unset GOOGLE_API_KEY 2>/dev/null || true # gh copilot is now a built-in; auth via GH_PAT (user token with Copilot subscription). ( export GH_TOKEN="$COPILOT_GITHUB_TOKEN" timeout "$DUCK_TIMEOUT_SEC" gh copilot suggest "$(cat "$prompt_file")" --agent shell diff --git a/scripts/list-prs.sh b/scripts/list-prs.sh index 9d79d3ece..00775a2ba 100644 --- a/scripts/list-prs.sh +++ b/scripts/list-prs.sh @@ -24,7 +24,7 @@ set -euo pipefail # Configurable via environment / repo variables. BOT_USER is the GitHub # identity the workflow PAT authenticates as — both the queue scope (which # repos to scan) and the self-approval filter use it. -BOT_USER="${BOT_USER:-don-petry-bot}" +BOT_USER="${BOT_USER:-donpetry-bot}" TARGET_ORG="${TARGET_ORG:-petry-projects}" # Reject BOT_USER values that aren't valid GitHub usernames before diff --git a/scripts/repair-pr-approvals.sh b/scripts/repair-pr-approvals.sh index 5ea97261c..b60c3cc8e 100644 --- a/scripts/repair-pr-approvals.sh +++ b/scripts/repair-pr-approvals.sh @@ -81,7 +81,7 @@ while IFS= read -r repo; do done < <(gh pr list --repo "$repo" --state open --json url,number,comments --limit 100 2>/dev/null | jq -r '.[] | select(.comments | length > 0) | {url, number} | "\(.url)|\(.number)"') done < <( { - gh repo list "${BOT_USER:-don-petry-bot}" --json nameWithOwner --jq '.[].nameWithOwner' 2>/dev/null || true + gh repo list "${BOT_USER:-donpetry-bot}" --json nameWithOwner --jq '.[].nameWithOwner' 2>/dev/null || true gh repo list "${TARGET_ORG:-petry-projects}" --json nameWithOwner --jq '.[].nameWithOwner' 2>/dev/null || true } | sort -u ) diff --git a/scripts/review-batch.sh b/scripts/review-batch.sh index a65cd3b0c..256f9246a 100644 --- a/scripts/review-batch.sh +++ b/scripts/review-batch.sh @@ -30,6 +30,7 @@ actual=0 skipped_noops=0 failed=0 engine_fallbacks=0 +fallback_engines="" processed=0 session_aborted=0 abort_pr="" @@ -53,18 +54,34 @@ while IFS= read -r pr_url; do rc=0 bash scripts/review-one-pr.sh "$pr_url" || rc=$? - # Exit code 2 = engine rate-limited. Switch from claude to copilot once, - # then retry this PR. All later PRs in the batch use copilot too. + # Exit code 2 = engine rate-limited. + # Fallback chain: claude -> gemini -> copilot if [ "$rc" -eq 2 ] && [ "${REVIEW_ENGINE:-claude}" = "claude" ]; then - if ! gh extension list 2>/dev/null | grep -q copilot; then - echo "::warning::Copilot fallback engine unavailable (not installed) — skipping $pr_url and continuing batch" + if command -v gemini >/dev/null 2>&1 && [ -n "${GOOGLE_API_KEY:-}" ]; then + echo "::warning::Claude rate limit hit — switching to Gemini engine for remaining PRs" + export REVIEW_ENGINE=gemini + engine_fallbacks=$((engine_fallbacks + 1)) + fallback_engines="${fallback_engines:+$fallback_engines, }gemini" + rc=0 + bash scripts/review-one-pr.sh "$pr_url" || rc=$? + else + echo "::warning::Claude rate limit hit but Gemini fallback unavailable (CLI not installed or GOOGLE_API_KEY missing) — falling through to Copilot" + rc=2 + export REVIEW_ENGINE=gemini # Set to gemini so the next block catches it + fi + fi + + if [ "$rc" -eq 2 ] && [ "${REVIEW_ENGINE}" = "gemini" ]; then + if ! gh extension list 2>/dev/null | grep -q copilot && ! gh copilot --version > /dev/null 2>&1; then + echo "::warning::Copilot fallback engine unavailable — skipping $pr_url and continuing batch" failed=$((failed + 1)) echo "::endgroup::" continue fi - echo "::warning::Claude rate limit hit — switching to Copilot engine for remaining PRs" + echo "::warning::Gemini rate limit hit — switching to Copilot engine for remaining PRs" export REVIEW_ENGINE=copilot engine_fallbacks=$((engine_fallbacks + 1)) + fallback_engines="${fallback_engines:+$fallback_engines, }copilot" rc=0 bash scripts/review-one-pr.sh "$pr_url" || rc=$? fi @@ -102,7 +119,7 @@ done < "$PRS_FILE" remaining=$((total_candidates - processed)) summary="Summary: $actual reviews posted, $skipped_noops no-ops skipped, $failed failures" -[ "$engine_fallbacks" -gt 0 ] && summary="$summary, $engine_fallbacks engine fallback(s) to copilot" +[ "$engine_fallbacks" -gt 0 ] && summary="$summary, $engine_fallbacks engine fallback(s) to $fallback_engines" summary="$summary (processed $processed/$total_candidates candidates)" if [ "$session_aborted" -eq 1 ]; then diff --git a/scripts/review-one-pr.sh b/scripts/review-one-pr.sh index 5d18c31a0..407bcb0f9 100644 --- a/scripts/review-one-pr.sh +++ b/scripts/review-one-pr.sh @@ -6,8 +6,9 @@ # # Env: # GH_TOKEN — set by the workflow -# REVIEW_ENGINE — "claude" or "copilot" (default: claude) +# REVIEW_ENGINE — "claude", "gemini", or "copilot" (default: claude) # CLAUDE_CODE_OAUTH_TOKEN — (claude engine) set by the workflow +# GOOGLE_API_KEY — (gemini engine) set by the workflow # COPILOT_GITHUB_TOKEN — (copilot engine) set by the workflow # DRY_RUN — "true" or "false" # @@ -160,7 +161,7 @@ This PR has been through $REVIEW_CYCLE automated review cycles (cap: $MAX_CYCLES Please take a look manually, or close this PR if it's no longer needed. Once a human review resolves the situation, remove the \`needs-human-review\` label and the cascade can be re-engaged on the next push. -_Posted by the ${BOT_USER:-don-petry-bot} PR-review cascade._ +_Posted by the ${BOT_USER:-donpetry-bot} PR-review cascade._ ESCALATION_END gh pr comment "$PR_URL" --body-file "$ESCALATION_BODY" || echo " warn: gh pr comment failed — escalation marker not posted; will retry next cycle" gh pr edit "$PR_URL" --add-label needs-human-review 2>/dev/null || true