claude-code-ultimate-guide/examples/skills/pr-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

19 KiB
Raw Blame History

name description tags
pr-triage 4-phase PR backlog management with audit, deep code review, validated comments, and optional worktree setup. Use when triaging pull requests, catching up on pending code reviews, or managing a backlog of open PRs. Args: 'all' to review all, PR numbers to focus (e.g. '42 57'), 'en'/'fr' for language, no arg = audit only.
github
pr
triage
review
maintainer
multi-agent
worktree

PR Triage

4-phase workflow for maintainers: automated audit of all open PRs, opt-in deep review via parallel agents, validated comment posting, and optional worktree setup for local review.

When to Use This Skill

Skill Usage Output
/pr-triage Sort, review, and comment on a PR backlog Triage table + reviews + posted comments
/review-pr Review a single PR in depth Inline PR review

Triggers:

  • Manually: /pr-triage or /pr-triage all or /pr-triage 42 57
  • Proactively: when >5 PRs open without review, or stale PR >14 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 (Phase 3) are ALWAYS in English (international audience)

Configuration

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

Parameter Default Description
staleness_days 14 Days without activity before flagging as stale
overlap_threshold 50% Shared files % to flag as overlapping
cluster_min_prs 3 Author PR count to trigger cluster suggestion
xl_cutoff_additions 1000 Additions above which a PR is classified XL
xl_cutoff_files 10 Changed files above which a PR is "too large"

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)

gh repo view --json nameWithOwner -q .nameWithOwner
gh pr list --state open --limit 50 \
  --json number,title,author,createdAt,updatedAt,additions,deletions,changedFiles,isDraft,mergeable,reviewDecision,statusCheckRollup,body
gh api "repos/{owner}/{repo}/collaborators" --jq '.[].login'

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

gh pr list --state merged --limit 10 --json author --jq '.[].author.login' | sort -u

If still ambiguous, ask via AskUserQuestion.

For each PR, fetch reviews and changed files:

gh api "repos/{owner}/{repo}/pulls/{num}/reviews" \
  --jq '[.[] | .user.login + ":" + .state] | join(", ")'
gh pr view {num} --json files --jq '[.files[].path] | join(",")'

Notes: Fetching files requires 1 API call per PR — for 20+ PRs, prioritize overlap candidates. The author field is an object; always extract .author.login.

Analysis

Size classification:

Label Additions
XS < 50
S 50200
M 200500
L 5001000
XL > 1000

Size format: +{additions}/-{deletions}, {files} files ({label})

Detections:

  • Overlaps: compare file lists across PRs — if >50% files in common → cross-reference
  • Clusters: author with 3+ open PRs → suggest review order (smallest first)
  • Staleness: no activity for >14 days → flag "stale"
  • CI status: via statusCheckRollupclean / unstable / dirty
  • Reviews: approved / changes_requested / none

PR ↔ Issue linking:

  • Scan each PR body for fixes #N, closes #N, resolves #N (case-insensitive)
  • If found, display in the table: Fixes #42 in the Action/Status column

Categorization:

Internal PRs: author in collaborators list

External — Ready: additions ≤ 1000 AND files ≤ 10 AND mergeableCONFLICTING AND CI clean/unstable

External — Problematic: any of:

  • additions > 1000 OR files > 10
  • OR mergeable == CONFLICTING (merge conflict)
  • OR CI dirty (statusCheckRollup contains failures)
  • OR overlap with another open PR (>50% shared files)

Output — Triage Table

## Open PRs ({count})

### Internal PRs
| PR | Title | Size | CI | Status |
| -- | ----- | ---- | -- | ------ |

### External — Ready for Review
| PR | Author | Title | Size | CI | Reviews | Action |
| -- | ------ | ----- | ---- | -- | ------- | ------ |

### External — Problematic
| PR | Author | Title | Size | Problem | Recommended Action |
| -- | ------ | ----- | ---- | ------- | ------------------ |

### Summary
- Quick wins: {XS/S PRs ready to merge}
- Risks: {overlaps, XL sizes, CI dirty}
- Clusters: {authors with 3+ PRs}
- Stale: {PRs with no activity >14d}
- Overlaps: {PRs touching the same files}

0 PRs → display No open PRs. and stop.

Navigation Post-Phase 1

After displaying the triage table, ask via AskUserQuestion:

question: "What would you like to do next?"
header: "Next Step"
options:
  - label: "Phase 2 — Deep review"
    description: "Analyze selected PRs with code-reviewer agents and generate comment drafts"
  - label: "Phase 4 — Create worktrees"
    description: "Set up local worktrees for hands-on review (skips comment generation)"
  - label: "Done"
    description: "End the workflow here"

Note: Phase 3 (posting comments) is NOT offered here — it requires the drafts generated in Phase 2. If the user picks "Phase 4", Phase 2 → Phase 3 remains accessible afterward.

Automatic Copy

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

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

Confirm: Triage table copied to clipboard. (EN) / Tableau copié dans le presse-papier. (FR)


Phase 2 — Deep Review (opt-in)

PR Selection

If argument passed:

  • "all" → all external PRs
  • Numbers ("42 57") → only those PRs
  • No argument → propose via AskUserQuestion

If no argument, display:

question: "Which PRs do you want to review in depth?"
header: "Deep Review"
multiSelect: true
options:
  - label: "All external"
    description: "Review {N} external PRs with parallel code-reviewer agents"
  - label: "Problematic only"
    description: "Focus on {M} risky PRs (CI dirty, too large, overlaps)"
  - label: "Ready only"
    description: "Review {K} PRs ready to merge"
  - label: "Skip"
    description: "Stop here — audit only"

Draft PR behavior:

  • Draft PRs are EXCLUDED from "All external" and "Ready only"
  • Draft PRs are INCLUDED in "Problematic only" (they need attention)
  • To review a draft: type its number explicitly (e.g. 42)

If "Skip" → end workflow.

Executing Reviews

For each selected PR, launch a code-reviewer agent via Task tool in parallel:

subagent_type: code-reviewer
model: sonnet
prompt: |
  Review PR #{num}: "{title}" by @{author}

  **Metadata**: +{additions}/-{deletions}, {changedFiles} files ({size_label})
  **CI**: {ci_status} | **Reviews**: {existing_reviews} | **Draft**: {isDraft}

  **PR Body**:
  {body}

  **Diff**:
  {gh pr diff {num} output}

  Apply your security and architecture expertise. Use the project-specific checklist
  from the SKILL.md Configuration section if available.

  Return structured review:
  ### Critical Issues
  ### Important Issues
  ### Suggestions
  ### What's Good

  Be specific: quote file:line, explain the issue, suggest the fix.

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

Fetch diff via:

gh pr diff {num}
gh pr view {num} --json body,title,author -q '{body: .body, title: .title, author: .author.login}'

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


Phase 3 — Comments (mandatory validation)

Draft Generation

For each reviewed PR, generate a GitHub comment using the template templates/review-comment.md.

Rules:

  • Language: English (international audience)
  • Tone: professional, constructive, factual
  • Always include at least 1 positive point
  • Quote code lines when relevant (format file:42)

Display and Validation

Display ALL drafted comments in format:

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

{full comment}

---

Then request validation via AskUserQuestion:

question: "These comments are ready. Which ones do you want to post?"
header: "Post Comments"
multiSelect: true
options:
  - label: "All ({N} comments)"
    description: "Post on all reviewed PRs"
  - label: "PR #{x} — {title_truncated}"
    description: "Post only on this PR"
  - label: "None"
    description: "Cancel — post nothing"

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

Posting

For each validated comment:

gh pr comment {num} --body-file - <<'REVIEW_EOF'
{comment}
REVIEW_EOF

Confirm each post: Comment posted on PR #{num}: {title}

If "None" → No comments posted. Workflow complete.


Project-Specific Checklist

Add your stack's checklist to the agent prompt in Phase 2. Examples by stack:

Node.js / TypeScript:

  • No any type without explicit justification
  • async/await error handling (try/catch or .catch())
  • No unhandled promise rejections
  • Input validation at API boundaries

Python:

  • Type hints on all public functions
  • Exception specificity (no bare except:)
  • Resource cleanup (with statements, context managers)
  • No mutable default arguments

Rust:

  • Result<T, E> with .context() for error chain (no .unwrap() in production code)
  • No clone() on hot paths without justification
  • lazy_static! or once_cell for static regex
  • Lifetime annotations where ownership is non-obvious

Go:

  • Explicit error handling (no _ discard without comment)
  • defer for resource cleanup
  • Context propagation in concurrent code
  • No goroutine leaks

Generic (stack-agnostic):

  • No secrets or hardcoded credentials
  • New public functions have tests
  • Breaking changes documented in PR body
  • Dependencies added have clear justification


Phase 4 — Worktree Setup (opt-in)

Creates local git worktrees for each selected PR so you can run, test, or review code without switching branches.

Never triggered automatically — only via Phase 1 navigation or explicit user request.

Step 4.1 — Cache check + PR list

Cache check: before using data from Phase 1, verify it is less than 30 minutes old:

CACHE_FILE="/tmp/pr-triage-prs.json"
CACHE_AGE=$(( $(date +%s) - $(stat -f %m "$CACHE_FILE" 2>/dev/null || echo 0) ))
if [ "$CACHE_AGE" -gt 1800 ]; then
  echo "STALE_CACHE"
fi

If STALE_CACHE → re-run the Phase 1 data gathering before continuing.

Filter: exclude Draft PRs and bot PRs (Dependabot, renovate, etc.):

python3 -c "
import json
prs = json.load(open('/tmp/pr-triage-prs.json'))
filtered = [
  p for p in prs
  if not p['isDraft']
  and not any(bot in p['author']['login'].lower() for bot in ['dependabot', 'renovate', 'snyk'])
]
import sys; json.dump(filtered, sys.stdout, indent=2)
" > /tmp/pr-triage-phase4.json

If 0 PRs after filtering → display No reviewable PRs available for worktree (all are drafts or bots). + end Phase 4.

Display grouped by author (use display name if available, fallback to login):

## PRs available for worktree (non-draft)

### Alice Martin (@alice)
  [1] #123 — feat(auth): add OAuth2 support
      Branch: feat/oauth2  |  Size: M  |  CI: clean

### Bob Chen (@bob)
  [2] #456 — fix(api): handle empty response
      Branch: fix/empty-response  |  Size: S  |  CI: dirty ⚠️

Step 4.2 — Selection

Ask via AskUserQuestion (multiSelect):

question: "Which PRs do you want to create a worktree for?"
header: "Worktree Setup"
multiSelect: true
options:
  - label: "All"
    description: "Create worktrees for all {N} listed PRs"
  - label: "[1] #{num} — {title} ({author})"
    description: "Branch: {branch} | Size: {size} | CI: {ci}"
  - label: "None"
    description: "Cancel — return to menu"

If "None" → end Phase 4.

Step 4.3 — Sequential creation

Execution model: Claude runs one bash command per PR, reads its output, updates its internal state (created / existing / failed), then moves to the next. Never a bash loop wrapping all PRs.

For each selected PR, Claude sets variables explicitly then runs:

PR_NUM="123"
BRANCH_NAME="feat/oauth2"
WORKTREE_NAME="${BRANCH_NAME//\//-}"
REPO_ROOT="$(cd "$(git rev-parse --git-common-dir)/.." && pwd)"
WORKTREE_DIR="$REPO_ROOT/.worktrees/$WORKTREE_NAME"

# Already exists?
if [ -d "$WORKTREE_DIR" ]; then
  echo "STATUS:EXISTING:$PR_NUM:$WORKTREE_DIR"
  exit 0
fi

# .gitignore check (fail-fast)
if ! grep -qE "^\.worktrees/?$" "$REPO_ROOT/.gitignore" 2>/dev/null; then
  echo "STATUS:GITIGNORE_MISSING:$PR_NUM"
  exit 1
fi

# Fetch remote branch
if ! git fetch origin "$BRANCH_NAME" 2>/tmp/wt-fetch-$PR_NUM.log; then
  echo "STATUS:FETCH_FAILED:$PR_NUM"
  exit 1
fi

mkdir -p "$REPO_ROOT/.worktrees"

# Create worktree (branch local exists or not)
if ! git branch --list "$BRANCH_NAME" | grep -q "$BRANCH_NAME"; then
  git worktree add -b "$BRANCH_NAME" "$WORKTREE_DIR" "origin/$BRANCH_NAME" \
    2>/tmp/wt-err-$PR_NUM.log
else
  git worktree add "$WORKTREE_DIR" "$BRANCH_NAME" \
    2>/tmp/wt-err-$PR_NUM.log
fi

if [ $? -ne 0 ]; then
  if grep -q "already checked out" /tmp/wt-err-$PR_NUM.log; then
    echo "STATUS:ALREADY_CHECKED_OUT:$PR_NUM"
  else
    echo "STATUS:CREATE_FAILED:$PR_NUM"
  fi
  exit 1
fi

# Optional: symlink node_modules (Node.js projects — avoids reinstall)
[ -d "$REPO_ROOT/node_modules" ] && ln -sf "$REPO_ROOT/node_modules" "$WORKTREE_DIR/node_modules"

# Copy project-specific files listed in .worktreeinclude (if present)
if [ -f "$REPO_ROOT/.worktreeinclude" ]; then
  while IFS= read -r entry || [ -n "$entry" ]; do
    [[ "$entry" =~ ^#.*$ || -z "$entry" ]] && continue
    entry="$(echo "$entry" | xargs)"
    [ -e "$REPO_ROOT/$entry" ] && {
      mkdir -p "$(dirname "$WORKTREE_DIR/$entry")"
      cp -R "$REPO_ROOT/$entry" "$WORKTREE_DIR/$entry"
    }
  done < "$REPO_ROOT/.worktreeinclude"
fi

echo "STATUS:CREATED:$PR_NUM:$WORKTREE_DIR"

Status handling (Claude maintains internal state between PRs):

Status Claude action
STATUS:CREATED:NUM:PATH Add to "created" list
STATUS:EXISTING:NUM:PATH Add to "existing" list → offer pull in Step 4.4
STATUS:FETCH_FAILED:NUM Warn + continue to next PR
STATUS:GITIGNORE_MISSING:NUM Fail-fast: show fix instructions + stop Phase 4
STATUS:ALREADY_CHECKED_OUT:NUM Warn: "Branch already checked out in another worktree. Run git worktree list to locate it."
STATUS:CREATE_FAILED:NUM Warn + continue to next PR

GITIGNORE_MISSING fix instructions:

.worktrees/ is not in .gitignore. Add it to avoid accidentally committing worktree files:
  echo ".worktrees/" >> .gitignore
Then re-run Phase 4.

Step 4.4 — Update existing worktrees

If any STATUS:EXISTING collected, offer a single prompt:

Existing worktrees detected:
  PR #123 — .worktrees/feat-oauth2
  PR #789 — .worktrees/fix-session-leak

- [Pull all] git pull --ff-only in all existing worktrees
- [#123] Pull PR #123 only
- [Skip] Leave as-is

For each selected pull, Claude runs (one command per worktree):

PR_NUM="123"
BRANCH_NAME="feat/oauth2"
WORKTREE_DIR="/abs/path/.worktrees/feat-oauth2"

cd "$WORKTREE_DIR" && git pull origin "$BRANCH_NAME" --ff-only 2>/tmp/wt-pull-$PR_NUM.log
echo "PULL_STATUS:$?:$PR_NUM"

If PULL_STATUS ≠ 0:

⚠️ PR #123 — --ff-only failed (branches have diverged)
   Manual fix: cd .worktrees/feat-oauth2 && git pull --rebase

Step 4.5 — Summary

## Worktrees ready

| PR | Author | Branch | Path | Status |
|----|--------|--------|------|--------|
| #123 | Alice | feat/oauth2 | .worktrees/feat-oauth2 | Created |
| #456 | Bob | fix/empty-response | .worktrees/fix-empty-response | Created |
| #789 | Alice | fix/session-leak | .worktrees/fix-session-leak | Updated (pull) |
| #321 | Carol | feat/chat | .worktrees/feat-chat | Fetch failed ⚠️ |

Note: if a PR modifies package.json, install dependencies manually:
  cd .worktrees/<branch-name> && npm install   # or pnpm/yarn/bun

Next steps:
  cd .worktrees/<branch-name>
  claude

.worktreeinclude convention

Create a .worktreeinclude file at the repo root to list files Phase 4 copies into each new worktree. Useful for local config files not tracked in git:

# .worktreeinclude
.env.local
.env.test
config/local.json

Edge Cases

Situation Behavior
0 open PRs Display No open PRs. + stop
Draft PR Show in table, skip for review unless explicitly selected
Unknown CI Display ? in CI column
Review agent timeout Show partial error, continue with others
gh pr diff empty Skip this PR, notify user
Very large PR (>5000 additions) Warn: "Partial review, diff truncated"
Collaborators API 403/404 Fallback to last 10 merged PR authors
Parallel agents unavailable Run sequential reviews, notify user
Phase 4: .gitignore missing .worktrees/ Fail-fast, show fix instructions, stop Phase 4
Phase 4: branch already checked out Warn with git worktree list hint, skip this PR
Phase 4: stale cache (>30min) Re-fetch PR list before creating worktrees
Phase 4: PR modifies package.json Warn in summary to run install manually
Phase 4: 0 non-draft PRs Display message + end Phase 4

Notes

  • Always derive owner/repo via gh repo view, never hardcode
  • Use gh CLI (not curl GitHub API) except for collaborators list
  • statusCheckRollup can be null → treat as ?
  • mergeable can be MERGEABLE, CONFLICTING, or UNKNOWN → treat UNKNOWN as ?
  • Never post without explicit user validation in chat
  • Drafted comments must be visible BEFORE any gh pr comment

/pr-triage /review-pr
Scope Full PR backlog Single PR
Use when Catching up after accumulation, periodic triage Reviewing a specific incoming PR
Phases 4 (audit + deep review + comments + worktrees) 1 (review only)
Agents Parallel sub-agents per PR Single session
Output Triage table + review reports + GitHub comments + local worktrees Inline review
Validation AskUserQuestion before posting Manual decision

Decision rule: use /pr-triage for backlog triage (5+ PRs), /review-pr for focused review of a single PR. Use Phase 4 when you want to run the code locally rather than just reading the diff.