name: Review PR

# /review triggers this workflow. The first word after the trigger picks a
# model alias from .github/bonk-models.json. Currently registered aliases
# (see that file for the source of truth):
#
#   /review                              -> default model (currently opus)
#   /review opus                         -> Claude Opus 4.7 (default)
#   /review kimi                         -> Kimi K2.7 Code (cheap pass for tiny PRs)
#
# Add new aliases by editing .github/bonk-models.json.

on:
  issue_comment:
    types: [created]
  pull_request_review_comment:
    types: [created]

# Concurrency is at job level (below) rather than workflow level so it's only
# evaluated when the `if:` filter passes.

jobs:
  review:
    # mentions in this check must match the `mentions` input below.
    # For issue_comment events, require the comment to be on a PR (not an issue).
    if: >-
      github.event.sender.type != 'Bot'
      && contains(github.event.comment.body, '/review')
      && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)
      && (github.event_name != 'issue_comment' || github.event.issue.pull_request != null)
    # Per-workflow group key so /review and /bonk have independent queues
    # per target.
    concurrency:
      group: ${{ github.workflow }}-${{ github.event.issue.number || github.event.pull_request.number || github.ref }}
      cancel-in-progress: false
    runs-on: ubuntu-latest
    timeout-minutes: 30
    permissions:
      id-token: write
      contents: read
      issues: write
      pull-requests: write
    steps:
      - name: Get PR number
        id: pr-number
        env:
          # issue_comment on a PR exposes the number via github.event.issue.number;
          # pull_request_review_comment exposes it via github.event.pull_request.number.
          PR_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
        run: |
          echo "number=${PR_NUMBER}" >> "$GITHUB_OUTPUT"

      - name: Verify PR exists
        id: verify-pr
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_REPOSITORY: ${{ github.repository }}
          PR_NUMBER: ${{ steps.pr-number.outputs.number }}
        run: |
          if gh api "/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" > /dev/null 2>&1; then
            echo "exists=true" >> "$GITHUB_OUTPUT"
          else
            echo "exists=false" >> "$GITHUB_OUTPUT"
            echo "::warning::PR #${PR_NUMBER} not found, skipping review"
          fi

      - name: Checkout repository
        if: steps.verify-pr.outputs.exists == 'true'
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          fetch-depth: 1
          persist-credentials: false

      - name: Resolve model from comment
        if: steps.verify-pr.outputs.exists == 'true'
        id: model
        env:
          BODY: ${{ github.event.comment.body }}
        run: node .github/scripts/resolve-bonk-model.mjs

      - name: Setup pnpm
        if: steps.verify-pr.outputs.exists == 'true'
        uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

      - name: Setup Node.js
        if: steps.verify-pr.outputs.exists == 'true'
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version-file: "package.json"
          cache: "pnpm"

      - name: Install dependencies
        if: steps.verify-pr.outputs.exists == 'true'
        run: pnpm install --frozen-lockfile

      - name: Get PR details
        if: steps.verify-pr.outputs.exists == 'true'
        id: pr-details
        run: |
          gh api "/repos/${GITHUB_REPOSITORY}/pulls/${PR_NUMBER}" > /tmp/pr_data.json
          # Use heredoc form for every field — PR titles and bodies can contain
          # newlines that would otherwise corrupt $GITHUB_OUTPUT and let a PR
          # author smuggle arbitrary step outputs via their title/body.
          {
            echo 'title<<PR_TITLE_EOF'
            jq -r .title /tmp/pr_data.json
            echo PR_TITLE_EOF
            echo "sha=$(jq -r .head.sha /tmp/pr_data.json)"
            echo 'body<<PR_BODY_EOF'
            jq -r .body /tmp/pr_data.json
            echo PR_BODY_EOF
          } >> "$GITHUB_OUTPUT"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_REPOSITORY: ${{ github.repository }}
          PR_NUMBER: ${{ steps.pr-number.outputs.number }}

      - name: Run Review (${{ steps.model.outputs.alias }})
        if: steps.verify-pr.outputs.exists == 'true'
        uses: ask-bonk/ask-bonk/github@19f4b9f34ed400c28b8cbed0ecd84bce931a38b7 # main as of 2026-04-24
        env:
          CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CF_AI_GATEWAY_ACCOUNT_ID }}
          CLOUDFLARE_GATEWAY_ID: ${{ secrets.CF_AI_GATEWAY_NAME }}
          CLOUDFLARE_API_TOKEN: ${{ secrets.CF_AI_GATEWAY_TOKEN }}
          OPENCODE_CONFIG_CONTENT: ${{ steps.model.outputs.opencode_config }}
        with:
          model: ${{ steps.model.outputs.model }}
          mentions: "/review"
          opencode_version: "1.4.11"
          permissions: write
          # NO_PUSH scopes the installation token to contents:read so the
          # reviewer cannot push, even if it tries to commit. Write tools
          # remain enabled in the agent definition so it can scaffold fixes
          # locally to verify reasoning.
          token_permissions: NO_PUSH
          # Custom agent defined in .opencode/agents/auto-reviewer.md.
          # Holds the review philosophy, investigation protocol, and posting
          # protocol so workflows stay small.
          agent: auto-reviewer
          prompt: |
            Review pull request #${{ steps.pr-number.outputs.number }} ("${{ steps.pr-details.outputs.title }}").

            Follow your agent instructions for investigation, severity calibration, and posting. Post a single review with line-anchored comments via the gh CLI; the summary will be posted as a separate workflow comment.

            <pr_number>${{ steps.pr-number.outputs.number }}</pr_number>
            <pr_description>
            ${{ steps.pr-details.outputs.body }}
            </pr_description>

            If the PR looks good, respond with only "LGTM!" and skip posting a review.
