name: Bonk

# /bonk and @ask-bonk both trigger 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):
#
#   /bonk <task>                         -> default model (currently opus)
#   /bonk opus <task>                    -> Claude Opus 4.7 (default)
#   /bonk kimi <task>                    -> Kimi K2.7 Code (cheap pass for tiny tasks)
#   @ask-bonk opus how would you do X?   -> Claude Opus 4.7
#
# Add new aliases by editing .github/bonk-models.json -- the resolver script
# only registers the selected alias in OPENCODE_CONFIG_CONTENT at runtime.

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

# Concurrency is at job level (below) rather than workflow level so it's only
# evaluated when the `if:` filter passes. Otherwise every issue_comment event
# in the repo would enter the group and could evict in-flight runs from
# unrelated, non-matching comments.

jobs:
  bonk:
    if: >-
      github.event.sender.type != 'Bot'
      && contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association || github.event.review.author_association)
      && (
        ((github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment')
          && (contains(github.event.comment.body, '/bonk') || contains(github.event.comment.body, '@ask-bonk')))
        || (github.event_name == 'pull_request_review'
          && (contains(github.event.review.body, '/bonk') || contains(github.event.review.body, '@ask-bonk')))
      )
    # Per-workflow group key so /bonk and /review have independent queues per
    # target. Spamming /bonk on the same issue/PR serializes; different actions
    # on the same target run in parallel.
    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: 45
    permissions:
      id-token: write
      contents: write
      issues: write
      pull-requests: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          fetch-depth: 1
          persist-credentials: false

      - name: Resolve model from comment
        id: model
        env:
          BODY: ${{ github.event.comment.body || github.event.review.body }}
        run: node .github/scripts/resolve-bonk-model.mjs

      - name: Setup pnpm
        uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8

      - name: Setup Node.js
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version-file: "package.json"
          cache: "pnpm"

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Resolve trigger context
        id: trigger
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_REPOSITORY: ${{ github.repository }}
          EVENT_NAME: ${{ github.event_name }}
          # issue_comment fires on both issues and PRs. github.event.issue.pull_request
          # is non-null when the issue is actually a PR.
          IS_PR_COMMENT: ${{ github.event.issue.pull_request != null }}
          ISSUE_NUMBER: ${{ github.event.issue.number }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
        run: |
          set -euo pipefail
          # Decide trigger_kind ("issue" or "pr") and target_number.
          if [[ "$EVENT_NAME" == "issue_comment" ]]; then
            if [[ "$IS_PR_COMMENT" == "true" ]]; then
              KIND=pr
              NUM="$ISSUE_NUMBER"   # issue.number is the PR number for PR comments
            else
              KIND=issue
              NUM="$ISSUE_NUMBER"
            fi
          else
            KIND=pr
            NUM="$PR_NUMBER"
          fi

          # Fetch title/body (heredoc-quoted to prevent newlines in titles or
          # bodies from corrupting $GITHUB_OUTPUT or smuggling step outputs).
          if [[ "$KIND" == "pr" ]]; then
              gh api "/repos/${GITHUB_REPOSITORY}/pulls/${NUM}" > /tmp/target.json
              SHA="$(jq -r .head.sha /tmp/target.json)"
              BASE="$(jq -r .base.sha /tmp/target.json)"
          else
              gh api "/repos/${GITHUB_REPOSITORY}/issues/${NUM}" > /tmp/target.json
              SHA=""
              BASE=""
          fi

          {
            echo "kind=${KIND}"
            echo "number=${NUM}"
            echo "head_sha=${SHA}"
            echo "base_sha=${BASE}"
            echo 'title<<TARGET_TITLE_EOF'
            jq -r '.title // ""' /tmp/target.json
            echo TARGET_TITLE_EOF
            echo 'body<<TARGET_BODY_EOF'
            jq -r '.body // ""' /tmp/target.json
            echo TARGET_BODY_EOF
          } >> "$GITHUB_OUTPUT"

      - name: Run Bonk (${{ steps.model.outputs.alias }})
        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: "/bonk,@ask-bonk"
          opencode_version: "1.4.11"
          permissions: write
          # Custom agent defined in .opencode/agents/auto-implementer.md.
          # Holds the investigation/reproduction protocol, scope discipline,
          # and PR body conventions so this workflow stays small.
          agent: auto-implementer
          prompt: |
            A maintainer has invoked /bonk on the emdash-cms/emdash repository. Follow your agent instructions for mode classification, investigation, reproduction, and posting.

            <trigger_kind>${{ steps.trigger.outputs.kind }}</trigger_kind>
            <target_number>${{ steps.trigger.outputs.number }}</target_number>
            <target_title>${{ steps.trigger.outputs.title }}</target_title>
            <target_body>
            ${{ steps.trigger.outputs.body }}
            </target_body>
            <head_sha>${{ steps.trigger.outputs.head_sha }}</head_sha>
            <base_sha>${{ steps.trigger.outputs.base_sha }}</base_sha>

            The maintainer's request:

            ${{ github.event.comment.body || github.event.review.body }}
