From gh-pr-tools
Fetch active PR review threads from GitHub and resolve them by making code changes. Use when the user wants to address, fix, or resolve PR review comments automatically.
npx claudepluginhub shouenlee/ghcp-dev-plugin --plugin gh-pr-toolsThis skill uses the workspace's default tool permissions.
Automatically address active PR review comments from GitHub by reading each comment, understanding the requested change, and making the code fix.
Fetches unresolved GitHub PR comments and threads, analyzes reviewer feedback, implements fixes, builds/tests, commits/pushes changes, replies to comments, and resolves threads.
Reads open GitHub PR review comments, triages by severity and type, applies code fixes, and drafts replies. Use for addressing PR feedback, fixing review issues, and responding to code reviews.
Interactively addresses GitHub PR review feedback: verifies each comment against code, confirms fixes with user, applies changes, and posts responses.
Share bugs, ideas, or general feedback.
Automatically address active PR review comments from GitHub by reading each comment, understanding the requested change, and making the code fix.
/resolve-pr <PR-number-or-URL> [--dry-run] [--reply] [--commit] [--address]
| Flag | Description |
|---|---|
--dry-run | Show what changes would be made without editing files |
--reply | Reply to each comment thread after addressing it |
--commit | Stage and commit all changes after resolution with a descriptive message |
--address | Triage-first mode: categorize comments, investigate with evidence, fix or draft replies |
gh CLI must be installed and authenticated (gh auth status)Accept any of these formats:
123 or #123https://github.com/{owner}/{repo}/pull/{number}gh pr view --json number --jq '.number'Fetch PR metadata and review threads. Use gh repo view --json owner,name --jq '.owner.login + " " + .name' to get owner and repo dynamically.
PR metadata (REST):
gh pr view <number> --json title,body,author,baseRefName,headRefName,state,additions,deletions,changedFiles
Review threads with node IDs (GraphQL) — needed for replies and thread resolution:
gh api graphql -f query='
query($owner:String!, $repo:String!, $pr:Int!) {
repository(owner:$owner, name:$repo) {
pullRequest(number:$pr) {
title
state
author { login }
baseRefName
headRefName
reviewThreads(first:100) {
nodes {
id
isResolved
isOutdated
comments(first:10) {
nodes {
id
body
author { login }
path
line
originalLine
}
}
}
}
}
}
}' -f owner='{owner}' -f repo='{repo}' -F pr='{number}'
Display the PR title, description, source branch, and target branch to the user.
Verify the local checkout matches the PR's source branch:
git branch --show-current
Compare with the PR's headRefName.
If they don't match, warn the user before proceeding:
Warning: You are on branch
{current}but the PR source branch is{headRefName}. Changes should be made on the source branch. Switch with:git checkout {headRefName}
If the user wants to continue on the current branch, proceed but note the mismatch in the summary.
Keep only threads where:
isResolved == falsepath)Skip threads that are:
isResolved == true)Optionally exclude outdated threads (isOutdated == true) — these reference code that has already changed.
Before resolving any individual comment, build a complete picture of the PR.
Read the PR title and description from Step 1. This tells you what the author was trying to accomplish — every comment resolution must align with this intent.
Run a local git diff against the PR's target branch:
git diff --name-only {baseRefName}...HEAD
Read the diff summary to understand the scope of changes:
git diff --stat {baseRefName}...HEAD
This gives you the full picture — not just files with comments, but the entire change being reviewed.
For each thread, read ALL comments — not just the root body.
Later replies often refine, clarify, or contradict the original ask:
The last actionable comment is what you should resolve, not the first.
Multiple comments may reference the same file. Group them so you can process all comments for a file together, avoiding conflicting sequential edits.
Core principle: Treat each comment as if the user pasted it directly into Claude Code as a task. Do not take shortcuts. Do not limit yourself to the single file mentioned in the comment. Explore, understand, and solve — exactly as you would for any coding task.
Progress feedback: Before starting each comment, show the user where you are:
Resolving comment {n}/{total}: {path}:{line} - "{first 60 chars of comment}..."
For example: Resolving comment 3/12: src/utils/auth.ts:45 - "Add null check before accessing user.email..."
For each active comment thread (grouped by file):
Build a complete picture of what's being asked:
path and lineRead the target file.
Do NOT stop at just reading the target file. Read as much as you need to fully understand the code before making changes:
.editorconfig, tsconfig.json, lint configs) that affect styleUse Glob and Grep freely to explore. This is exactly what you would do if a user asked you to fix something — do not skip this step.
Before making an edit, make sure you understand:
If --dry-run, report the planned change without editing:
[DRY RUN] {file}:{line} - Would {description of change}
If NOT --dry-run, make the change as you normally would for any coding task:
Do not make changes beyond what the reviewer asked for. But do make ALL the changes needed to properly address what they asked for.
After making edits, verify the change is correct:
Re-read the modified file(s) to confirm the edit looks right.
Determine the project's build and test tooling. Look for project config files to identify the correct commands:
package.json — Node.js: npm run build, npm testtsconfig.json — TypeScript: tsc --noEmitpyproject.toml / setup.py — Python: pytestMakefile — check for build/test targetsCargo.toml — Rust: cargo build, cargo test*.csproj / *.sln — .NET: dotnet build, dotnet testpom.xml — Java: mvn compile, mvn testCONTRIBUTING.md / README.md for build instructionsRun the build if the project has a compile step and the change is non-trivial. Only run the build check relevant to the modified files — do not rebuild the entire monorepo for a one-line fix.
Run relevant tests if tests exist for the modified code. Target only the tests related to the changed files — do not run the full test suite.
If the build or tests fail, investigate and fix the failure before moving to the next comment. If you cannot fix the failure, revert your change and flag the comment for manual review.
--reply)Draft a concise reply summarizing the change made. For example:
getUserData to fetchUserData here and updated the 3 call sites."validateInput() in src/utils/validation.ts and updated the import."Present the draft to the user before posting. Show the reply text and ask if they want to:
Use AskUserQuestion to collect the user's choice. If the user chooses to edit, use their provided text as the reply content.
Post the approved reply (reply to the last comment in the thread):
gh api repos/{owner}/{repo}/pulls/<number>/comments/<comment_id>/replies \
--method POST \
-f body="{final reply text}"
Resolve the thread using the GraphQL thread node ID from Step 1:
gh api graphql -f query='
mutation($threadId:ID!) {
resolveReviewThread(input: { threadId: $threadId }) {
thread { isResolved }
}
}' -f threadId='{thread_node_id}'
Some comments cannot be auto-resolved and require human judgment. Do not force a resolution — it is better to flag a comment for manual review than to make a wrong change.
Flag a comment as unresolvable when:
Collect these and present them to the user at the end with a clear explanation of why each one needs manual attention.
After processing all threads, present a summary:
## PR Comment Resolution Summary
**PR:** #<number> - <title>
**Threads processed:** <total active threads>
### Resolved (<count>)
| File | Line | Comment | Change Made |
|------|------|---------|-------------|
| <file> | <line> | <truncated comment> | <brief change description> |
### Needs Manual Review (<count>)
| File | Line | Comment | Reason |
|------|------|---------|--------|
| <file> | <line> | <truncated comment> | <why it couldn't be auto-resolved> |
### General Comments (no file context) (<count>)
- <comment text>
After the summary:
--commit was NOT specified, remind the user: "Review changes with git diff before committing. Changes are local only."--commit was specified, proceed to Step 8.--commit)Only run this step if the --commit flag was provided AND at least one comment was resolved (not just flagged for manual review).
Show the diff summary to the user:
git diff --stat
Stage only the files that were modified during comment resolution.
Use git add with specific file paths — do NOT use git add -A or git add ..
Create a commit with a descriptive message:
Address PR #<number> review comments
Resolved:
- <file>:<line> - <brief description of change>
- <file>:<line> - <brief description of change>
Needs manual review:
- <file>:<line> - <reason>
Do NOT push automatically.
After committing, tell the user: "Changes committed. Push when ready with git push."
--address)When --address is specified, use a triage-first approach instead of the default "fix everything" workflow.
This mode investigates each comment with evidence before acting — better for nuanced feedback, questions, and architecture concerns.
The default mode (no --address) is still the right choice when comments are straightforward fix requests.
Same as the default workflow Step 1.
Load addressing-feedback.md for investigation guidance. See response-examples.md for calibrated response examples.
Before triaging, verify local state matches the PR:
git log -1 --format=%H
Compare against the PR's latest source commit (from gh pr view output or GraphQL response).
If they differ, warn the user before proceeding.
See addressing-feedback.md for sync state handling.
Instead of filtering to "unresolved with file context" and fixing everything, categorize each active comment:
| Category | Action |
|---|---|
| Clear fix request | Investigate and fix the code |
| Question from reviewer | Investigate and draft a reply with evidence |
| Style/preference nit | Fix if trivial, draft reply if debatable |
| Architecture concern | Investigate, draft informed reply |
| Already addressed | Draft reply pointing to the fix |
For each comment, follow the reviewer-first approach from addressing-feedback.md:
For each comment that needs a code change:
For each comment that needs a reply:
getUser() on line 42 can return null when the cache miss path is hit (see CacheManager.ts:89). Added null check."processItems() is called only from handleBatch() (line 112) which already validates the input array is non-empty."## Addressing PR #<number> Feedback
### Changed (<count>)
**<file>:<line>** — <reviewer>: "<comment excerpt>"
→ <what was changed and why>
### Investigated, No Change Needed (<count>)
**<file>:<line>** — <reviewer>: "<comment excerpt>"
→ <evidence showing current code is correct>
### Needs Discussion (<count>)
**<file>:<line>** — <reviewer>: "<comment excerpt>"
→ <why this needs human decision>
### For Your PR Response
- Re: <topic> — <talking point with evidence reference>
If --reply is also specified (or user approves), post drafted replies using the same reply mechanism as Step 5.6 in the default workflow.
If --commit is also specified, commit the code fixes using Step 8 from the default workflow.
gh not authenticated: Suggest running gh auth login.../../secrets/, /etc/passwd, ~/.ssh/).
Only read and modify files within the local repository checkout.The following shows a complete end-to-end resolution flow.
User input: /resolve-pr 4821 --reply
gh pr view 4821 --json title,body,author,baseRefName,headRefName,state
→ PR #4821 "Add input validation to checkout form"
Source: feature/checkout-validation → Target: main
Review threads (via GraphQL — 3 unresolved):
| Thread ID | File | Line | Resolved | Comment |
|---|---|---|---|---|
| T_abc123 | src/components/CheckoutForm.tsx | 45 | No | "Extract this validation logic into a validateEmail() helper in src/utils/validation.ts" |
| T_def456 | src/components/CheckoutForm.tsx | 82 | No | "Why did you use any here? Please add a proper type." |
| (none) | (no file) | — | No | "Overall looks good, just a couple of nits." |
git branch --show-current
→ feature/checkout-validation ✓ matches PR source branch
Thread T_abc123 — "Extract validation into helper":
src/components/CheckoutForm.tsx — find inline email regex at line 45src/utils/validation.ts already exists with validatePhone()src/utils/validation.ts to match existing patternsvalidateEmail() to src/utils/validation.ts following the same export patternCheckoutForm.tsx line 45 to import and call validateEmail()validateEmail() into src/utils/validation.ts (matching the existing validatePhone() pattern) and updated the import in CheckoutForm.tsx."gh api and resolve thread via GraphQL mutationThread T_def456 — "Add proper type instead of any":
any here?" — this is a question directed at the author, not a clear change request.## PR Comment Resolution Summary
**PR:** #4821 - Add input validation to checkout form
**Threads processed:** 3
### Resolved (1)
| File | Line | Comment | Change Made |
|------|------|---------|-------------|
| src/components/CheckoutForm.tsx | 45 | Extract validation into helper | Moved email validation to `validateEmail()` in `src/utils/validation.ts`, updated import |
### Needs Manual Review (1)
| File | Line | Comment | Reason |
|------|------|---------|--------|
| src/components/CheckoutForm.tsx | 82 | "Why did you use `any` here?" | Question directed at author — multiple valid types possible, reviewer hasn't specified preference |
### General Comments (no file context) (1)
- "Overall looks good, just a couple of nits."
Review changes with git diff before committing.
Changes are local only — commit and push when satisfied.