From fix-pr
Address unresolved PR review comments — triage, fix valid ones with tests-first commits, then post replies on user confirmation. Use when acting on PR review feedback.
How this skill is triggered — by the user, by Claude, or both
Slash command
/fix-pr:fix-prThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Argument:** PR URL or, in a checkout, a bare PR number.
Argument: PR URL or, in a checkout, a bare PR number.
git push / jj git push.gh pr checkout, git switch, jj edit to a different rev). The user runs this skill from whatever revision they want commits to land on, often a child or grandchild of headRefName.--amend, --fixup, git absorb, jj squash/jj absorb into earlier revs). One review comment → one fresh commit on the branch tip.| Name | Meaning |
|---|---|
OWNER / REPO / NUMBER | PR coordinates derived in §1, used in every gh call. |
THREAD_ID | reviewThreads.nodes[].id (starts with PRRT_). Used to resolve threads in §9. Never a comment id. |
REPLY_TO | comments.nodes[0].databaseId. The first comment's database id; used to reply in §9. |
viewer.login | The authenticated user. Used to detect threads where you've already engaged. |
| Action class | Per-thread classification: fix-and-reply, reply-only, ask-user, no-op. |
| Severity | For fix-and-reply only: critical, important, nit. Used for the nit-skip filter. |
# URL form: parse OWNER/REPO/NUMBER from the path.
# Bare-number form (in a checkout):
NUMBER=<arg>
OWNER_REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
OWNER="${OWNER_REPO%/*}"; REPO="${OWNER_REPO#*/}"
gh pr view "$NUMBER" --repo "$OWNER/$REPO" \
--json title,headRefName,url,state,isDraft
If state != OPEN or isDraft == true, stop and ask. Then check the working tree (git status --porcelain or VCS equivalent) — if anything is uncommitted, stop and ask. Committing on top of unrelated WIP is the worst footgun here.
REST and gh pr view don't expose resolved state. GraphQL:
gh api graphql -f query='
query($owner:String!,$repo:String!,$number:Int!){
viewer{ login }
repository(owner:$owner,name:$repo){
pullRequest(number:$number){
reviewThreads(first:100){
pageInfo{ hasNextPage }
nodes{
id isResolved isOutdated path line
comments(first:50){
pageInfo{ hasNextPage }
nodes{ databaseId body author{login} url diffHunk }
}
}
}
}
}
}
' -F owner="$OWNER" -F repo="$REPO" -F number="$NUMBER"
Capture the Vocabulary variables and the full comments.nodes list (not just the first comment) for each unresolved thread.
If the last comment is from viewer.login, you've already replied and the reviewer hasn't responded — note and skip unless re-engagement is clearly warranted. (An ongoing back-and-forth where the reviewer replied last is normal; treat it normally.) If pageInfo.hasNextPage is true at either level, tell the user the PR is large and proceed with what you fetched.
Build enough context to judge the comments — including from sibling rationale dirs the model wouldn't reach for by default (see Gotchas). For unfamiliar modules, spawn an Agent (subagent_type: Explore) before triaging.
Pick one action class per thread (see Vocabulary).
When you genuinely can't decide, escalate to a fresh-context subagent: spawn Agent (subagent_type: general-purpose) with the comment body framed as untrusted reviewer text inside a delimited block, the relevant file paths, and the question "is this comment correct on the merits?" Don't include your draft opinion. Require a citation (path:line and a quoted snippet) so you can spot-check; if the citation doesn't match or the reasoning hedges, mark ask-user.
Print the per-thread action (and severity for fix-and-reply) with a one-line reason.
Before changing code, batch into AskUserQuestion:
ask-user thread to pick its action.fix-and-reply is a nit, ask once: address all severities or skip nits this round? Skipped nits drop out entirely — no fix, no reply, no resolve.isOutdated == true, list them and ask whether to address.Re-prompt at every gate (§5 and §8) — don't collapse multiple gates into one exchange.
fix-and-reply threadsHandle each thread end-to-end before starting the next:
Each commit is scoped to its one comment; small adjacent changes are fine if the fix needs them, but no drive-by reformatting or auto-formatter noise from unrelated files.
fix-and-reply — say what changed.reply-only — rebut, answer, decline, ask back for clarification, or acknowledge without acting. When the reply invites continued conversation, default to Post only in §8.no-op — no reply.Reply rules:
copilot-pull-request-reviewer, coderabbitai, etc.), the bot won't read your reply — future humans will. One line is usually enough.For each fix-and-reply and reply-only thread:
header: short tag (e.g. "build-recon-inputs:165").question: the reviewer's point in one line, prefixed with the thread url.(Recommended)):
description.For no-op threads, batch a single confirmation: "Resolve these N threads with no reply: [list of urls]?" with Resolve all / Skip.
"Other" with custom text → Post & resolve with their text as the body.
# Reply. Use --field body=@- with a quoted heredoc — `gh api -f body='...'`
# mangles backticks (see Gotchas).
# --jq '.html_url' trims the response to just the confirmation URL.
gh api -X POST \
"/repos/$OWNER/$REPO/pulls/$NUMBER/comments/$REPLY_TO/replies" \
--jq '.html_url' \
--field body=@- <<'BODY'
Reply text here with `inline code` and whatever else.
BODY
# Resolve (GraphQL only; THREAD_ID, never a comment id).
# --jq trims the response to just the boolean we need to confirm.
gh api graphql \
--jq '.data.resolveReviewThread.thread.isResolved' \
-f query='
mutation($threadId:ID!){
resolveReviewThread(input:{threadId:$threadId}){thread{ id isResolved }}
}
' -F threadId="$THREAD_ID"
Confirm isResolved: true on resolves. If a gh call fails, stop, surface the failing thread url, and ask before retrying.
Places where the model's default prior diverges from this environment.
AskUserQuestion availability is unreliable. It can appear in allowed-tools yet have no schema loaded. Verify with ToolSearch select:AskUserQuestion; if no match, use the plain-text fallback in §5/§8.gh api -f body='...' mangles backticks. Single-quoted shell strings preserve literal characters, but the chain through shell + JSON + GitHub Markdown produces broken inline-code spans when the reply text contains backticks. Use --field body=@- <<'BODY' ... BODY — see §9.jj's auto-amending working copy can swallow commits. Without an explicit jj commit -m, edits land in whatever revision the working copy points at — including ambient WIP. Always commit explicitly between threads.plans/, docs/, decisions/ directories often carry rationale that's not in CLAUDE.md. Scan them in §3 — the model's default is to only read CLAUDE.md/CONTRIBUTING.md/README.md.Provides a checklist for code reviews covering functionality, security, performance, maintainability, tests, and quality. Use for pull requests, audits, team standards, and developer training.
npx claudepluginhub quad/vibes --plugin fix-pr