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 -> default model (currently opus) # /bonk opus -> Claude Opus 4.7 (default) # /bonk kimi -> 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<> "$GITHUB_OUTPUT" - name: Run Bonk (${{ steps.model.outputs.alias }}) uses: ask-bonk/ask-bonk/github@bfd92f4d0abb62d98558b35432534f7b9992f3eb # 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. ${{ steps.trigger.outputs.kind }} ${{ steps.trigger.outputs.number }} ${{ steps.trigger.outputs.title }} ${{ steps.trigger.outputs.body }} ${{ steps.trigger.outputs.head_sha }} ${{ steps.trigger.outputs.base_sha }} The maintainer's request: ${{ github.event.comment.body || github.event.review.body }}