pr-comment-resolving
PR Comment Resolving
Find every unresolved review comment on the current branch's PR, try to address each, reply explaining how it was addressed (or what blocks resolution), and mark threads resolved where appropriate.
When to Use This Skill
/rrslash command- User asks to resolve, address, or respond to PR review comments
- Working through feedback from CodeRabbit, Copilot, Claude Code reviewer, or human reviewers
- Cleaning up a PR before merge by closing out review threads
Scope: Three Distinct Comment Surfaces
A GitHub PR carries feedback in three surfaces. All must be checked:
- Inline review threads — attached to a line of code. These are the only
ones with a real
isResolvedflag. Must be enumerated via GraphQL; REST does not expose resolution state. - PR-level issue comments — top-level comments on the Conversation tab. No resolution flag; treat as addressed when you've replied or acted.
- Review summary bodies — the body of a submitted review (CodeRabbit walkthrough, Copilot summary, human review comment). No resolution flag; often contain actionable items the inline threads don't cover.
Input
The skill accepts an optional PR reference — a PR number (123), a full URL
(https://github.com/owner/repo/pull/123), or an owner/repo#123 form. When
no argument is given, fall back to the current branch's PR.
Process
1. Identify the PR
Parse the argument if provided:
- URL
https://github.com/OWNER/REPO/pull/N: extract OWNER, REPO, N. OWNER/REPO#N: extract OWNER, REPO, N.- Bare number
N: use N with the current repo (fromgh repo view). - No argument: resolve from the current branch.
Resolve the PR once and read the identifiers into your own context. Review
threads live on the base repo (not the head repo — important for
cross-fork PRs), so use baseRepository:
# Pass the PR ref if given (number, URL, or branch). Omit to use current branch.
gh pr view [PR_REF] --json number,baseRepository \
-q '{num:.number, owner:.baseRepository.owner.login, repo:.baseRepository.name}'
Example output: {"num":42,"owner":"adamspiers-org","repo":"ai-config"}.
For owner/repo#N input, split into OWNER/REPO and N and pass them as
gh -R OWNER/REPO pr view N.
Do not persist these values to disk. Shell state is not preserved between
Bash tool calls, and any file-based approach breaks when multiple agents
share a worktree. Instead, read the three values from the JSON output above
and substitute the literals directly into every subsequent command in
steps 2-7. For example, once you have num=42, owner=foo, repo=bar, the
step 3 command becomes:
gh api "repos/foo/bar/issues/42/comments" --paginate \
| tee tmp/issue-comments.json > /dev/null
— no variables, no sourcing. The commands below use $OWNER, $REPO_NAME,
$PR_NUMBER as placeholders for you to replace, not as shell variables.
If gh pr view fails with "no pull requests found" (and no argument was
given), stop and report — no PR exists for the current branch. If a bad
argument was given, show the error and stop.
2. Enumerate unresolved inline review threads (GraphQL)
Write the query to tmp/unresolved-threads.json for repeated analysis without
re-fetching (per the user's preference for capturing gh output via tee).
Replace the three $... placeholders with the literals from step 1:
mkdir -p tmp
gh api graphql -f query='
query($owner:String!, $repo:String!, $pr:Int!, $cursor:String) {
repository(owner:$owner, name:$repo) {
pullRequest(number:$pr) {
reviewThreads(first:100, after:$cursor) {
pageInfo { hasNextPage endCursor }
nodes {
id
isResolved
isOutdated
isCollapsed
path
line
viewerCanResolve
viewerCanReply
comments(first:50) {
nodes {
id
databaseId
body
url
createdAt
author { login __typename }
replyTo { id }
}
}
}
}
}
}
}' -F owner="$OWNER" -F repo="$REPO_NAME" -F pr="$PR_NUMBER" \
| tee tmp/review-threads.json \
| jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved==false)]' \
> tmp/unresolved-threads.json
Paginate if .data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage is
true — re-run with -F cursor="$END_CURSOR" and merge.
3. Enumerate PR-level issue comments
gh api "repos/$OWNER/$REPO_NAME/issues/$PR_NUMBER/comments" --paginate \
| tee tmp/issue-comments.json > /dev/null
4. Enumerate review summary bodies
gh api "repos/$OWNER/$REPO_NAME/pulls/$PR_NUMBER/reviews" --paginate \
| tee tmp/reviews.json > /dev/null
Filter to reviews with a non-empty body. CodeRabbit and Copilot place their
actionable summary here.
5. Triage and address each item
For each unresolved thread / comment / review body:
- Read the content and the code it refers to (use
pathandlinefrom the thread, or parse file refs out of issue-comment / review bodies). - Decide: is the point valid, already addressed, out of scope, or wrong?
- Act:
- Valid & actionable → make the code change.
- Already addressed in a later commit → note the commit SHA in the reply.
- Out of scope → explain why and suggest a follow-up issue.
- Wrong / disagree → explain the reasoning, don't just dismiss.
- Reply (see step 6). Every item gets a reply explaining outcome.
- Resolve if appropriate and possible (see step 7).
6. Post replies
Always include short sentence at the top of the reply making it clear that this reply has been posted by an AI agent, e.g.
_(reply generated by <INSERT NAME OF AGENT / LLM HERE>)_
Inline thread reply (preferred: GraphQL, using thread id from step 2):
gh api graphql -f query='
mutation($tid:ID!, $body:String!) {
addPullRequestReviewThreadReply(input:{pullRequestReviewThreadId:$tid, body:$body}) {
comment { id url }
}
}' -F tid="$THREAD_ID" -F body="$REPLY_BODY"
PR-level issue comment reply (no thread structure — just another comment):
gh pr comment "$PR_NUMBER" --body "$BODY"
Reference the comment you're replying to by quoting or linking its URL.
Reply to a review summary: post a PR-level issue comment quoting the relevant section — GitHub has no "reply to review summary" primitive.
7. Resolve threads
Only resolve an inline thread when:
viewerCanResolveis true, and- The reply makes it clear the concern is addressed (fixed, invalid, or deferred with agreement).
Do NOT resolve threads where:
- The user might still want to weigh in.
- You disagree with the reviewer — let the human decide.
- The fix is speculative and unverified.
gh api graphql -f query='
mutation($tid:ID!) {
resolveReviewThread(input:{threadId:$tid}) {
thread { id isResolved }
}
}' -F tid="$THREAD_ID"
Identifying Comment Sources
In the GraphQL response, author.__typename is "Bot" or "User". Known bots:
| Source | Login (GraphQL) | Login (REST) |
|---|---|---|
| CodeRabbit | coderabbitai |
coderabbitai[bot] |
| Copilot reviewer | copilot-pull-request-reviewer |
copilot-pull-request-reviewer[bot] |
| GitHub Actions | github-actions |
github-actions[bot] |
| Claude Code reviewer | varies (claude[bot] for official app; PAT user for self-hosted) |
varies |
Treat bot comments with the same rigor as human ones — CodeRabbit in particular surfaces real issues.
Reply Style
Each reply should be one of:
- Fixed: "Fixed in
<sha>.<one-line explanation>." - Already addressed: "Addressed earlier in
<sha>—<brief>." - Deferred: "Out of scope for this PR. Filed as
<issue link or note>." - Disagreed: "
<reasoning>. Leaving as-is."
Keep replies terse. Link to commits / issues rather than restating changes.
Gotchas
- Thread
idvs commentdatabaseId: mutations use the opaqueid. REST replies-to-comment use the numericdatabaseIdof the thread's first comment. - Outdated threads (
isOutdated==true): often the line moved/disappeared. Reply is still possible but may be low-value — use judgement. - Fork PRs from
GITHUB_TOKENhave read-only repo access; resolve mutations will fail. The user'sgh auth logintoken is needed. - Pagination: both REST endpoints need
--paginate; the GraphQL query needs manual cursor handling if the PR has >100 threads. - No
isResolvedin REST: don't try to filter unresolved via/pulls/{pr}/comments— it's not there.
Output
After processing, summarise to the user:
- Count of threads/comments addressed, deferred, disagreed, resolved.
- Any that need the user's judgement before proceeding.
- Any code changes that still need committing & pushing.
More from aspiers/ai-config
allow-agent-commands
Add or change allowed commands in AI agent permission configs (OpenCode, Claude Code)
20project-initialization
Initialize a project with AI agent rules and documentation. Use when setting up a new repository for AI agent collaboration.
14code-linting
Run linters according to repository guidelines. Use immediately after creating or modifying code, or before committing changes.
13safe-rm
Safely delete files / directories without asking for permission
13subagent-authoring
Create subagent definitions for Claude Code and OpenCode that delegate to skills. Use when creating new subagents or refactoring existing ones to follow the delegation pattern.
12test-running
Run tests according to repository guidelines. Use after linting passes, before staging changes.
11