Agent skill

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.

Stars 3,172
Forks 439

Install this agent skill to your Project

npx add-skill https://github.com/FlorianBruniaux/claude-code-ultimate-guide/tree/main/examples/skills/pr-triage

SKILL.md

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

bash
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)

bash
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:

bash
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:

bash
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 50–200
M 200–500
L 500–1000
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:

bash
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:

bash
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:

bash
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:

bash
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.):

bash
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:

bash
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):

bash
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

Related: /review-pr

/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.

Expand your agent's capabilities with these related and highly-rated skills.

FlorianBruniaux/claude-code-ultimate-guide

eval-skills

Audit all skills in the current project for frontmatter completeness, effort level appropriateness, allowed-tools scoping, and content quality. Produces a scored report with effort-level recommendations for each skill. Use when onboarding to a new project, reviewing skill quality before shipping, or adding effort fields to an existing skill library.

3,172 439
Explore
FlorianBruniaux/claude-code-ultimate-guide

git-ai-archaeology

Analyze AI config evolution in a git repo — first commits per path, monthly distribution, major PRs, maturity phases

3,172 439
Explore
FlorianBruniaux/claude-code-ultimate-guide

design-patterns

Detect, suggest, and evaluate GoF design patterns in TypeScript/JavaScript codebases. Use when refactoring code, applying singleton/factory/observer/strategy patterns, reviewing pattern quality, or finding stack-native alternatives for React, Angular, NestJS, and Vue.

3,172 439
Explore
FlorianBruniaux/claude-code-ultimate-guide

rtk-optimizer

Wrap high-verbosity shell commands with RTK to reduce token consumption. Use when running git log, git diff, cargo test, pytest, or other verbose CLI output that wastes context window tokens.

3,172 439
Explore
FlorianBruniaux/claude-code-ultimate-guide

guide-recap

Transform CHANGELOG entries into social content (LinkedIn, Twitter/X, Newsletter, Slack) in FR + EN. Use after releases or weekly to generate release notes, announcements, social media posts, or recap summaries from guide updates.

3,172 439
Explore
FlorianBruniaux/claude-code-ultimate-guide

talk-stage5-script

Produces a complete 5-act pitch with speaker notes, a slide-by-slide specification, and a ready-to-paste Kimi prompt for AI slide generation. Requires validated angle and title from Stage 4. Use when you have a confirmed talk angle and need the full script, slide spec, and AI-generated presentation prompt.

3,172 439
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results