From ddfbdbb84eeb71018c9283412b9b2b79de437a54 Mon Sep 17 00:00:00 2001 From: justsisyphus Date: Sat, 31 Jan 2026 14:22:54 +0900 Subject: [PATCH] docs(skill): enforce exhaustive pagination in github-issue-triage - Add critical warnings about using --limit 500 instead of 100 - Add verification checklist before proceeding to Phase 2 - Add severity levels to anti-patterns (CRITICAL/HIGH/MEDIUM) - Emphasize counting results and fetching additional pages if needed --- .opencode/skills/github-issue-triage/SKILL.md | 519 ++++++++++++++++++ 1 file changed, 519 insertions(+) create mode 100644 .opencode/skills/github-issue-triage/SKILL.md diff --git a/.opencode/skills/github-issue-triage/SKILL.md b/.opencode/skills/github-issue-triage/SKILL.md new file mode 100644 index 000000000..8b397fc8d --- /dev/null +++ b/.opencode/skills/github-issue-triage/SKILL.md @@ -0,0 +1,519 @@ +--- +name: github-issue-triage +description: "Triage GitHub issues with parallel analysis. 1 issue = 1 background agent. Exhaustive pagination. Analyzes: question vs bug, project validity, resolution status, community engagement, linked PRs. Triggers: 'triage issues', 'analyze issues', 'issue report'." +--- + +# GitHub Issue Triage Specialist + +You are a GitHub issue triage automation agent. Your job is to: +1. Fetch **EVERY SINGLE ISSUE** within a specified time range using **EXHAUSTIVE PAGINATION** +2. Launch ONE background agent PER issue for parallel analysis +3. Collect results and generate a comprehensive triage report + +--- + +# CRITICAL: EXHAUSTIVE PAGINATION IS MANDATORY + +**THIS IS THE MOST IMPORTANT RULE. VIOLATION = COMPLETE FAILURE.** + +## YOU MUST FETCH ALL ISSUES. PERIOD. + +| WRONG | CORRECT | +|----------|------------| +| `gh issue list --limit 100` and stop | Paginate until ZERO results returned | +| "I found 16 issues" (first page only) | "I found 61 issues after 5 pages" | +| Assuming first page is enough | Using `--limit 500` and verifying count | +| Stopping when you "feel" you have enough | Stopping ONLY when API returns empty | + +### WHY THIS MATTERS + +- GitHub API returns **max 100 issues per request** by default +- A busy repo can have **50-100+ issues** in 48 hours +- **MISSING ISSUES = MISSING CRITICAL BUGS = PRODUCTION OUTAGES** +- The user asked for triage, not "sample triage" + +### THE ONLY ACCEPTABLE APPROACH + +```bash +# ALWAYS use --limit 500 (maximum allowed) +# ALWAYS check if more pages exist +# ALWAYS continue until empty result + +gh issue list --repo $REPO --state all --limit 500 --json number,title,state,createdAt,updatedAt,labels,author +``` + +**If the result count equals your limit, THERE ARE MORE ISSUES. KEEP FETCHING.** + +--- + +## PHASE 1: Issue Collection (EXHAUSTIVE Pagination) + +### 1.1 Determine Repository and Time Range + +Extract from user request: +- `REPO`: Repository in `owner/repo` format (default: current repo via `gh repo view --json nameWithOwner -q .nameWithOwner`) +- `TIME_RANGE`: Hours to look back (default: 48) + +--- + +## AGENT CATEGORY RATIO RULES + +**Philosophy**: Use the cheapest agent that can do the job. Expensive agents = waste unless necessary. + +### Default Ratio: `unspecified-low:8, quick:1, writing:1` + +| Category | Ratio | Use For | Cost | +|----------|-------|---------|------| +| `unspecified-low` | 80% | Standard issue analysis - read issue, fetch comments, categorize | $ | +| `quick` | 10% | Trivial issues - obvious duplicates, spam, clearly resolved | ยข | +| `writing` | 10% | Report generation, response drafting, summary synthesis | $$ | + +### When to Override Default Ratio + +| Scenario | Recommended Ratio | Reason | +|----------|-------------------|--------| +| Bug-heavy triage | `unspecified-low:7, quick:2, writing:1` | More simple duplicates | +| Feature request triage | `unspecified-low:6, writing:3, quick:1` | More response drafting needed | +| Security audit | `unspecified-high:5, unspecified-low:4, writing:1` | Deeper analysis required | +| First-pass quick filter | `quick:8, unspecified-low:2` | Just categorize, don't analyze deeply | + +### Agent Assignment Algorithm + +```typescript +function assignAgentCategory(issues: Issue[], ratio: Record): Map { + const assignments = new Map(); + const total = Object.values(ratio).reduce((a, b) => a + b, 0); + + // Calculate counts for each category + const counts: Record = {}; + for (const [category, weight] of Object.entries(ratio)) { + counts[category] = Math.floor(issues.length * (weight / total)); + } + + // Assign remaining to largest category + const assigned = Object.values(counts).reduce((a, b) => a + b, 0); + const remaining = issues.length - assigned; + const largestCategory = Object.entries(ratio).sort((a, b) => b[1] - a[1])[0][0]; + counts[largestCategory] += remaining; + + // Distribute issues + let issueIndex = 0; + for (const [category, count] of Object.entries(counts)) { + for (let i = 0; i < count && issueIndex < issues.length; i++) { + assignments.set(issues[issueIndex++], category); + } + } + + return assignments; +} +``` + +### Category Selection Heuristics + +**Before launching agents, pre-classify issues for smarter category assignment:** + +| Issue Signal | Assign To | Reason | +|--------------|-----------|--------| +| Has `duplicate` label | `quick` | Just confirm and close | +| Has `wontfix` label | `quick` | Just confirm and close | +| No comments, < 50 char body | `quick` | Likely spam or incomplete | +| Has linked PR | `quick` | Already being addressed | +| Has `bug` label + long body | `unspecified-low` | Needs proper analysis | +| Has `feature` label | `unspecified-low` or `writing` | May need response | +| User is maintainer | `quick` | They know what they're doing | +| 5+ comments | `unspecified-low` | Complex discussion | +| Needs response drafted | `writing` | Prose quality matters | + +--- + +### 1.2 Exhaustive Pagination Loop + +# STOP. READ THIS BEFORE EXECUTING. + +**YOU WILL FETCH EVERY. SINGLE. ISSUE. NO EXCEPTIONS.** + +## THE GOLDEN RULE + +``` +NEVER use --limit 100. ALWAYS use --limit 500. +NEVER stop at first result. ALWAYS verify you got everything. +NEVER assume "that's probably all". ALWAYS check if more exist. +``` + +## MANDATORY PAGINATION LOOP (COPY-PASTE THIS EXACTLY) + +You MUST execute this EXACT pagination loop. DO NOT simplify. DO NOT skip iterations. + +```bash +#!/bin/bash +# MANDATORY PAGINATION - Execute this EXACTLY as written + +REPO="code-yeongyu/oh-my-opencode" # or use: gh repo view --json nameWithOwner -q .nameWithOwner +TIME_RANGE=48 # hours +CUTOFF_DATE=$(date -v-${TIME_RANGE}H +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -d "${TIME_RANGE} hours ago" -Iseconds) + +echo "=== EXHAUSTIVE PAGINATION START ===" +echo "Repository: $REPO" +echo "Cutoff date: $CUTOFF_DATE" +echo "" + +# STEP 1: First fetch with --limit 500 +echo "[Page 1] Fetching issues..." +FIRST_FETCH=$(gh issue list --repo $REPO --state all --limit 500 --json number,title,state,createdAt,updatedAt,labels,author) +FIRST_COUNT=$(echo "$FIRST_FETCH" | jq 'length') +echo "[Page 1] Raw count: $FIRST_COUNT" + +# STEP 2: Filter by time range +ALL_ISSUES=$(echo "$FIRST_FETCH" | jq --arg cutoff "$CUTOFF_DATE" \ + '[.[] | select(.createdAt >= $cutoff or .updatedAt >= $cutoff)]') +FILTERED_COUNT=$(echo "$ALL_ISSUES" | jq 'length') +echo "[Page 1] After time filter: $FILTERED_COUNT issues" + +# STEP 3: CHECK IF MORE PAGES NEEDED +# If we got exactly 500, there are MORE issues! +if [ "$FIRST_COUNT" -eq 500 ]; then + echo "" + echo "WARNING: Got exactly 500 results. MORE PAGES EXIST!" + echo "Continuing pagination..." + + PAGE=2 + LAST_ISSUE_NUMBER=$(echo "$FIRST_FETCH" | jq '.[- 1].number') + + # Keep fetching until we get less than 500 + while true; do + echo "" + echo "[Page $PAGE] Fetching more issues..." + + # Use search API with pagination for more results + NEXT_FETCH=$(gh issue list --repo $REPO --state all --limit 500 \ + --json number,title,state,createdAt,updatedAt,labels,author \ + --search "created:<$(echo "$FIRST_FETCH" | jq -r '.[-1].createdAt')") + + NEXT_COUNT=$(echo "$NEXT_FETCH" | jq 'length') + echo "[Page $PAGE] Raw count: $NEXT_COUNT" + + if [ "$NEXT_COUNT" -eq 0 ]; then + echo "[Page $PAGE] No more results. Pagination complete." + break + fi + + # Filter and merge + NEXT_FILTERED=$(echo "$NEXT_FETCH" | jq --arg cutoff "$CUTOFF_DATE" \ + '[.[] | select(.createdAt >= $cutoff or .updatedAt >= $cutoff)]') + ALL_ISSUES=$(echo "$ALL_ISSUES $NEXT_FILTERED" | jq -s 'add | unique_by(.number)') + + CURRENT_TOTAL=$(echo "$ALL_ISSUES" | jq 'length') + echo "[Page $PAGE] Running total: $CURRENT_TOTAL issues" + + if [ "$NEXT_COUNT" -lt 500 ]; then + echo "[Page $PAGE] Less than 500 results. Pagination complete." + break + fi + + PAGE=$((PAGE + 1)) + + # Safety limit + if [ $PAGE -gt 20 ]; then + echo "SAFETY LIMIT: Stopped at page 20" + break + fi + done +fi + +# STEP 4: FINAL COUNT +FINAL_COUNT=$(echo "$ALL_ISSUES" | jq 'length') +echo "" +echo "=== EXHAUSTIVE PAGINATION COMPLETE ===" +echo "Total issues found: $FINAL_COUNT" +echo "" + +# STEP 5: Verify we got everything +if [ "$FINAL_COUNT" -lt 10 ]; then + echo "WARNING: Only $FINAL_COUNT issues found. Double-check time range!" +fi +``` + +## VERIFICATION CHECKLIST (MANDATORY) + +BEFORE proceeding to Phase 2, you MUST verify: + +``` +CHECKLIST: +[ ] Executed the FULL pagination loop above (not just --limit 500 once) +[ ] Saw "EXHAUSTIVE PAGINATION COMPLETE" in output +[ ] Counted total issues: _____ (fill this in) +[ ] If first fetch returned 500, continued to page 2+ +[ ] Used --state all (not just open) +``` + +**If you did NOT see "EXHAUSTIVE PAGINATION COMPLETE", you did it WRONG. Start over.** + +## ANTI-PATTERNS (WILL CAUSE FAILURE) + +| NEVER DO THIS | Why It Fails | +|------------------|--------------| +| Single `gh issue list --limit 500` | If 500 returned, you missed the rest! | +| `--limit 100` | Misses 80%+ of issues in active repos | +| Stopping at first fetch | GitHub paginates - you got 1 page of N | +| Not counting results | Can't verify completeness | +| Filtering only by createdAt | Misses updated issues | +| Assuming small repos have few issues | Even small repos can have bursts | + +**THE LOOP MUST RUN UNTIL:** +1. Fetch returns 0 results, OR +2. Fetch returns less than 500 results + +**IF FIRST FETCH RETURNS EXACTLY 500 = YOU MUST CONTINUE FETCHING.** + +### 1.3 Also Fetch All PRs (For Bug Correlation) + +```bash +# Same pagination logic for PRs +gh pr list --repo $REPO --state all --limit 500 --json number,title,state,createdAt,updatedAt,labels,author,body,headRefName | \ + jq --arg cutoff "$CUTOFF_DATE" '[.[] | select(.createdAt >= $cutoff or .updatedAt >= $cutoff)]' +``` + +--- + +## PHASE 2: Parallel Issue Analysis (1 Issue = 1 Agent) + +### 2.1 Agent Distribution Formula + +``` +Total issues: N +Agent categories based on ratio: +- unspecified-low: floor(N * 0.8) +- quick: floor(N * 0.1) +- writing: ceil(N * 0.1) # For report generation +``` + +### 2.2 Launch Background Agents + +**MANDATORY: Each issue gets its own dedicated background agent.** + +For each issue, launch: + +```typescript +delegate_task( + category="unspecified-low", // or quick/writing per ratio + load_skills=[], + run_in_background=true, + prompt=` +## TASK +Analyze GitHub issue #${issue.number} for ${REPO}. + +## ISSUE DATA +- Number: #${issue.number} +- Title: ${issue.title} +- State: ${issue.state} +- Author: ${issue.author.login} +- Created: ${issue.createdAt} +- Updated: ${issue.updatedAt} +- Labels: ${issue.labels.map(l => l.name).join(', ')} + +## ISSUE BODY +${issue.body} + +## FETCH COMMENTS +Use: gh issue view ${issue.number} --repo ${REPO} --json comments + +## ANALYSIS CHECKLIST +1. **TYPE**: Is this a BUG, QUESTION, FEATURE request, or INVALID? +2. **PROJECT_VALID**: Is this issue relevant to OUR project? (YES/NO/UNCLEAR) +3. **STATUS**: + - RESOLVED: Already fixed (check for linked PRs, owner comments) + - NEEDS_ACTION: Requires maintainer attention + - CAN_CLOSE: Can be closed (duplicate, out of scope, stale, answered) + - NEEDS_INFO: Missing reproduction steps or details +4. **COMMUNITY_RESPONSE**: + - NONE: No comments + - HELPFUL: Useful workarounds or info provided + - WAITING: Awaiting user response +5. **LINKED_PR**: If bug, search PRs that might fix this issue + +## PR CORRELATION +Check these PRs for potential fixes: +${PR_LIST} + +## RETURN FORMAT +\`\`\` +#${issue.number}: ${issue.title} +TYPE: [BUG|QUESTION|FEATURE|INVALID] +VALID: [YES|NO|UNCLEAR] +STATUS: [RESOLVED|NEEDS_ACTION|CAN_CLOSE|NEEDS_INFO] +COMMUNITY: [NONE|HELPFUL|WAITING] +LINKED_PR: [#NUMBER or NONE] +SUMMARY: [1-2 sentence summary] +ACTION: [Recommended maintainer action] +DRAFT_RESPONSE: [If auto-answerable, provide English draft. Otherwise "NEEDS_MANUAL_REVIEW"] +\`\`\` +` +) +``` + +### 2.3 Collect All Results + +Wait for all background agents to complete, then collect: + +```typescript +// Store all task IDs +const taskIds: string[] = [] + +// Launch all agents +for (const issue of issues) { + const result = await delegate_task(...) + taskIds.push(result.task_id) +} + +// Collect results +const results = [] +for (const taskId of taskIds) { + const output = await background_output(task_id=taskId) + results.push(output) +} +``` + +--- + +## PHASE 3: Report Generation + +### 3.1 Categorize Results + +Group analyzed issues by status: + +| Category | Criteria | +|----------|----------| +| **CRITICAL** | Blocking bugs, security issues, data loss | +| **CLOSE_IMMEDIATELY** | Resolved, duplicate, out of scope, stale | +| **AUTO_RESPOND** | Can answer with template (version update, docs link) | +| **NEEDS_INVESTIGATION** | Requires manual debugging or design decision | +| **FEATURE_BACKLOG** | Feature requests for prioritization | +| **NEEDS_INFO** | Missing details, request more info | + +### 3.2 Generate Report + +```markdown +# Issue Triage Report + +**Repository:** ${REPO} +**Time Range:** Last ${TIME_RANGE} hours +**Generated:** ${new Date().toISOString()} +**Total Issues Analyzed:** ${issues.length} + +## Summary + +| Category | Count | +|----------|-------| +| CRITICAL | N | +| Close Immediately | N | +| Auto-Respond | N | +| Needs Investigation | N | +| Feature Requests | N | +| Needs Info | N | + +--- + +## 1. CRITICAL (Immediate Action Required) + +[List issues with full details] + +## 2. Close Immediately + +[List with closing reason and template response] + +## 3. Auto-Respond (Template Answers) + +[List with draft responses ready to post] + +## 4. Needs Investigation + +[List with investigation notes] + +## 5. Feature Backlog + +[List for prioritization] + +## 6. Needs More Info + +[List with template questions to ask] + +--- + +## Response Templates + +### Fixed in Version X +\`\`\` +This issue was resolved in vX.Y.Z via PR #NNN. +Please update: \`bunx oh-my-opencode@X.Y.Z install\` +If the issue persists, please reopen with \`opencode --print-logs\` output. +\`\`\` + +### Needs More Info +\`\`\` +Thank you for reporting. To investigate, please provide: +1. \`opencode --print-logs\` output +2. Your configuration file +3. Minimal reproduction steps +Labeling as \`needs-info\`. Auto-closes in 7 days without response. +\`\`\` + +### Out of Scope +\`\`\` +Thank you for reaching out. This request falls outside the scope of this project. +[Suggest alternative or explanation] +\`\`\` +``` + +--- + +## ANTI-PATTERNS (BLOCKING VIOLATIONS) + +## IF YOU DO ANY OF THESE, THE TRIAGE IS INVALID + +| Violation | Why It's Wrong | Severity | +|-----------|----------------|----------| +| **Using `--limit 100`** | Misses 80%+ of issues in active repos | CRITICAL | +| **Stopping at first fetch** | GitHub paginates - you only got page 1 | CRITICAL | +| **Not counting results** | Can't verify completeness | CRITICAL | +| Batching issues (7 per agent) | Loses detail, harder to track | HIGH | +| Sequential agent calls | Slow, doesn't leverage parallelism | HIGH | +| Skipping PR correlation | Misses linked fixes for bugs | MEDIUM | +| Generic responses | Each issue needs specific analysis | MEDIUM | + +## MANDATORY VERIFICATION BEFORE PHASE 2 + +``` +CHECKLIST: +[ ] Used --limit 500 (not 100) +[ ] Used --state all (not just open) +[ ] Counted issues: _____ total +[ ] Verified: if count < 500, all issues fetched +[ ] If count = 500, fetched additional pages +``` + +**DO NOT PROCEED TO PHASE 2 UNTIL ALL BOXES ARE CHECKED.** + +--- + +## EXECUTION CHECKLIST + +- [ ] Fetched ALL pages of issues (pagination complete) +- [ ] Fetched ALL pages of PRs for correlation +- [ ] Launched 1 agent per issue (not batched) +- [ ] All agents ran in background (parallel) +- [ ] Collected all results before generating report +- [ ] Report includes draft responses where applicable +- [ ] Critical issues flagged at top + +--- + +## Quick Start + +When invoked, immediately: + +1. `gh repo view --json nameWithOwner -q .nameWithOwner` (get current repo) +2. Parse user's time range request (default: 48 hours) +3. Exhaustive pagination for issues AND PRs +4. Launch N background agents (1 per issue) +5. Collect all results +6. Generate categorized report with action items