Repository navigation
Issue Triage Agent (Agentic) #27
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Issue Triage Agent (Agentic) | |
| on: | |
| issues: | |
| types: [opened, edited] | |
| schedule: | |
| - cron: '0 */6 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| mode: | |
| description: 'Run mode: single or all' | |
| required: true | |
| default: 'all' | |
| type: choice | |
| options: | |
| - all | |
| - single | |
| force: | |
| description: 'Re-triage issues that already have a triage label (debug only)' | |
| required: false | |
| default: false | |
| type: boolean | |
| permissions: | |
| contents: read | |
| issues: write | |
| pull-requests: write | |
| models: read # required for the GitHub Models inference API | |
| jobs: | |
| triage: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - name: Checkout repo | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: '3.11' | |
| - name: Install dependencies | |
| run: | | |
| pip install -r requirements.txt || true | |
| gh --version | |
| # ───────────────────────────────────────────────────────────── | |
| # SINGLE-ISSUE PATH (triggered by issue opened/edited) | |
| # | |
| # We do NOT inline ${{ github.event.issue.body }} into a heredoc — | |
| # backticks, quotes, and $-signs in the body would otherwise mangle | |
| # the script. Instead we fetch the issue via `gh api` so it round-trips | |
| # cleanly through JSON. | |
| # ───────────────────────────────────────────────────────────── | |
| - name: Fetch issue payload (single) | |
| if: github.event_name == 'issues' | |
| run: | | |
| echo "📥 Fetching issue #${{ github.event.issue.number }} via gh api" | |
| gh api "repos/${GITHUB_REPOSITORY}/issues/${{ github.event.issue.number }}" \ | |
| --jq '{number: .number, title: .title, body: .body}' \ | |
| > /tmp/issue.json | |
| echo "── Issue payload (sanitized preview) ──" | |
| jq '{number, title, body_length: (.body | length)}' /tmp/issue.json | |
| # Wrap into a list so triage_batch.py can consume it uniformly | |
| jq '[.]' /tmp/issue.json > /tmp/issues.json | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Log full body (single) | |
| if: github.event_name == 'issues' | |
| run: | | |
| echo "================================================================================" | |
| echo "� FULL ISSUE BODY (as the AI will see it)" | |
| echo "================================================================================" | |
| jq -r '.body' /tmp/issue.json | |
| echo "================================================================================" | |
| - name: Pre-label as triaged (single) | |
| if: github.event_name == 'issues' | |
| run: | | |
| gh label create "triaged" --repo "$GITHUB_REPOSITORY" 2>/dev/null || true | |
| gh issue edit ${{ github.event.issue.number }} \ | |
| --add-label "triaged" --repo "$GITHUB_REPOSITORY" | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| continue-on-error: true | |
| # ───────────────────────────────────────────────────────────── | |
| # AI-DRIVEN TRIAGE — the LLM reads the body and decides everything: | |
| # classification, the comment to post, and (for auto_fix) the literal | |
| # current_text / desired_text to substitute in the docs. | |
| # triage_batch.py applies the AI's decision (label + comment + PR). | |
| # ───────────────────────────────────────────────────────────── | |
| - name: AI triage + (optional) auto-PR (single) | |
| if: github.event_name == 'issues' | |
| run: | | |
| echo "🤖 Running AI triage via GitHub Models on issue #${{ github.event.issue.number }}" | |
| python3 triage_batch.py /tmp/issues.json | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| LEARN_PR_TOKEN: ${{ secrets.LEARN_PR_TOKEN }} | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| AI_MODEL: openai/gpt-4o-mini | |
| # Set AI_DEBUG=1 to dump the full prompt + raw model response. | |
| AI_DEBUG: '1' | |
| # Single-issue (event-driven) runs always reprocess — a human | |
| # just opened/edited the issue and wants fresh triage. | |
| TRIAGE_MODE: single | |
| # ───────────────────────────────────────────────────────────── | |
| # BATCH PATH (scheduled OR workflow_dispatch with mode=all) | |
| # ───────────────────────────────────────────────────────────── | |
| - name: Batch triage (scheduled / manual) | |
| if: github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && github.event.inputs.mode == 'all') | |
| run: | | |
| echo "🔄 BATCH TRIAGE starting..." | |
| # We include `labels` in the JSON so triage_batch.py can skip | |
| # issues we've already handled (anything carrying one of the | |
| # TRIAGE_LABELS — triaged / auto-fix / needs-review / etc.). | |
| gh issue list --state open \ | |
| --json number,title,body,labels --limit 50 \ | |
| --repo "$GITHUB_REPOSITORY" > /tmp/issues.json | |
| echo "📋 Fetched $(jq length /tmp/issues.json) open issues" | |
| python3 triage_batch.py /tmp/issues.json | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| LEARN_PR_TOKEN: ${{ secrets.LEARN_PR_TOKEN }} | |
| GITHUB_REPOSITORY: ${{ github.repository }} | |
| AI_MODEL: openai/gpt-4o-mini | |
| TRIAGE_MODE: batch | |
| # Only re-triage already-labeled issues when the user explicitly | |
| # asks for it via the workflow_dispatch "force" input. | |
| FORCE_RETRIAGE: ${{ github.event.inputs.force == 'true' && '1' || '' }} |