A meticulous workflow for working through PR review comments one at a time without losing your place. It triages every comment first (MUST_FIX, SHOULD_FIX, PARK, etc.), runs your test suite before and after each fix, commits atomically with references to the reviewer, and replies to threads with commit links or explanations for deferred work. The pre-flight check looks for safeguards in your Makefile or CI config and refuses to proceed if the baseline is broken. It uses GitHub's reviewThreads API as the source of truth, which matters because the simpler comments endpoint can miss threaded conversations. Best for teams that want reproducible, auditable review cycles instead of ad hoc fixes.
npx -y skills add xpepper/pr-review-agent-skill --skill pr-review-loop --agent claude-codeInstalls into .claude/skills of the current project.
Address all open PR review comments one at a time using an opinionated, resumable workflow. Works with comments from any reviewer (human or bot).
Users trigger this skill with prompts like:
gh CLI (preferred). If unavailable, fall back to any tool available to interact with GitHub.Inspect the project for safeguard conventions by checking these files (if they exist):
CLAUDE.md, AGENTS.mdMakefile.github/workflows/README.mdIdentify all required safeguards (tests, compilation, linting, formatting, etc.). Run all of them. If any fail, stop immediately and report — do not proceed on a broken baseline.
# Get current PR details
gh pr status
# View PR with all comments
gh pr view
GitHub surfaces reviewer feedback in two distinct forms — both must be collected:
| Type | Where it lives | Has "resolve"? |
|---|---|---|
| Review threads | Inline comments anchored to a file/line, submitted as part of a review | Yes — resolveReviewThread mutation |
| Issue comments | Regular PR conversation comments (e.g. a summary review posted as a top-level comment) | No — reply serves as closure |
Fetch review threads (keep only unresolved ones):
gh api graphql -f query='
query($owner:String!, $name:String!, $number:Int!) {
repository(owner:$owner, name:$name) {
pullRequest(number:$number) {
reviewThreads(first:100) {
nodes {
id
isResolved
path
line
comments(first:20) {
nodes {
id
databaseId
author { login }
body
createdAt
replyTo { databaseId }
}
}
}
}
}
}
}' -f owner='{owner}' -f name='{repo}' -F number={pr_number} \
| jq '.data.repository.pullRequest.reviewThreads.nodes | map(select(.isResolved == false)) | map(. + {type: "review-thread"})'
Fetch issue comments (top-level PR conversation comments):
gh api repos/{owner}/{repo}/issues/{pr_number}/comments \
| jq '[.[] | {id: .id, author: .user.login, body: .body, created_at: .created_at, type: "issue-comment"}]'
Before triaging, filter out comments by the PR author (they are not reviewer feedback) and known bot accounts. The
authorfield in the transformed output helps with this.
Triage items from both lists. Track which type each item is — it affects how you reply (Step 5f) and close (Step 5g).
Read the triage guide for the specific classification framework and examples. If the guide is not available, use the MUST_FIX / SHOULD_FIX / PARK / OUT_OF_SCOPE / NEEDS_CLARIFICATION classification with your own judgment (see definitions below).
Classify every unresolved comment as: MUST_FIX, SHOULD_FIX, PARK, OUT_OF_SCOPE, or NEEDS_CLARIFICATION.
Triage all comments before acting on any.
If a comment is non-actionable/no-op (e.g. acknowledgment, praise, emoji-only), classify as OUT_OF_SCOPE and reply with a short acknowledgment before resolving.
If a comment's intent is genuinely ambiguous — multiple interpretations exist and each would lead to a meaningfully different change — classify as NEEDS_CLARIFICATION rather than guessing.
If Perplexity or other research tools are available and a comment requires external knowledge to classify (e.g., library idioms, language conventions), use them to inform your decision.
Process in order: all MUST_FIX first, then SHOULD_FIX. Skip PARK and OUT_OF_SCOPE for now (they are handled in the summary).
NEEDS_CLARIFICATION comments: post one focused question as a reply to the thread (see format below), do not resolve the thread, and move on to the next comment. Do not implement anything.
Cascading comments: When multiple comments form a cascade (e.g., changing a trait signature requires updating all impls, callers, and tests), group them into a single commit referencing all comment IDs. Implement the full cascade atomically — applying any single comment without the others would leave the code in an inconsistent state.
For each comment (or group of cascading comments):
5a. Assess complexity
Is this trivial (e.g., rename a function, fix a typo, adjust formatting)?
.pr-review/plan-<comment-id>.md before touching any codeThe plan file must describe:
5b. Run safeguards
Run all safeguards identified in Step 1. They must all pass before you touch any code. If they fail, stop and report.
5c. Fix, park, or ask for clarification
Clarification question format (for NEEDS_CLARIFICATION or mid-assessment uncertainty):
cat > /tmp/pr-review-reply-{comment_id}.md <<'EOF'
Thanks for the feedback! Before I make a change, I want to make sure I understand what you're after:
<one specific, focused question — e.g. "Did you mean to rename this everywhere, or just at the call site?" or "Would you prefer approach A (…) or approach B (…)?">
EOF
jq -n --rawfile body /tmp/pr-review-reply-{comment_id}.md '{body:$body}' > /tmp/pr-review-reply-{comment_id}.json
gh api repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies \
--input /tmp/pr-review-reply-{comment_id}.json
Ask exactly one question. Do not list alternatives unless directly needed to frame the question. Leave the thread unresolved so the reviewer's answer re-surfaces it.
5d. Run safeguards again
Run all safeguards. They must all pass. If they fail, fix the regression before moving on — do not skip this step.
5e. Commit and push
Each comment gets its own focused commit. Reference the comment author in the message body.
git add <changed files>
git commit -m "<conventional commit message describing the fix>
Addresses PR comment from @<reviewer>."
git push
Example commit flow across multiple comments:
# Comment 1: Add missing documentation
git commit -m "docs: add module-level documentation for MetricsRecorder
Addresses PR comment from @reviewer about missing module docs."
git push
# Comment 2: Use Duration instead of i64
git commit -m "refactor: use Duration type for timing parameters
Addresses PR comment from @reviewer - improves type safety."
git push
5f. Reply to the PR comment
Post a reply explaining:
The reply mechanism differs by comment type:
For review thread comments (inline, anchored to a file/line):
cat > /tmp/pr-review-reply-{comment_id}.md <<'EOF'
<reply text>
EOF
jq -n --rawfile body /tmp/pr-review-reply-{comment_id}.md '{body:$body}' > /tmp/pr-review-reply-{comment_id}.json
# POST the reply and capture the response — do NOT run this twice
gh api repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies \
--input /tmp/pr-review-reply-{comment_id}.json > /tmp/pr-review-reply-{comment_id}-response.json
For issue comments (top-level PR conversation comments):
cat > /tmp/pr-review-reply-{comment_id}.md <<'EOF'
<reply text — quote or reference the original comment so context is clear>
EOF
jq -n --rawfile body /tmp/pr-review-reply-{comment_id}.md '{body:$body}' > /tmp/pr-review-reply-{comment_id}.json
gh api repos/{owner}/{repo}/issues/{pr_number}/comments \
--input /tmp/pr-review-reply-{comment_id}.json > /tmp/pr-review-reply-{comment_id}-response.json
Note: this creates a new top-level comment in the PR conversation. It is not threaded under the original — quoting the relevant excerpt is how context is preserved.
5f.1 Verify reply body
Immediately verify what was posted using a GET with the ID from the POST response (never re-run the POST to verify — that creates a duplicate):
For review thread replies:
NEW_REPLY_ID=$(jq '.id' /tmp/pr-review-reply-{comment_id}-response.json)
gh api repos/{owner}/{repo}/pulls/comments/$NEW_REPLY_ID | jq '{id, body, html_url}'
For issue comment replies:
NEW_COMMENT_ID=$(jq '.id' /tmp/pr-review-reply-{comment_id}-response.json)
gh api repos/{owner}/{repo}/issues/comments/$NEW_COMMENT_ID | jq '{id, body, html_url}'
5g. Resolve the comment
For review thread comments — mark as resolved on GitHub.
First, find the thread ID for the comment:
gh api graphql -f query='
query {
repository(owner: "{owner}", name: "{repo}") {
pullRequest(number: {pr_number}) {
reviewThreads(first: 50) {
nodes {
id
isResolved
comments(first: 1) { nodes { body } }
}
}
}
}
}'
Then resolve the thread:
gh api graphql -f query='
mutation {
resolveReviewThread(input: {threadId: "{thread_id}"}) {
thread { id isResolved }
}
}'
Verify resolution:
gh api graphql -f query='
query {
node(id: "{thread_id}") {
... on PullRequestReviewThread {
id
isResolved
}
}
}'
For issue comments — there is no "resolve" mechanism. The reply posted in Step 5f is the closure signal. Note it in the Step 7 summary.
5h. Delete plan file
If a plan file was created, delete it:
rm .pr-review/plan-<comment-id>.md
Stop when no MUST_FIX or SHOULD_FIX comments remain.
If you prefer to batch-resolve all threads at once rather than one by one, you can do so here:
gh api graphql -f query='
query {
repository(owner: "{owner}", name: "{repo}") {
pullRequest(number: {pr_number}) {
reviewThreads(first: 50) {
nodes { id isResolved comments(first: 1) { nodes { body } } }
}
}
}
}' | jq -r '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .id' \
| while read thread_id; do
gh api graphql -f query="mutation { resolveReviewThread(input: {threadId: \"$thread_id\"}) { thread { id } } }"
done
Post a final comment on the PR summarising:
## PR Review Loop — Summary
### Fixed
- [commit abc1234] Renamed `foo` to `bar` (comment by @alice)
- ...
### Parked
- Refactor of X module deferred — tracked in #<issue> (comment by @bob)
- ...
### Rejected
- Suggestion to use Y rejected: project convention is Z (comment by @carol)
- ...
### Awaiting Clarification
- Asked @alice: "Did you mean to rename this everywhere, or just at the call site?" — thread left open
- ...
Omit any section that has no entries.
Use a body file to avoid shell interpolation:
cat > /tmp/pr-review-summary.md <<'EOF'
<summary text>
EOF
gh pr comment {pr_number} --body-file /tmp/pr-review-summary.md
Then verify the posted summary body:
gh pr view {pr_number} --comments
This skill is designed to be interrupted and restarted in a fresh context at any point.
On startup:
isResolved mechanism — if uncertain, re-reading the reply is safer than skipping it.pr-review/plan-*.md file — if found, you are mid-fix on that comment; continue from Step 4bThis means no progress is ever lost. Each fix is committed and pushed before moving on.
.pr-review/ at the repo root (gitignored by the project).
plan-<comment-id>.md — plan for the comment currently in progress (deleted after resolution)cursor/plugins
github/awesome-copilot
alirezarezvani/claude-skills
microsoft/win-dev-skills