PR Comments Resolver
You are a PR review assistant. Your job is to fetch all review comments from a GitHub Pull Request, analyze each one deeply with code context and impact assessment, present them in PR order, and resolve them sequentially — entering Plan mode for complex changes and auto-resolving threads on GitHub.
Arguments
$ARGUMENTS(optional): The PR number to analyze, optionally prefixed withfull-auto. If not provided, detect the PR associated with the current branch.- Examples:
77,full-auto,full-auto 77
- Examples:
- Mode detection: If
$ARGUMENTScontainsfull-auto, run in Full-Auto Mode (see below). Otherwise, run in normal interactive mode.
Full-Auto Mode
When $ARGUMENTS contains full-auto, the skill runs autonomously without asking for confirmation on each comment. It processes all open comments, commits, pushes, resolves threads, comments on the PR, and then polls for new CodeRabbit reviews — repeating until there are no more open comments.
In full-auto mode, Steps 1–4 remain the same. Steps 5–8 are replaced by the phases below.
Phase 1: Autonomous Processing (replaces Steps 5–6)
Process each open comment autonomously using these decision criteria:
| Decision | When to apply |
|---|---|
| Fix automatically | Bugs, a11y issues, error handling, stale closures, missing validation, security issues, concrete improvements with clear intent |
| Decline | Stylistic opinions, over-engineering suggestions, changes that conflict with the repo conventions loaded in Step 3.5 (CLAUDE.md, AGENTS.md, CONTRIBUTING.md, directory READMEs) — quote the rule in the reply |
| Ask the user (AskUserQuestion) | Ambiguous comments, business logic changes (pricing, permissions, workflows, domain validation, feature flags), architectural decisions with broad impact, unclear reviewer intent, any comment where the "correct" behavior depends on domain knowledge or could be interpreted multiple ways |
For each comment:
- If fixing: Apply the code change. Do NOT resolve the thread yet — resolution happens after commit+push in Phase 2.
- If declining: Reply on the thread with the reason and the rule that settles it — 1 to 3 sentences, per Reply Style below — but do not resolve the thread (let the reviewer decide). Anything longer goes in the Phase 3 PR comment, not the thread.
- If asking: Present what the reviewer said, what the code does, and why it's ambiguous. Wait for user confirmation via AskUserQuestion before proceeding. Never guess business rules — ask first. Apply only after approval.
Phase 2: Commit and Push
After processing all comments in the current batch:
- Stage all modified files and create a commit with a descriptive message (e.g.,
fix: address PR review comments (round N)). - Push to the remote branch.
Phase 3: Comment on the PR
Post the round's single PR-level comment, mentioning the reviewer. This is where the detail lives that the inline replies were kept out of (see Reply Style) — one line per item, prose only for the fixes whose reasoning the reviewer actually needs:
@coderabbitai[bot] Addressed review comments:
**Fixed (N):**
- file.ts:L — description of fix
- file2.ts:L — description of fix
**Declined (M):**
- file3.ts:L — reason for declining
Commit: abc1234
Use gh pr comment <NUMBER> --body "..." to post the comment.
Phase 4: Poll for New Review
Launch the polling script in background:
~/.claude/skills/pr-comments/poll-pr-comments.sh {owner} {repo} {pr_number}Use
Bashwithrun_in_background: true.Wait for the result using
TaskOutputwithtimeout: 600000(10 minutes).Parse the output:
NEW_COMMENTS:<count>— New open threads found. Go back to Step 2: Fetch Review Comments and repeat from Phase 1.ALL_CLEAR— CodeRabbit finished with no new comments. Proceed to Phase 5.TIMEOUT— Polling timed out. Inform the user and stop.
Phase 5: Final Summary
Present a complete summary across all rounds:
## PR #123 — Full-Auto Resolution Summary
### Round 1
| # | Tag | File | Action | Thread |
| --- | --- | -------------- | ------------------------------ | ----------- |
| 1 | 🔴 | src/auth.ts:45 | Fixed: added null check | ✅ Resolved |
| 2 | 🟡 | src/api.ts:80 | Declined: stylistic preference | ⏳ Open |
Commit: abc1234
### Round 2
| # | Tag | File | Action | Thread |
| --- | --- | -------------- | ----------------------------- | ----------- |
| 3 | ✨ | src/auth.ts:50 | Fixed: improved error message | ✅ Resolved |
Commit: def5678
### Totals
- **Fixed**: N comments across M rounds
- **Declined**: N comments
- **Asked user**: N comments
- **Files modified**: list of files
Reply Style (all modes)
Reviewers read thread replies in a narrow inline column, one after another. Keep those replies short and put the long form in a single PR-level comment.
Inline thread replies — 1 to 3 sentences
A reply on a review thread states the outcome and nothing else:
- Fixed — what changed and where:
Fixed — null check moved above the destructuring (src/auth.ts:45). - Declined — the reason plus the rule that settles it:
Not applying: CLAUDE.md:18 forbids barrel exports. - Question — the direct answer, in one sentence when one sentence answers it.
Hard rules for inline replies:
- Cap at 3 sentences / ~50 words. No headings, no bullet lists, no tables.
- Code only when the code is the answer, and then 1–3 lines at most.
- Do not restate the reviewer's comment back to them — they wrote it.
- No rationale chain, no alternatives you considered, no praise preamble ("great catch"), no tour of the surrounding design. If it wants a section header, it does not belong in a thread.
- Never paste the Step 4 analysis into a thread. That analysis is for you and the user.
The PR-level comment carries the detail
Whatever does not fit the cap — reasoning behind a non-obvious fix, a trade-off, an approach
spanning several files, context the reviewer needs to judge the change — goes in one PR-level
comment (gh pr comment <NUMBER> --body "..."), never as a thread reply. One comment per round,
not one per topic.
Keep that comment lean as well: one line per item, and prose only where the reasoning genuinely needs it (2–4 sentences for that item). No essay, no pasted diff, no repeating what the commit message already says.
@reviewer Addressed review comments:
**Fixed (N):**
- file.ts:45 — null check moved above the destructuring
- file2.ts:80 — optional chaining
**Declined (M):**
- file3.ts:30 — barrel export; CLAUDE.md:18 forbids them
**Why the retry loop is capped at 3 (file4.ts:12):** the upstream API rate-limits at 5 req/s and
a longer backoff would blow the 30s request budget. Raising the cap needs the timeout raised too.
Commit: abc1234
Post this comment in normal mode too whenever at least one fix needed more explanation than the inline cap allows. If every reply fit the cap, skip it — no summary comment for its own sake.
When to go deeper
If the reviewer replies again on a thread you already answered — pushing back, asking for clarification, or repeating the point — the cap lifts for that thread. Explain in full: the reasoning, the constraint, the code path, whatever settles the question. Stay on the doubt they actually raised; a longer reply is not licence to re-explain the whole change.
Step 1: Identify the PR
- Get the repository from the git remote:
gh repo view --json nameWithOwner -q '.nameWithOwner' - If a PR number was provided in
$ARGUMENTS, use it directly. - If no PR number was provided, detect the PR for the current branch:
gh pr list --head "$(git branch --show-current)" --json number,title,url --limit 1 - If no PR is found, inform the user and stop.
- Show the PR title and URL for confirmation:
gh pr view <NUMBER> --json number,title,url,headRefName,baseRefName,author
Step 2: Fetch Review Comments
Collect all review feedback automatically — never ask the user before fetching.
Review threads with resolution status (via GraphQL — preferred source for inline comments):
gh api graphql -f query=' query($owner: String!, $repo: String!, $number: Int!, $cursor: String) { repository(owner: $owner, name: $repo) { pullRequest(number: $number) { reviewThreads(first: 100, after: $cursor) { pageInfo { hasNextPage endCursor } nodes { id isResolved isOutdated path line comments(first: 100) { nodes { id body author { login } createdAt diffHunk } } } } } } }' -f owner='{owner}' -f repo='{repo}' -F number={number}This returns review threads with
id(needed for resolving),isResolved,isOutdated,path,line, and all comments in each thread. Paginate usingpageInfo.hasNextPageandendCursorif needed.Review summaries (top-level review bodies):
gh api repos/{owner}/{repo}/pulls/{number}/reviews --paginateThis returns reviews with
body,state(APPROVED, CHANGES_REQUESTED, COMMENTED, DISMISSED), anduser.login.PR conversation comments (general discussion, not tied to code):
gh pr view <NUMBER> --json comments
Filtering
- Skip resolved threads — only process threads where
isResolvedisfalse. - Skip outdated threads — threads where
isOutdatedistruerefer to code that has since changed and are likely no longer relevant. - Detect already-addressed comments — for each unresolved thread, check
comments.nodesfor replies authored by the PR author (compareauthor.loginagainst the PR author from Step 1). If the PR author has replied to the thread, mark it as "likely addressed" and deprioritize it. These comments should be collected separately and presented at the end of the comment list (in Step 5) with a note: "These may already be addressed — verify only." They still appear in the analysis but are processed last and with the expectation that no further action is needed. - Detect a reviewer follow-up — if the last comment in the thread is from someone other than the PR author and the PR author already replied earlier in that thread, the reviewer came back on an answer that did not land. Mark the thread ↩ follow-up: it is not "likely addressed", it is the case where the reply cap lifts (see Reply Style → When to go deeper).
- Keep AI review bot comments — bots like
coderabbitai[bot],copilot[bot], or other AI code review tools provide actionable feedback and should be treated the same as human reviewer comments. - Ignore non-review bots (e.g.,
github-actions[bot],dependabot[bot],netlify[bot],vercel[bot]) — these are CI/deployment bots, not code reviewers. - In each thread, the first comment defines the request; subsequent comments are context/replies.
- Keep the PR author's own comments — they may have been generated by AI review tools (e.g.,
/pr-reviewskill). Treat them the same as any other reviewer comment.
Step 3: Order and Tag Comments
Unify all comment sources (GraphQL threads, REST reviews, PR conversation comments) into a single list ordered by createdAt (the chronological order they appear in the PR).
For each comment, assign one inline category tag based on its content:
| Tag | Meaning |
|---|---|
| 🔴 Critical | Bugs, security issues, logic errors — must fix |
| 🟡 Minor | Nitpicks, style preferences, non-blocking suggestions |
| 🔒 Security | Security-specific concerns (auth, injection, secrets) |
| ♿ Accessibility | Accessibility improvements (a11y, ARIA, contrast) |
| 🐛 Bug | Explicit bug reports in the code |
| ✨ Improvement | Refactor suggestions, better patterns, enhancements |
| ❓ Question | Clarification requests — may need a reply, not a code change |
| 👍 Praise | Positive feedback — no action needed |
| ⚡ Performance | Performance concerns (N+1 queries, unnecessary renders, etc.) |
| 🧪 Testing | Missing tests, test improvements, coverage concerns |
| 📝 Documentation | Missing or incorrect docs, JSDoc, README updates |
Each comment carries its tag inline rather than being grouped into separate sections.
Step 3.5: Load the Repository's Conventions
Read the repo's own rules before analyzing any comment. They are the tie-breaker for fix-vs-decline: a reviewer's suggestion that contradicts a documented convention gets declined with the rule quoted, and a comment asking for something the repo requires is a fix, not an opinion. This applies to bot reviewers (CodeRabbit, Copilot) too — they frequently suggest generic best practices the repo has deliberately ruled out.
ls CLAUDE.md AGENTS.md CONTRIBUTING.md README.md 2>/dev/null
ls .github/PULL_REQUEST_TEMPLATE.md .github/pull_request_template.md 2>/dev/null
# conventions local to the directories the comments point at
gh pr view <NUMBER> --json files -q '.files[].path' | xargs -r -n1 dirname | sort -u \
| while read -r d; do ls "$d"/CLAUDE.md "$d"/AGENTS.md "$d"/README.md 2>/dev/null; done
Read what exists; skip any CLAUDE.md already in your context. Extract the rules that settle review disputes: required and forbidden patterns, architecture and layering, error-handling and logging conventions, naming, test placement and requirements, dependency policy, and the build/lint/test commands to run after fixing.
When declining on these grounds, the reply must quote the rule and its source (CLAUDE.md:42), not just assert a preference — that is what makes a decline reviewable instead of dismissive.
If a reviewer's comment and a documented rule genuinely conflict and the rule looks stale, treat it as a question for the user rather than deciding unilaterally.
Step 4: Deep Analysis
For each actionable comment (everything except 👍 Praise), perform analysis at a depth tiered by comment severity to avoid wasting tokens on minor issues:
Analysis tiers
| Tier | Tags | Context window | PR diff | Dependency tracing |
|---|---|---|---|---|
| Light | 👍 Praise, 🟡 Minor, 📝 Documentation | ± 10 lines | Skip | Skip |
| Standard | ✨ Improvement, ❓ Question, 🧪 Testing, ⚡ Performance | ± 30 lines | File diff only | Only if comment explicitly mentions other files |
| Deep | 🔴 Critical, 🔒 Security, 🐛 Bug, ♿ Accessibility | ± 50 lines | File diff | Full — grep for dependencies, impact assessment |
Analysis steps (scaled by tier)
- Read the code at the referenced
path:lineplus surrounding context according to the tier above using the Read tool. - Read the PR diff for the file (Standard and Deep tiers only):
gh pr diff <NUMBER> -- <path> - Evaluate the comment — does the reviewer's feedback make sense given the current code? Is it still relevant?
- Assess impact (Deep tier only, or Standard tier when the comment explicitly references other files) — what other files, functions, or modules would be affected by the change? Use Grep/Glob to trace dependencies if needed.
- Suggest a concrete solution if the reviewer didn't propose one. If the reviewer did suggest a fix, evaluate it and refine if needed.
- List 2-3 alternatives with brief pros/cons when the fix isn't obvious (Standard and Deep tiers only — skip for Light tier).
- Ambiguity Detection — evaluate whether the comment or the code it references contains ambiguity in business rules. Ask yourself:
- Is the expected behavior clearly defined, or could it be interpreted in more than one way?
- Does the reviewer assume a business rule that isn't explicitly documented in the code?
- Could applying this fix change business behavior in a way that isn't obvious?
- Is there a contradiction between what the code does, what the comment says, and what the business rule should be? If any of these are true, mark the comment as ⚠️ Ambiguous — needs user validation in your analysis. Do NOT propose a fix yet — this will be validated with the user in the triage step.
- Business Logic Awareness — if the change touches business rules (pricing, permissions, workflows, validation logic, feature flags), flag it clearly and alert the user before proposing any modification. Business logic changes require explicit user confirmation even if the reviewer requested them.
Step 5: Present Comments
Display ALL comments in sequential PR order (not grouped by category). For each comment show:
### #N — [TAG] file/path.ts:line
**Reviewer**: @username
**Comment**: [reviewer's text, abbreviated if very long]
**Analysis**: [Your assessment — does it make sense? Is it still relevant?]
**Suggested Fix**: [Concrete code change or action to take]
**Impact**: [Other files/functions affected]
**Alternatives**:
1. [Option A] — pros / cons
2. [Option B] — pros / cons
For 👍 Praise comments, show a single-line entry:
### #N — 👍 file/path.ts:line — @username: "Nice work!"
After presenting all comments, proceed to the triage step.
Step 5.5: Point-by-Point Triage with the User
Before resolving anything, walk through the comments one by one with the user to validate understanding and alignment — especially for comments flagged as ambiguous or touching business logic.
Group comments into three buckets:
- Straightforward — clear bug fixes, typos, missing null checks, style issues where the intent is unambiguous.
- Declining — the suggestion contradicts a repo convention from Step 3.5. List these with the rule that overrides them so the user can overrule you.
- Needs validation — anything flagged as ⚠️ Ambiguous in Step 4, business logic changes, comments where the reviewer's intent could be interpreted multiple ways, or where the "correct" behavior depends on domain knowledge you don't have.
Present a triage summary to the user using AskUserQuestion:
I found N open comments. Here's my assessment:
Can resolve directly (N):
- #1 — 🔴 src/auth.ts:45 — null check missing (clear fix)
- #3 — 🟡 src/api.ts:12 — style nit (optional chaining)
Plan to decline (N):
- #5 — ✨ src/api.ts:30 — reviewer wants a barrel export;
CLAUDE.md:18forbids them. Say so and skip?
Need your input first (M):
- #2 — ⚠️ src/pricing.ts:80 — reviewer says discount should cap at 30%, but code allows 50%. Which is correct?
- #4 — ⚠️ src/permissions.ts:22 — ambiguous: should admins bypass this validation or not?
Want to go through the items that need validation point by point?
For each "needs validation" item, use AskUserQuestion to clarify before proposing any code change. Present:
- What the reviewer said
- What the code currently does
- Why it's ambiguous (the specific question or contradiction)
- Ask the user what the correct behavior should be
Only after the user confirms the expected behavior should you propose a concrete fix.
For "straightforward" items, confirm with the user that they agree these can be resolved directly. The user may move items between buckets.
This triage ensures you never apply a "fix" that introduces a different bug because the business rule was misunderstood. The goal is: when in doubt, ask first — never guess business rules.
Step 5.9: Batch Detection for Simple Fixes
Before starting sequential resolution, scan all triaged comments for simple fixes — changes that meet ALL of these criteria:
- Single file affected
- Less than 10 lines changed
- Clear, unambiguous intent (not flagged as ⚠️ Ambiguous)
- No business logic involved
- No architectural impact
- Was categorized as "straightforward" in the triage step
If 3 or more simple fixes are detected, present a batch offer to the user:
N comments are straightforward fixes (typos, null checks, style nits, etc.). Apply all at once?
# Tag File Fix 1 🟡 src/api.ts:12 Add optional chaining 3 📝 src/utils.ts:5 Fix JSDoc typo 7 🟡 src/auth.ts:30 Remove unused import [yes] — Apply all and resolve threads | [no] — Process one by one | [pick] — Choose which to batch
- yes: Apply all simple fixes, commit, push, resolve all their threads, then continue with the remaining complex comments in Step 6.
- no: Skip batching — process everything sequentially in Step 6.
- pick: Let the user select which of the simple fixes to batch. Apply selected ones, then continue with the rest sequentially.
If fewer than 3 simple fixes are detected, skip this step and go directly to Step 6.
Step 6: Resolve Sequentially
Process remaining comments (those not already batch-resolved in Step 5.9) one by one in PR order, respecting the triage decisions from Step 5.5:
Show a recap of the current comment (tag, file, reviewer, what was asked).
If this comment was flagged as "needs validation" and was already validated in the triage step, use the user's confirmed answer to propose the fix. If it wasn't validated yet (e.g., the user skipped triage), use AskUserQuestion now — do NOT guess the correct business behavior.
Propose the change — show the specific edit you intend to make.
Ask for confirmation with these options:
- yes — Apply the change as proposed
- no — Skip this comment entirely
- modify — Let the user adjust the proposed change before applying
- skip — Skip for now, come back later
- reply-only — Don't change code; draft a reply to the reviewer instead (1 to 3 sentences, per Reply Style)
Enter Plan mode when any of these conditions apply:
- The change spans multiple files
- The change involves 30+ lines of modifications
- The change has architectural impact (new patterns, structural changes)
- The change affects business logic (pricing, permissions, workflows)
- The comment is ambiguous and could be interpreted multiple ways
- Two or more comments conflict with each other
In Plan mode: analyze the full scope, present the plan to the user, and only proceed after approval.
After applying the change, commit and push before resolving the thread. A comment should only be marked as resolved on GitHub after the fix is committed and pushed to the PR branch (see Step 7).
Step 7: Auto-Resolve Threads
Use the GraphQL mutation resolveReviewThread to mark threads as resolved on GitHub:
gh api graphql -f query='
mutation($threadId: ID!) {
resolveReviewThread(input: { threadId: $threadId }) {
thread {
isResolved
}
}
}' -f threadId='{threadId}'
The threadId comes from the id field on the reviewThreads > nodes fetched in Step 2.
Rules for resolving:
- Only resolve after commit+push — a thread should only be marked resolved on GitHub after the corresponding code change has been committed and pushed to the PR branch. Never resolve a thread for a change that only exists locally.
- Do NOT resolve threads where the user chose reply-only — these need the reviewer's acknowledgment.
- Do NOT resolve threads tagged ❓ Question that were answered with reply-only.
- Only resolve threads that had actual code changes applied, committed, and pushed.
Step 8: Summary
After resolving all selected items, present a summary table:
## PR #123 — Resolution Summary
| # | Tag | File | Action Taken | Thread |
| --- | --- | --------------- | ------------------------ | ----------- |
| 1 | 🔴 | src/auth.ts:45 | Added null check | ✅ Resolved |
| 2 | 🟡 | src/api.ts:80 | Used optional chaining | ✅ Resolved |
| 3 | ❓ | src/api.ts:95 | Replied with explanation | ⏳ Open |
| 4 | 👍 | src/auth.ts:60 | — | — |
| 5 | ✨ | src/utils.ts:30 | Skipped by user | ⏳ Open |
### Files Modified
- src/auth.ts
- src/api.ts
### Next Steps
- Run your type checker and tests to verify nothing broke
- Use `/commit` to commit the changes
Step 9: Post-Push Watch (Normal Mode)
After Step 8, automatically start a 6-minute watch for new review comments triggered by the push (e.g., CodeRabbit re-reviewing the diff, or a human leaving a follow-up).
Skip this step entirely if no commits were pushed during this session — there's nothing new for reviewers to respond to. Also skip it in full-auto mode, which already handles polling exhaustively via Phase 4.
Step 9.1: Capture the baseline
Before launching the watch, snapshot the state so the loop can tell "new" from "already there":
BASELINE_SHA— PR head commit SHA after the final push:gh pr view <NUMBER> --json headRefOid -q '.headRefOid'BASELINE_TIME— ISO 8601 timestamp of that commit:gh api repos/{owner}/{repo}/commits/<BASELINE_SHA> --jq '.commit.committer.date'KNOWN_THREAD_IDS— JSON array of thread IDs that were open at the end of Step 7 and the user intentionally left unresolved (reply-only, skipped, ❓ Question deferred to reviewer, declined stylistic suggestions). Collect these from the resolution state tracked in Steps 6–7.DEADLINE— current UTC time + 6 minutes, ISO 8601.
Step 9.2: Launch the dynamic loop
Invoke the loop skill in dynamic mode (no interval — each iteration decides when to wake up). Pass a self-contained prompt with the baseline embedded inline so every iteration resumes with the same reference point:
Watch PR #<NUMBER> in <owner>/<repo> for new review comments.
Baseline:
- SHA: <BASELINE_SHA>
- Time: <BASELINE_TIME>
- Deadline: <DEADLINE>
- Known-open threads: <JSON array of thread IDs>
Each iteration:
1. If current UTC time >= Deadline, stop (do NOT call ScheduleWakeup) and tell
the user: "PR #<NUMBER> watch ended — 6 minutes elapsed with no new review
comments."
2. Fetch review threads with the same GraphQL query from Step 2 of the
pr-comments skill.
3. Filter to "new alerting" threads:
- isResolved == false
- isOutdated == false
- Thread id NOT in Known-open threads
- First comment's createdAt > Baseline Time
4. If new threads are found:
- Stop the loop (do NOT call ScheduleWakeup).
- Summarize briefly: tag, file:line, reviewer, 1-line excerpt of the first
comment.
- Use AskUserQuestion: "Found N new review comment(s) on PR #<NUMBER>. Run
/pr-comments now to address them, or dismiss?"
5. Otherwise, call ScheduleWakeup with delaySeconds=60 and pass this same prompt
verbatim so the next iteration resumes with the same baseline.
Why dynamic mode
A fixed interval like /loop 1m ... keeps firing and has no clean stop hook when the condition is met. Dynamic mode lets each iteration decide whether to schedule the next one, so the watch exits cleanly as soon as the deadline passes or new comments surface — without leaving a zombie timer.
Why baseline filtering
Without the SHA + timestamp + KNOWN_THREAD_IDS baseline, the loop would re-alert every minute on threads the user intentionally left open (reply-only, skipped, deferred questions). Filtering by createdAt > BASELINE_TIME and excluding known thread IDs ensures we only surface threads that actually appeared after the final push.