Use this skill when the user provides 2 or more PR numbers or URLs to review. Unlike /code-review (which processes PRs sequentially), this skill fans out to parallel agents — one per PR — then merges their findings into a unified summary.
gh auth and repo accessgh auth status. If unauthenticated, stop and tell the user.owner/repo from them. Otherwise run gh repo view --json nameWithOwner -q .nameWithOwner to get the repo from the current directory context. Store this as {owner}/{repo} — it is required for API calls in Step 5.gh repo view {owner}/{repo} to confirm read access. If it fails, stop and tell the user./code-review instead.Use gh and the GitHub search API to find open PRs by the user’s teammates that are awaiting review. Run these steps:
Resolve the org: extract the org from the {owner}/{repo} resolved in Step 0 (e.g. ProjectAussie).
gh api 'orgs/{org}/members' --paginate --jq '.[] | select(.login | test("{name}"; "i")) | .login'
gh search prs --state open --author {login} --json number,title,url,author,repository --limit 20
Run one search per author. Collect all results.
Filter to actionable PRs only — for each PR, fetch its review status and check whether the current user has already reviewed:
a. Get the current user’s login:
gh api user --jq .login
b. Get PR metadata:
gh pr view <number> -R <owner/repo> --json reviewDecision,isDraft,reviewRequests
c. Get the current user’s most recent review on this PR (if any) and the latest commit date:
gh api repos/{owner}/{repo}/pulls/{number}/reviews --jq '[.[] | select(.user.login == "{current_user}")] | sort_by(.submitted_at) | last | {state: .state, submitted_at: .submitted_at}'
gh api repos/{owner}/{repo}/pulls/{number}/commits --jq 'last | .commit.committer.date'
d. Keep only PRs where all of the following are true:
isDraft: falsereviewDecision is NOT excluded — include all values: "REVIEW_REQUIRED", "" (empty), "APPROVED", and "CHANGES_REQUESTED" (someone else may have requested changes — you still need to review)e. Skip PRs where the current user is already waiting on the author:
state is CHANGES_REQUESTED, ANDsubmitted_at timestamp (meaning no new commits since the review)reviewRequests contains the current user’s loginREVIEW_REQUIRED but no individual review request for you — these are waiting on a teamPresent the list to the user grouped by author and bucket, with URLs. If any PRs were skipped because the current user is waiting on the author, list them separately:
Skipped (waiting on author after your CHANGES_REQUESTED):
- {owner}/{repo}#{number} — {title} — last reviewed {date}, no new commits since
Then ask: “Should I review all of these, or select a subset? I can also include the skipped PRs if you want to re-review them.”
gh pr view <number> --json number,title,state,isDraft,baseRefName,headRefName,createdAt to get metadata.| Relationship | Definition | Review strategy |
|---|---|---|
| Stacked | Each PR targets the previous PR’s branch | Review in merge order; pass raw diff context forward |
| Parallel | Multiple PRs for the same feature, split by concern | Review independently; flag integration risks |
| Batch / Unrelated | Unrelated changes reviewed together (e.g. release batch) | Review fully independently |
Spin up one Agent per PR using subagent_type: general-purpose. Name each agent after a unique American outlaw from the 1800s–1900s (e.g. Jesse James, Belle Starr, Black Bart, Dutch Schultz, Pretty Boy Floyd, Billy the Kid, Bonnie Parker). Names must be unique per session — do not reuse a name even if reviewing many PRs.
Each agent receives a self-contained prompt with:
{owner}/{repo}), and is_draft flag.gh pr diff <number> — save this output; it is the authoritative source of changed lines for inline comment line numbersgh pr view <number> --comments--comments does NOT include formal reviews:
gh api repos/{owner}/{repo}/pulls/{number}/reviews --jq '.[] | {user: .user.login, state: .state, body: .body}'
This returns APPROVED, CHANGES_REQUESTED, COMMENTED, DISMISSED, or PENDING per reviewer. A DISMISSED review means a maintainer overrode it — note who dismissed and why.
gh api repos/{owner}/{repo}/pulls/{number}/comments --jq '.[] | {user: .user.login, path: .path, line: .line, body: .body, created_at: .created_at}'
gh pr checks <number> — note that this only shows current run state; to assess whether failures are pre-existing, also run gh pr checks <base-branch> or gh pr checks $(gh pr view <number> --json baseRefName -q .baseRefName) for comparison. If base-branch checks are also failing, mark ci_failures_introduced_by_pr: false.gh pr diff <prior-number> from the prior agent — passed verbatim, not summarized — so the agent understands what the prior layer changed and can attribute findings to the correct PR.is_draft: true, the agent must still review and produce findings, but must set verdict: "COMMENT" unconditionally. It should post inline comments on Github (authors expect feedback on drafts) but the summary must clearly label the PR as a draft.Run all agents in parallel (single message, multiple tool calls) unless the PRs are stacked — in that case, run them sequentially in merge order so each agent gets the prior layer’s raw diff as context.
Each agent must return a JSON object with exactly these fields:
{
"pr": 123,
"title": "...",
"verdict": "APPROVE",
"is_draft": false,
"ci_status": "passing",
"ci_failures_introduced_by_pr": false,
"stacked_context_diff": "<raw output of gh pr diff for this PR — included only for stacked PRs, to pass forward to the next agent>",
"findings": [
{
"severity": "BLOCKER",
"file": "path/to/file.ts",
"line": 42,
"body": "..."
}
],
"prior_discussions": [
{
"author": "<github username of the person who raised the concern>",
"summary": "brief description of the prior comment or concern",
"status": "accepted | unresolved | addressed_in_code",
"original_severity": "BLOCKER | HIGH | MEDIUM | LOW | QUESTION",
"file": "path/to/file.ts (optional — only if the concern was about a specific file)",
"line": 42,
"reasoning": "why this status was assigned — e.g. 'reviewer replied OK to defer' or 'no response from reviewer after author acknowledged'"
}
],
"summary": "2–4 sentence summary of what the PR does and overall quality"
}
Valid verdict values: "APPROVE", "REQUEST_CHANGES", "COMMENT".
Line number constraint: every finding with a line value must reference a line that actually appears in the diff output from gh pr diff. Do not invent or approximate line numbers — a line number not in the diff will cause the GitHub API to reject the comment with a 422 error.
Each agent evaluates:
Agents must complete this section before forming a verdict. Omitting it is a review failure — populate the prior_discussions array in the Output Contract even if empty.
Use all three data sources together — each one shows different things:
| Source | What it shows | Command |
|---|---|---|
| PR conversation comments | Top-level discussion | gh pr view <number> --comments |
| Formal review state | APPROVED, CHANGES_REQUESTED, DISMISSED per reviewer |
gh api repos/{owner}/{repo}/pulls/{number}/reviews |
| Inline review thread comments | Line-level reviewer concerns | gh api repos/{owner}/{repo}/pulls/{number}/comments |
Scan reviewer comment threads (not PR description, not commit messages) for phrases that indicate deferral: “we’ll fix this later”, “out of scope for this PR”, “follow-up ticket”, “known issue”, “accepted risk”, or similar.
Do not treat the following as deferral signals:
TODO comments in the code diff (those are code annotations, not reviewer deferrals)Prior reviewer comments do not use this skill’s severity taxonomy. When a prior concern has no explicit severity, assign one based on impact:
BLOCKER or HIGHMEDIUMLOWDocument your severity assignment and reasoning in the reasoning field of the prior_discussions entry.
| Status | Criteria |
|---|---|
| accepted | The reviewer who raised the concern (not the author) explicitly accepted the deferral — e.g., replied “OK to defer”, “fine for now”, submitted a new APPROVED review after discussion. Author acknowledgment alone is not sufficient. If the reviewer went silent after the author acknowledged, classify as unresolved — reviewer silence does not equal acceptance. |
| addressed_in_code | The concern was addressed by a code change. You must verify this — cross-reference the PR diff to confirm the fix is actually present. A comment saying “fixed” or “done” without a corresponding code change means the status is unresolved, not addressed_in_code. |
| unresolved | Anything else: no reply, author disputed it without reviewer resolution, reviewer went silent, or the formal review state is still CHANGES_REQUESTED and not DISMISSED. |
CHANGES_REQUESTED and has not been DISMISSED: that review is still active. Check whether the specific concerns raised in that review have been addressed in code or accepted by the reviewer.DISMISSED: note who dismissed it (the reviewer themselves, or a maintainer). A maintainer-dismissed review without a replacement approval should still be surfaced as a prior discussion — it may indicate an override that the human reviewer should see.unresolved prior concern with original_severity of BLOCKER prevents an APPROVE verdict. This is absolute.unresolved HIGHs should result in REQUEST_CHANGES unless you have strong evidence the concern is stale (e.g., the code it referenced no longer exists in the diff). If downgrading a stale concern, document why in the reasoning field.is_draft: true, the verdict remains COMMENT regardless of unresolved prior concerns. However, the prior discussions must still be surfaced and classified — the draft status overrides the verdict, not the analysis.Populate the prior_discussions array in the Output Contract for every prior concern found. Include a dedicated “Prior Discussions” subsection in the review output listing each item, its status, original severity, and your reasoning.
| Level | Meaning |
|---|---|
| BLOCKER | Must be fixed before merge. Correctness bug, security vuln, broken contract, CI failure introduced by this PR. |
| HIGH | Serious design or reliability issue. Should fix; discuss if deferring. |
| MEDIUM | Real improvement, not blocking. Author should address or explicitly accept risk. |
| LOW / NIT | Style, naming, minor cleanup. Don’t block merge over these. |
| QUESTION | Unclear intent — ask before judging. |
Do not manufacture findings to look thorough. If the code is good, say so.
Before posting anything to GitHub, write a draft markdown file and present it to the user for review and editing.
Write all findings to review-draft-{timestamp}.md (e.g. review-draft-2026-04-14T15-44.md) in the current working directory. The file has two sections per PR: a Changes Summary and the Proposed Comments.
File format:
# Review Draft — {date}
---
## {owner}/{repo}#{number} — {title}
**Author:** {login} | **CI:** passing / failing / pending
**Review Event:** `APPROVE` / `REQUEST_CHANGES` / `COMMENT` ← _edit this to control the formal GitHub review event submitted for this PR_
### Changes Summary
<3–6 sentence plain-English description of what the PR actually does — not the PR description copy-pasted, but your own read of the diff. What files changed, what behavior changed, what was added or removed.>
### Proposed Comments
#### Prior Discussions
| Author | Summary | Status | Orig. Severity | Reasoning |
|--------|---------|--------|---------------|-----------|
| @reviewer-login | brief description of concern | accepted / unresolved / addressed_in_code | BLOCKER / HIGH / MEDIUM / LOW | why this status was assigned |
#### Inline Comments
| File | Line | Severity | Comment |
|------|------|----------|---------|
| `path/to/file.ts` | 42 | BLOCKER | Farty Bobo says: ... |
---
## Cross-PR Summary (for human reviewer only — NOT posted to GitHub)
> **Relationship:** Stacked / Parallel / Batch
> **Overall:** ALL APPROVE / MIXED / ALL REQUEST_CHANGES
>
> | PR | Title | Review Event | Blockers | Highs |
> |----|-------|-------------|----------|-------|
> | #123 | ... | APPROVE | 0 | 1 |
> | #124 | ... | REQUEST_CHANGES | 1 | 0 |
>
> **Integration Concerns:** ...
Include every PR in the file, in order. Leave the cross-PR summary at the bottom.
Tell the user:
“Draft written to
{filename}. Open it, make any edits you want — remove findings, soften wording, add context. TheReview Eventfield on each PR controls the formal GitHub review action (APPROVE / REQUEST_CHANGES / COMMENT) — change it if you disagree with my recommendation. Tell me to post when ready, or say ‘post as-is’.”
Do not proceed to Step 6 until the user explicitly says to post. This gate is not optional — the whole point is to let the human adjust before anything hits GitHub.
Re-read the (possibly edited) draft file before posting — use its content as the source of truth for what gets posted, not the original agent outputs. Then delete the draft file after posting completes.
After human approval, re-read the draft file. For each PR:
Parse the Review Event field from the draft file header. Valid values: APPROVE, REQUEST_CHANGES, COMMENT. The human may have changed this from the agent’s original recommendation — always use the value in the file, not the agent’s original verdict.
Post inline comments with the review event using gh api. Use side: "RIGHT" for all inline comments. Only post comments for lines confirmed to exist in the diff. Use the content from the approved draft file — not the raw agent output.
gh api repos/{owner}/{repo}/pulls/{number}/reviews \
--method POST \
--field body="" \
--field event="{REVIEW_EVENT}" \
--field "comments[][path]=path/to/file.ts" \
--field "comments[][line]=42" \
--field "comments[][side]=RIGHT" \
--field "comments[][body]=Farty Bobo says: <finding>"
Where {REVIEW_EVENT} is the value read from the draft file’s Review Event field for that PR (APPROVE, REQUEST_CHANGES, or COMMENT).
APPROVE or REQUEST_CHANGES, submit the review without comments:gh api repos/{owner}/{repo}/pulls/{number}/reviews \
--method POST \
--field body="Reviewed by Farty Bobo" \
--field event="{REVIEW_EVENT}"
Post reviews for all PRs.
The agent populates the Review Event field in the draft file as its initial recommendation. The human can override it before posting. These are the rules for the agent’s initial recommendation:
APPROVE — no BLOCKERs or HIGHs (including unresolved prior concerns with original severity BLOCKER or HIGH), CI passing (or failures pre-existing on base)REQUEST_CHANGES — one or more BLOCKERs or HIGHs introduced by this PR, OR one or more unresolved prior concerns with original severity BLOCKER or HIGHCOMMENT — draft PR (overrides all other events — see below), questions only, or observations with no blocking concernsDraft PR precedence: if is_draft: true, the review event is always COMMENT regardless of unresolved prior concerns. Prior discussions are still analyzed and surfaced in the output — the draft status overrides the event, not the analysis.
Human override: Whatever value the human leaves in the Review Event field when they approve the draft is what gets submitted to the GitHub API. The agent’s recommendation is just a starting point.
After posting all reviews:
/resolve-ci-failures.