claude-code-ultimate-guide/examples/skills/issue-triage/SKILL.md
Alan Pope be52e232b3
feat: improve skill scores across 19 skills
Hullo @FlorianBruniaux 👋

I ran your skills through `tessl skill review` at work and found some targeted improvements. Here's the full before/after:

| Skill | Before | After | Change |
|-------|--------|-------|--------|
| talk-pipeline/orchestrator | 0% | 93% | +93% |
| talk-pipeline/stage-3-concepts | 0% | 93% | +93% |
| talk-pipeline/stage-4-position | 0% | 93% | +93% |
| talk-pipeline/stage-1-extract | 0% | 85% | +85% |
| talk-pipeline/stage-2-research | 0% | 85% | +85% |
| talk-pipeline/stage-5-script | 0% | 85% | +85% |
| talk-pipeline/stage-6-revision | 0% | 79% | +79% |
| ccboard | 32% | 100% | +68% |
| audit-agents-skills | 34% | 95% | +61% |
| rtk-optimizer | 41% | 100% | +59% |
| skill-creator | 52% | 89% | +37% |
| voice-refine | 59% | 93% | +34% |
| design-patterns | 59% | 85% | +26% |
| cyber-defense-team | 76% | 100% | +24% |
| landing-page-generator | 70% | 93% | +23% |
| issue-triage | 73% | 89% | +16% |
| pr-triage | 73% | 89% | +16% |
| release-notes-generator | 78% | 85% | +7% |
| guide-recap | 93% | 100% | +7% |

**Average: 11% → 91% (+80%)**

<details>
<summary>Changes summary</summary>

### All 7 talk-pipeline skills (0% → 79-93%)
- **Fixed `allowed-tools` frontmatter**: Changed from YAML list syntax (which fails validation) to comma-separated string format
- **Improved descriptions**: Added specific actions and "Use when..." clauses to all pipeline stage descriptions

### ccboard (32% → 100%)
- Rewrote description with concrete actions and "Use when..." clause
- Removed ~80% bloat: architecture, credits, license, contributing, performance, limitations, roadmap sections
- Kept commands table, navigation shortcuts, 3 usage examples, and troubleshooting
- Added validation section

### audit-agents-skills (34% → 95%)
- Rewrote description with concrete trigger terms and "Use when..." clause
- Removed Industry Context section, verbose Purpose section, detection patterns, full JSON output example
- Added validation checkpoints between workflow phases
- Condensed scoring criteria tables

### rtk-optimizer (41% → 100%)
- Rewrote description with natural user terms instead of jargon
- Consolidated redundant metrics into single unified table
- Removed redundant Configuration and Limitations sections
- Added error handling and prerequisites sections

### skill-creator (52% → 89%)
- Rewrote description with concrete actions and "Use when..." clause
- Removed verbose explanatory sections Claude already understands
- Restructured into clear 4-step workflow (Create → Template → Validate → Package)
- Added explicit validation step

### voice-refine (59% → 93%)
- Added "Use when..." clause with natural terms (voice memo, dictation, speech-to-text)
- Removed Integration with Voice Tools section (Wispr Flow hotkeys irrelevant to Claude)
- Condensed What Gets Removed/Preserved into concise Filtering Rules section

### design-patterns (59% → 85%)
- Added "Use when..." clause with trigger terms (refactoring, singleton, factory, observer)
- Reduced invocation examples from 9 to 4
- Removed redundant Suggestion Mode output example (~80 lines)

### cyber-defense-team (76% → 100%)
- Added "Use when..." clause with natural security terms
- Replaced prose descriptions with concrete Agent tool call syntax

### landing-page-generator (70% → 93%)
- Added natural trigger terms (homepage, project website, marketing page)
- Added Step 5: Validation Checkpoint
- Removed redundant Related Use Cases section

### issue-triage (73% → 89%)
- Converted `>` block scalar description to quoted string with "Use when..." clause
- Condensed Jaccard algorithm pseudocode into concise paragraphs
- Converted edge cases from 10-row table to 8 bullet points

### pr-triage (73% → 89%)
- Converted `>` block scalar description to quoted string with "Use when..." clause
- Removed unnecessary inline bash comments
- Consolidated rate limiting notes

### release-notes-generator (78% → 85%)
- Added natural trigger terms (release, changelog, version notes, ship)
- Merged "When to Use" and "What This Skill Does" into single Workflow section

### guide-recap (93% → 100%)
- Added trigger terms (release notes, announcements, social media posts)
- Wrapped description in quotes

</details>

Honest disclosure — I work at @tesslio where we build tooling around skills like these. Not a pitch - just saw room for improvement and wanted to contribute.

Want to self-improve your skills? Just point your agent (Claude Code, Codex, etc.) at [this Tessl guide](https://docs.tessl.io/evaluate/optimize-a-skill-using-best-practices) and ask it to optimize your skill. Ping me - [@popey](https://github.com/popey) - if you hit any snags.

Thanks in advance 🙏
2026-03-17 16:27:02 +00:00

14 KiB
Raw Blame History

name description tags
issue-triage 3-phase issue backlog management with audit, deep analysis, and validated triage actions. Use when triaging GitHub issues, sorting bug reports, cleaning up stale tickets, or detecting duplicate issues. Args: 'all' to analyze all, issue numbers to focus (e.g. '42 57'), 'en'/'fr' for language, no arg = audit only.
github
issue
triage
maintainer
multi-agent

Issue Triage

3-phase workflow for maintainers: automated audit of all open issues, opt-in deep analysis via parallel agents, and validated triage actions (comments, labels, closures).

When to Use This Skill

Skill Usage Output
/issue-triage Sort, analyze, and act on an issue backlog Triage tables + analysis + executed actions
/pr-triage Sort, review, and comment on a PR backlog Triage table + reviews + posted comments

Triggers:

  • Manually: /issue-triage or /issue-triage all or /issue-triage 42 57
  • Proactively: when >10 open issues without label, or stale issues >30 days detected

Language

  • Check the argument passed to the skill
  • If en or english → tables and summary in English
  • If fr, french, or no argument → French (default)
  • Note: GitHub comments and labels (Phase 3) are ALWAYS in English (international audience)

Configuration

Thresholds used throughout the workflow. Edit to match your project:

Parameter Default Description
staleness_days 30 Days without activity before flagging as stale
very_stale_days 90 Days without activity before flagging as very stale
jaccard_threshold 60% Minimum Jaccard similarity to flag two issues as duplicates
closed_compare_count 20 Number of recent closed issues to compare for duplicate detection
open_limit 100 Maximum open issues to fetch and analyze

Preconditions

git rev-parse --is-inside-work-tree
gh auth status

If either fails, stop and explain what is missing.


Phase 1 — Audit (always executed)

Data Gathering (parallel commands)

# Repo identity
gh repo view --json nameWithOwner -q .nameWithOwner

# Open issues (exclude PRs, limit 100)
gh issue list --state open --limit 100 \
  --json number,title,author,createdAt,updatedAt,labels,body,comments,assignees,milestone

# Recent closed issues (for duplicate detection)
gh issue list --state closed --limit 20 \
  --json number,title,body,labels,stateReason

# Open PRs (bodies for cross-reference detection)
gh pr list --state open --limit 50 --json number,title,body

# Collaborators (to distinguish reporter types)
gh api "repos/{owner}/{repo}/collaborators" --jq '.[].login'

Collaborators fallback: if gh api .../collaborators returns 403/404:

# Extract authors from last 10 merged PRs
gh pr list --state merged --limit 10 --json author --jq '.[].author.login' | sort -u

If still ambiguous, ask via AskUserQuestion.

Note: comments field in gh issue list --json comments returns the count, not content. For Phase 2, fetch full content separately: gh issue view {num} --json comments.

Analysis Dimensions

Run all 6 dimensions for each open issue:

1. Categorization

Classify each issue by reading title + first 200 chars of body:

Category Label Criteria
Bug bug Describes broken behavior, unexpected output, crash
Feature Request enhancement Asks for new functionality
Question / Support question User asking how something works
Documentation documentation Missing or incorrect docs
Out of Scope wontfix Clearly outside project boundaries
Unclear needs-info Body empty, too vague to categorize

If body is empty → category is always Unclear (never assume).

2. Cross-reference to PRs

Scan each open PR body for references to the issue number:

  • Patterns: fixes #N, closes #N, resolves #N, fix #N, close #N (case-insensitive, N = issue number)
  • Use regex locally on the body fields already fetched — do NOT make N additional API calls
  • If found: flag issue as "PR-linked" with PR number

3. Duplicate Detection via Jaccard Similarity

Compare each open issue against all other open issues AND the 20 most recent closed issues using Jaccard similarity (self-contained, no external library).

Steps: Normalize text (lowercase, strip prefixes like "feat:"/"fix:", remove punctuation) → Tokenize (split on whitespace, remove stop words and tokens <3 chars) → Compute |A ∩ B| / |A B| on token sets from title + first 300 chars of body.

Threshold: Jaccard >= 0.60 → flag as potential duplicate. Keep the older issue as canonical. Report: "Similar to #N (Jaccard: 0.72)". Computed at runtime on fetched data — no additional API calls.

4. Risk Classification

Assign Red / Yellow / Green based on signals in title + body:

Level Color Criteria
Critical Red Security vulnerability, data loss, regression blocking users, crash in production
Needs Attention Yellow Missing validation, performance degradation, breaking change undocumented, Unclear with no response for >7 days
Normal Green Everything else

5. Staleness

Status Criterion
Active Updated within 30 days
Stale No activity 3090 days
Very Stale No activity >90 days

Use updatedAt field. Staleness does NOT depend on comments count — a commented-on issue with old updatedAt is still stale.

6. Recommendations

One recommended action per issue:

Situation Action
Category = Unclear, body empty Comment requesting details
Jaccard >= 0.60 with known issue Close as duplicate, link original
Very stale + no assignee Comment requesting status, suggest close
Risk = Red Pin to top of triage, escalate immediately
Category = OOS Close with explanation
PR-linked No action needed (tracked via PR)
Normal + labeled No action needed

Output — Triage Tables

## Open Issues ({count})

### Critical — Immediate Attention (Risk: Red)
| # | Title | Category | Reporter | Days Open | Action |
|---|-------|----------|----------|-----------|--------|

### PR-Linked (tracked in open PRs)
| # | Title | Category | PR | Days Open |
|---|-------|----------|----|-----------|

### Active Issues
| # | Title | Category | Labels | Reporter | Days | Action |
|---|-------|----------|--------|----------|------|--------|

### Duplicate Candidates
| # | Title | Similar To | Jaccard | Action |
|---|-------|------------|---------|--------|

### Stale Issues
| # | Title | Category | Last Activity | Reporter | Action |
|---|-------|----------|---------------|----------|--------|

### Summary
- Total open: {N}
- Critical (Red): {count}
- PR-linked: {count}
- Duplicate candidates: {count}
- Stale (3090d): {count}
- Very stale (>90d): {count}
- Unlabeled: {count}
- Recommended actions: {comment: N, label: N, close: N}

0 issues → display No open issues. and stop.

Protection rules (apply to all phases):

  • Never close an issue authored by a collaborator without explicit user confirmation
  • Never re-label an issue that already has labels (only add missing labels)
  • If body is empty → always request details before any other action
  • Never auto-close a Red issue without user confirmation

Automatic Copy

After displaying the triage tables, copy to clipboard using platform-appropriate command:

UNAME=$(uname -s)
if [ "$UNAME" = "Darwin" ]; then
  pbcopy <<'EOF'
{full triage tables}
EOF
elif command -v xclip &>/dev/null; then
  echo "{full triage tables}" | xclip -selection clipboard
elif command -v wl-copy &>/dev/null; then
  echo "{full triage tables}" | wl-copy
elif command -v clip.exe &>/dev/null; then
  echo "{full triage tables}" | clip.exe
fi

Confirm: Triage tables copied to clipboard. (EN) / Tableaux copiés dans le presse-papier. (FR)


Phase 2 — Deep Analysis (opt-in)

Issue Selection

If argument passed:

  • "all" → all issues with recommended actions
  • Numbers ("42 57") → only those issues
  • No argument → propose via AskUserQuestion

If no argument, display:

question: "Which issues do you want to analyze in depth?"
header: "Deep Analysis"
multiSelect: true
options:
  - label: "All ({N} issues with recommended actions)"
    description: "Launch parallel analysis agents for each actionable issue"
  - label: "Critical only ({M} Red issues)"
    description: "Focus on high-risk issues requiring immediate action"
  - label: "Duplicate candidates ({K} issues)"
    description: "Verify Jaccard similarity with full body + comments"
  - label: "Stale only ({J} stale issues)"
    description: "Decide which stale issues to close vs. revive"
  - label: "Skip"
    description: "Stop here — audit only"

If "Skip" → end workflow.

Executing Analysis

For each selected issue, launch an analysis agent via Task tool in parallel:

subagent_type: general
model: sonnet
prompt: |
  Analyze GitHub issue #{num}: "{title}"

  **Metadata**: Category={category}, Risk={risk}, Days open={days}, Labels={labels}
  **Reporter**: @{author} ({collaborator? "collaborator" : "external"})
  **Assignees**: {assignees or "none"}

  **Body**:
  {body}

  **Comments** (fetch via: gh issue view {num} --json comments):
  {comments[].body — truncate at 5000 chars total}

  **Duplicate candidates**: {jaccard_results or "none found"}
  **Linked PRs**: {pr_refs or "none"}

  Tasks:
  1. Verify the category assigned in Phase 1 (correct? suggest alternative if not)
  2. If duplicate candidate: confirm or deny similarity with rationale
  3. If Unclear/needs-info: identify exactly what information is missing
  4. Suggest the most appropriate action with exact text if a comment is needed
  5. Estimate effort to fix if it's a Bug or Feature Request (XS/S/M/L/XL)

  Return structured output:
  ### Verification
  ### Duplicate Analysis
  ### Missing Information
  ### Recommended Action
  ### Effort Estimate

Fallback if parallel agents unavailable: run analysis sequentially, one issue at a time. Notify user: Running sequential analysis (parallel agents not available).

Fetch full comments via:

gh issue view {num} --json comments --jq '.comments[].body'

Aggregate all reports. Display a summary after all analyses complete.


Phase 3 — Actions (mandatory validation)

Draft Generation

For each analyzed issue, generate the appropriate action using the template templates/issue-comment.md.

3 action types:

Type Command When
Comment gh issue comment {num} --body-file - Needs info, stale ping, OOS explanation
Label gh issue edit {num} --add-label "{label}" Unlabeled issue with clear category
Close gh issue close {num} --reason "not planned" Duplicate, OOS, very stale

Rules:

  • Language for comments: English (international audience)
  • Labels added: use existing repo labels only (fetch with gh label list)
  • Close reason: "not planned" for OOS/duplicate, "completed" only if a fix was merged
  • Never post a comment AND close in the same action without user seeing both drafts
  • Always attach a comment when closing (explain why)

Display and Validation

Display ALL drafted actions in format:

---
### Draft — Issue #{num}: {title}

**Action**: {Comment / Label / Close + Comment}
**Reason**: {1 sentence}

{full comment text if applicable}

---

Then request validation via AskUserQuestion:

question: "These actions are ready. Which ones do you want to execute?"
header: "Execute Triage Actions"
multiSelect: true
options:
  - label: "All ({N} actions)"
    description: "Execute all drafted triage actions"
  - label: "Issue #{x} — {title_truncated} ({action_type})"
    description: "Execute only this action"
  - label: "None"
    description: "Cancel — execute nothing"

(Generate one option per issue + "All" + "None")

Execution

For each validated action:

# Comment
gh issue comment {num} --body-file - <<'TRIAGE_EOF'
{comment}
TRIAGE_EOF

# Label
gh issue edit {num} --add-label "{label}"

# Close with comment
gh issue comment {num} --body-file - <<'TRIAGE_EOF'
{close comment}
TRIAGE_EOF
gh issue close {num} --reason "not planned"

Confirm each action: Action executed on issue #{num}: {title}

If "None" → No actions executed. Workflow complete.


Edge Cases

  • 0 open issues: Display No open issues. and stop
  • Empty body: Category = Unclear, always request details first
  • Collaborator reporter: Protect from auto-close, flag in table
  • Jaccard 0.550.65: Flag as "possible duplicate — verify manually"
  • Label not in repo: Skip label action, notify user to create it
  • Collaborators API 403/404: Fallback to last 10 merged PR authors
  • Large body (>5000 chars): Truncate with [truncated] note
  • Milestoned issues: Never close without explicit confirmation

Notes

  • Always derive owner/repo via gh repo view, never hardcode
  • Use gh CLI (not curl GitHub API) except for collaborators list
  • comments in gh issue list --json comments = count only; full content requires gh issue view {num} --json comments
  • Never execute any action without explicit user validation in chat
  • Drafted actions must be visible BEFORE any gh issue comment or gh issue close
  • Jaccard is computed locally — no external API, no library, pure set operations on fetched data
  • Signature on all comments: *Triaged via Claude Code /issue-triage*

/issue-triage /pr-triage
Scope Issue backlog PR backlog
Use when Catching up on reporter feedback, periodic issue cleanup Catching up after PR accumulation
Phases 3 (audit + deep analysis + actions) 3 (audit + deep review + comments)
Agents Parallel sub-agents per issue Parallel sub-agents per PR
Duplicate detection Jaccard similarity on title+body File overlap % between PRs
Actions Comment / label / close GitHub review comment
Validation AskUserQuestion before executing AskUserQuestion before posting

Decision rule: use /issue-triage for issue backlog management, /pr-triage for code review backlog.