|
| 1 | +name: Issue Triage |
| 2 | + |
| 3 | +on: |
| 4 | + issues: |
| 5 | + types: [opened] |
| 6 | + |
| 7 | +permissions: |
| 8 | + issues: write |
| 9 | + |
| 10 | +jobs: |
| 11 | + triage: |
| 12 | + runs-on: ubuntu-latest |
| 13 | + steps: |
| 14 | + - name: Wait before responding |
| 15 | + run: | |
| 16 | + DELAY=$((300 + RANDOM % 600)) |
| 17 | + echo "Waiting ${DELAY}s" |
| 18 | + sleep $DELAY |
| 19 | +
|
| 20 | + - name: Triage issue |
| 21 | + uses: actions/github-script@v7 |
| 22 | + env: |
| 23 | + SLM_SERVICE_KEY: ${{ secrets.SLM_SERVICE_KEY }} |
| 24 | + with: |
| 25 | + github-token: ${{ secrets.GH_PAT }} |
| 26 | + script: | |
| 27 | + const issue = context.payload.issue; |
| 28 | +
|
| 29 | + // Skip if already triaged |
| 30 | + const triageLabels = ['bug', 'enhancement', 'question']; |
| 31 | + const hasTriageLabel = issue.labels.some(l => triageLabels.includes(l.name)); |
| 32 | + if (hasTriageLabel) { |
| 33 | + console.log('Issue already triaged, skipping'); |
| 34 | + return; |
| 35 | + } |
| 36 | +
|
| 37 | + const systemPrompt = `You are a maintainer of Steam Library Manager (SLM), a Linux Steam library organizer. Your GitHub username is HeikesFootSlave. |
| 38 | +
|
| 39 | + You triage incoming issues. Respond like a real person - short, casual, helpful. English only. |
| 40 | +
|
| 41 | + RULES: |
| 42 | + - Never use em-dash, smart quotes, or formal AI phrases |
| 43 | + - Never say "I'm an AI" or "automated response" |
| 44 | + - Keep responses 1-4 sentences max |
| 45 | + - For bugs: ask for log file if not provided, don't try to solve it |
| 46 | + - For features: acknowledge, don't overcommit |
| 47 | + - For spam: close with a one-liner |
| 48 | + - Sometimes (20% chance) just label without commenting |
| 49 | +
|
| 50 | + ABOUT SLM: |
| 51 | + - Python/PyQt6 desktop app for organizing Steam game libraries on Linux |
| 52 | + - Features: Smart Collections (AND/OR/NOT logic), 17 AutoCat types, HLTB/ProtonDB/SteamDeck integration, external games (Epic/GOG/Lutris/etc) |
| 53 | + - Install: AppImage, AUR (yay -S steam-library-manager), .deb, .rpm, tar.gz |
| 54 | + - Requires: Python 3.10+, running Steam client (not Big Picture) |
| 55 | + - Data dir: ~/.local/share/SteamLibraryManager/ |
| 56 | + - Log file: ~/.local/share/SteamLibraryManager/steamlibmgr.log |
| 57 | + - Config: Settings dialog (Ctrl+P) |
| 58 | + - Steam path auto-detected (native + Flatpak) |
| 59 | + - Database rebuilt on first launch (~10-30s) |
| 60 | +
|
| 61 | + ISSUE TEMPLATE FIELDS (bug reports come structured): |
| 62 | + Bug reports have these fields: SLM Version, Install method, Linux Distro, Steam installation (Native/Flatpak), Steam Deck (No/Yes-LCD/Yes-OLED), What happened, Steps to reproduce, Expected behavior, Log file, Screenshots. |
| 63 | + Feature requests have: What should SLM do, Why do you need this, Alternatives, Additional context. |
| 64 | + Use these fields to give targeted responses. |
| 65 | +
|
| 66 | + COMMON SUPPORT ANSWERS: |
| 67 | + - "Steam not found" -> Set path in Settings > General > Steam Path. Flatpak Steam is at ~/.var/app/com.valvesoftware.Steam/.local/share/Steam |
| 68 | + - "Collections disappeared" -> Steam cloud reset. Use File > Import > DB Backup |
| 69 | + - "App won't start" -> Check log at ~/.local/share/SteamLibraryManager/steamlibmgr.log |
| 70 | + - "Permission denied" -> Check Steam dir permissions. Flatpak needs --filesystem access |
| 71 | + - "ProtonDB/Deck filters empty" -> Run Tools > Batch > Update ProtonDB / Deck Status first |
| 72 | + - "External games not found" -> Run the platform launcher once first (Epic/GOG/etc create config files on first run) |
| 73 | + - "HLTB data missing" -> Run Tools > Batch > Update HLTB Data |
| 74 | + - "Enrichment takes forever" -> Normal for first run with large libraries. Subsequent runs use cache |
| 75 | + - "Auto-update not working" -> Only for AppImage. AUR/Flatpak use their package manager |
| 76 | +
|
| 77 | + Respond with JSON only: |
| 78 | + { |
| 79 | + "category": "spam" | "question" | "bug" | "enhancement", |
| 80 | + "should_comment": true | false, |
| 81 | + "should_close": true | false, |
| 82 | + "comment": "your response text (empty if should_comment is false)" |
| 83 | + }`; |
| 84 | +
|
| 85 | + const response = await fetch('https://api.anthropic.com/v1/messages', { |
| 86 | + method: 'POST', |
| 87 | + headers: { |
| 88 | + 'Content-Type': 'application/json', |
| 89 | + 'x-api-key': process.env.SLM_SERVICE_KEY, |
| 90 | + 'anthropic-version': '2023-06-01' |
| 91 | + }, |
| 92 | + body: JSON.stringify({ |
| 93 | + model: 'claude-sonnet-4-6', |
| 94 | + max_tokens: 500, |
| 95 | + messages: [{ |
| 96 | + role: 'user', |
| 97 | + content: `Triage this GitHub issue:\n\nTitle: ${issue.title}\n\nBody:\n${(issue.body || '(empty)').substring(0, 2000)}` |
| 98 | + }], |
| 99 | + system: systemPrompt |
| 100 | + }) |
| 101 | + }); |
| 102 | +
|
| 103 | + if (!response.ok) { |
| 104 | + console.log(`API error: ${response.status}`); |
| 105 | + return; |
| 106 | + } |
| 107 | +
|
| 108 | + const data = await response.json(); |
| 109 | + const text = data.content[0].text; |
| 110 | +
|
| 111 | + let triage; |
| 112 | + try { |
| 113 | + const cleaned = text.replace(/```json\n?/g, '').replace(/```\n?/g, '').trim(); |
| 114 | + triage = JSON.parse(cleaned); |
| 115 | + } catch (e) { |
| 116 | + console.log('Failed to parse response:', text); |
| 117 | + return; |
| 118 | + } |
| 119 | +
|
| 120 | + // Apply label |
| 121 | + const labelMap = { |
| 122 | + 'question': 'question', |
| 123 | + 'bug': 'bug', |
| 124 | + 'enhancement': 'enhancement' |
| 125 | + }; |
| 126 | + if (labelMap[triage.category]) { |
| 127 | + await github.rest.issues.addLabels({ |
| 128 | + owner: context.repo.owner, |
| 129 | + repo: context.repo.repo, |
| 130 | + issue_number: issue.number, |
| 131 | + labels: [labelMap[triage.category]] |
| 132 | + }); |
| 133 | + } |
| 134 | +
|
| 135 | + // Post comment |
| 136 | + if (triage.should_comment && triage.comment) { |
| 137 | + await github.rest.issues.createComment({ |
| 138 | + owner: context.repo.owner, |
| 139 | + repo: context.repo.repo, |
| 140 | + issue_number: issue.number, |
| 141 | + body: triage.comment |
| 142 | + }); |
| 143 | + } |
| 144 | +
|
| 145 | + // Close if spam |
| 146 | + if (triage.should_close) { |
| 147 | + await github.rest.issues.update({ |
| 148 | + owner: context.repo.owner, |
| 149 | + repo: context.repo.repo, |
| 150 | + issue_number: issue.number, |
| 151 | + state: 'closed' |
| 152 | + }); |
| 153 | + } |
| 154 | +
|
| 155 | + console.log(`Triaged as: ${triage.category}, commented: ${triage.should_comment}, closed: ${triage.should_close}`); |
0 commit comments