| name | review-prs |
| description | Review all open PRs in the current repo, triage for quality, close unworthy ones, fix issues, and merge worthy ones sequentially. |
| disable-model-invocation | true |
| allowed-tools | ["Bash(gh *)","Bash(git *)","Bash(*notify-discord.sh*)","Read","Grep","Glob","Edit","Write"] |
Review PRs: Triage, Fix, Close, and Merge
Review every open non-draft PR in the current repo. For each PR: rebase onto the base branch, run quality checks, fix CI/review issues, and merge if worthy — or close it if not. PRs are processed sequentially — after merging one, rebase the next onto the updated base before proceeding. Present a summary and send it to Discord at the end.
Step 1: List Open PRs
Run:
gh pr list --state open --draft=false --json number,title,headRefName,baseRefName,url --limit 100
If no PRs are returned, report "No open non-draft PRs found" and stop.
Save the list. You will need number, title, headRefName, baseRefName, and url for each PR.
Step 2: Get Repo Info
Run:
gh repo view --json nameWithOwner --jq '.nameWithOwner'
Split on / to get OWNER and REPO. You will need these for API calls.
Step 3: Triage All PRs in Parallel
For EVERY PR from Step 1, dispatch a parallel haiku subagent using the Agent tool with model: "haiku". Send ALL Agent tool calls in a single message so they run concurrently.
Each subagent receives the PR number, title, branch name, base branch name, URL, OWNER, REPO, and the full instructions from the "Triage Subagent Instructions" section below.
Each subagent returns a structured report:
pr_number, pr_title, pr_url, base_branch
verdict: one of worthy, closed
reason: short explanation
Not-worthy PRs are closed by the triage subagent (after posting an explanatory comment), so they return closed directly.
After all triage subagents complete, split the results into two lists: worthy PRs and closed PRs.
Step 4: Merge Worthy PRs Sequentially
Process the worthy PRs one at a time, in order (lowest PR number first). For each PR:
4a. Rebase onto Latest Base Branch
The base branch may have changed since triage (due to earlier PRs merging). Fetch and rebase:
git fetch origin <BASE_BRANCH>
Dispatch a subagent with model: "haiku" and isolation: "worktree" with the instructions from the "Rebase Subagent Instructions" section below. This subagent will check out the branch, rebase, and push.
If the rebase subagent reports merge conflicts, dispatch a sonnet subagent with model: "sonnet" using the "Conflict Resolution Subagent Instructions" section below.
If conflicts are unresolvable, comment on the PR and add it to the "Needs Human Attention" list. Move to the next PR.
4b. Wait for CI
After the rebase push, wait for CI to run:
sleep 30
Then poll CI status up to 20 times (sleep 15s each):
gh pr checks <NUMBER>
Stop polling when all checks complete (pass or fail).
4c. Fix CI Failures / Review Comments (if needed)
Check CI status and review state:
gh pr checks <NUMBER>
gh api repos/<OWNER>/<REPO>/pulls/<NUMBER>/reviews --jq '[.[] | select(.state == "CHANGES_REQUESTED")] | length'
If CI is green and no CHANGES_REQUESTED reviews, skip to 4d.
If there are issues, dispatch a sonnet subagent with model: "sonnet" and isolation: "worktree" with the instructions from the "Fix Subagent Instructions" section below. Up to 5 fix-push cycles.
If still failing after 5 cycles, comment on the PR and add it to the "Needs Human Attention" list. Move to the next PR.
4d. Merge
Once CI is green and no blocking reviews:
gh pr merge <NUMBER> --squash --delete-branch
If merge fails (e.g., new conflicts from a race condition), retry the rebase-CI-merge cycle once. If it fails again, add to "Needs Human Attention" and move on.
Record the PR as "Merged" with a short reason.
Step 5: Present Summary
After processing all PRs, present a summary:
## PR Review Summary -- <OWNER>/<REPO>
### Merged (N)
- #<number> -- "<title>" -- <reason> -- <url>
### Closed (N)
- #<number> -- "<title>" -- <reason> -- <url>
### Needs Human Attention (N)
- #<number> -- "<title>" -- <reason> -- <url>
If a section has 0 items, omit it.
Step 6: Notify via Discord
After presenting the summary to the user, send it to Discord via the bundled notification script notify-discord.sh (located in this skill's directory). The script handles Doppler lookup, env-var fallback, payload construction, error reporting, and Discord's 2000-char limit itself — do NOT replicate any of that logic inline.
Locating the script
Try these in order, using the first one that exists:
"$CLAUDE_PLUGIN_ROOT/skills/review-prs/notify-discord.sh" — set by Claude Code when loading plugin-bundled skills.
- Fallback: locate it dynamically:
NOTIFY_SCRIPT="$(ls -t ~/.claude/plugins/cache/*/TytaniumAgentSkills/*/skills/review-prs/notify-discord.sh 2>/dev/null | head -1)"
Pick the most recent match if several plugin versions are cached.
If neither resolves, skip Step 6 entirely and print ⚠ Discord notification skipped: notify-discord.sh not found in plugin cache at the end of your response.
Invoking the script
Pass the full summary markdown as the body via stdin (heredoc is cleanest):
"$NOTIFY_SCRIPT" "PR Review: <OWNER>/<REPO>" <<'SUMMARY'
<the exact markdown summary from Step 5 — merged list, closed list, needs-human list>
SUMMARY
Interpreting the result
- Exit 0 → notification posted, no further action needed.
- Exit 1 → Doppler not installed and no
DISCORD_WEBHOOK env var set. Surface this prominently so the user knows to install Doppler (brew install dopplerhq/cli/doppler && doppler login) or export DISCORD_WEBHOOK.
- Exit 2 → Doppler lookup failed (project/config/secret missing, or not authenticated). Surface the stderr output so the user can diagnose.
- Exit 3 → Discord rejected the webhook (rotated, deleted, or rate-limited). Surface the stderr output.
- Exit 4 →
python3 missing. Surface the error.
In every non-zero case, print a visible ⚠ Discord notification failed: <reason from stderr> line at the end of your response. Do not treat the failure as fatal — the PR work is already complete; the user just loses the push notification for this run.
Triage Subagent Instructions
IMPORTANT: Copy this entire section into the prompt for each triage subagent, substituting the PR-specific values.
You are triaging PR # ("") on branch <BRANCH> targeting <BASE_BRANCH> in /.
URL:
Your job: evaluate whether this PR is worthy of merging. You are NOT merging it — just evaluating. Return a structured verdict.
Check 1: Code Review Quality
Read the full PR diff:
gh pr diff <NUMBER>
Evaluate holistically: Do the changes make sense? Is the code correct, reasonably clean, and not introducing obvious bugs or security issues? Would a competent reviewer approve this?
Check 2: No Duplication of Existing Functionality
Look at what the PR adds (new functions, components, utilities, etc). For each significant addition, search the existing codebase using Grep and Glob to check if similar functionality already exists. Flag if the PR reimplements something that's already there.
Check 3: No CI Naming Changes
First check if any workflow files are changed:
gh pr diff <NUMBER> --name-only | grep '.github/workflows/' || true
If workflow files are changed, check specifically for modifications to name: fields on jobs or workflows. Renaming CI jobs or workflows is an automatic rejection — it breaks existing automations. Adding new workflows or changing non-name fields is fine.
Return Result
If ALL checks pass:
- Return:
{ verdict: "worthy", reason: "<brief positive summary>" }
If ANY check fails:
Rebase Subagent Instructions
You are rebasing PR # branch <BRANCH> onto origin/<BASE_BRANCH> in /.
-
Check out the PR branch:
git checkout <BRANCH>
-
Fetch latest base branch:
git fetch origin <BASE_BRANCH>
-
Attempt rebase:
git rebase origin/<BASE_BRANCH>
-
If rebase succeeds with no conflicts, force-push:
git push --force-with-lease
Report: { result: "rebased" }
-
If rebase fails due to merge conflicts:
git rebase --abort
Report: { result: "conflicts" }
Conflict Resolution Subagent Instructions
You are in a worktree on branch <BRANCH> for PR # in /.
A rebase onto origin/<BASE_BRANCH> failed with merge conflicts. Resolve them:
- Make sure rebase is aborted:
git rebase --abort 2>/dev/null; true
- Merge the base branch instead:
git merge origin/<BASE_BRANCH>
- For each conflicted file, read the file, understand both sides, and resolve the conflict sensibly
- After resolving all conflicts:
git add <resolved files>
git commit -m "chore: merge <BASE_BRANCH> into <BRANCH>"
git push --force-with-lease
Report { result: "conflicts_resolved" } if successful, or { result: "conflicts_unresolvable", reason: "<explanation>" } if not.
Fix Subagent Instructions
You are in a worktree on branch <BRANCH> for PR # in /.
Fix CI failures and/or review comments on this PR. You have up to 5 fix-push cycles.
For each cycle:
- Read CI failure logs:
gh run list --branch <BRANCH> --limit 1 --json databaseId --jq '.[0].databaseId' then gh run view <RUN_ID> --log-failed
- Read unresolved review comments:
gh api repos/<OWNER>/<REPO>/pulls/<NUMBER>/comments
- Fix the issues in the code using Read/Edit/Write tools
- Commit and push:
git add <files you modified>
git commit -m "fix: address CI failures and review feedback"
git push
Stage only the files you modified. Do NOT use git add -A or git add . — this skill runs across arbitrary repos and could accidentally stage sensitive files.
- Wait 30 seconds for CI to start, then poll CI status up to 20 times (sleep 15s each):
gh pr checks <NUMBER>
Stop polling when all checks complete (pass or fail).
- If CI passes and no more unresolved comments, report
{ result: "fixed" } and stop.
- If CI still fails, start the next cycle.
After 5 failed cycles, report { result: "unfixed", reason: "<summary of what was tried and what's still broken>" }.