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 🙏
14 KiB
| 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. |
|
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-triageor/issue-triage allor/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
enorenglish→ 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
bodyfields 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 30–90 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 (30–90d): {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.55–0.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
ghCLI (notcurlGitHub API) except for collaborators list commentsingh issue list --json comments= count only; full content requiresgh issue view {num} --json comments- Never execute any action without explicit user validation in chat
- Drafted actions must be visible BEFORE any
gh issue commentorgh 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*
Related: /pr-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.