name: Issue triage on: issues: types: [opened, reopened] permissions: issues: write contents: read jobs: label: runs-on: ubuntu-latest steps: - name: Auto-label by title and body uses: actions/github-script@v7 with: script: | const issue = context.payload.issue; const title = (issue.title || '').toLowerCase(); const body = (issue.body || '').toLowerCase(); const text = `${title}\n${body}`; const labels = new Set(); // Type if (/\b(bug|crash|panic|broken|stack ?trace|regression|err(?:or)?|fail(?:ed|ure)?)\b/.test(text)) labels.add('bug'); if (/\b(feat(?:ure)?|request|enhancement|wishlist|proposal|please add|would be nice|support for)\b/.test(text)) labels.add('enhancement'); if (/\b(docs?|readme|documentation|typo|grammar|wording|spelling)\b/.test(text)) labels.add('documentation'); if (/\b(question|how (?:do|to)|why does|what does|is it possible)\b/.test(text)) labels.add('question'); // Locale — title contains CJK (Chinese, Japanese, Korean) characters if (/[぀-ヿ㐀-鿿가-힯]/.test(issue.title || '')) labels.add('lang:zh'); // Areas (path-driven hints) if (/crates\/tui|\btui\b|ratatui|composer|sidebar/.test(text)) labels.add('area:tui'); if (/crates\/core|engine|turn ?loop|agent ?loop/.test(text)) labels.add('area:core'); if (/crates\/mcp|\bmcp\b/.test(text)) labels.add('area:mcp'); if (/crates\/state|sqlite|sessions?|persistence/.test(text)) labels.add('area:state'); if (/crates\/execpolicy|approval|sandbox|seatbelt|landlock/.test(text)) labels.add('area:execpolicy'); if (/crates\/tools|tool[ _]call|tool[ _]registry/.test(text)) labels.add('area:tools'); if (/install|cargo install|npm install|scoop|homebrew|prebuilt|binary/.test(text)) labels.add('area:install'); if (/windows/.test(text)) labels.add('os:windows'); if (/macos|darwin|apple silicon/.test(text)) labels.add('os:macos'); if (/\blinux\b|ubuntu|debian|fedora|arch ?linux/.test(text)) labels.add('os:linux'); if (labels.size === 0) return; // Only add labels that already exist on the repo to avoid creating noise. const existing = await github.paginate(github.rest.issues.listLabelsForRepo, { owner: context.repo.owner, repo: context.repo.repo, per_page: 100, }); const existingNames = new Set(existing.map(l => l.name)); const toAdd = [...labels].filter(name => existingNames.has(name)); if (toAdd.length === 0) return; await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, labels: toAdd, });