From pr-respond
Address review comments on your own PR via an interactive walkthrough. Fetches unresolved comment threads, walks through them one at a time, lets you choose Fix / Push back / Defer / Skip per comment, dispatches an implementer subagent for each Fix, batches the fixes into one commit, posts replies (with commit SHA for fixes, reasoning for push-backs), and resolves the threads. Use when the user says "respond to PR comments", "address the review", "respond to feedback on PR 42", or invokes /pr-respond.
How this skill is triggered — by the user, by Claude, or both
Slash command
/pr-respond:pr-respondThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Interactive walkthrough for addressing review comments on your own PRs.
Interactive walkthrough for addressing review comments on your own PRs.
CRITICAL: Never implement code changes without explicit user approval of the decision (Fix). Fully automated fixing is out of scope — this skill exists to give the author control while automating the mechanical parts (dispatching implementers, batching commits, replying, resolving).
/pr-respond 42 — respond to comments on PR #42/pr-respond https://github.com/owner/repo/pull/42 — by URL/pr-respond — inbox mode: find open PRs you authored that have unresolved review commentsExtract PR number from argument or URL.
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
YOUR_LOGIN=$(gh api user --jq '.login')
gh search prs --author=@me --repo="$REPO" --state=open \
--json number,title,url
For each PR, count unresolved threads via GraphQL:
gh api graphql -f query="
{
repository(owner: \"$OWNER\", name: \"$REPO\") {
pullRequest(number: $NUMBER) {
reviewThreads(first: 50) {
nodes { isResolved }
}
}
}
}" --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)] | length'
Show summary and prompt:
📬 Found {COUNT} of your PRs with unresolved review threads:
# | PR | Unresolved
----|-----------------------------------|------------
1 | #{NUM} {TITLE} | {COUNT}
Which PR? (number / none)
If no PRs with unresolved threads:
📭 No PRs with unresolved review comments on {REPO}.
Stop.
# PR metadata
gh pr view {NUMBER} --json title,author,headRefOid,headRefName,url
# Unresolved review threads via GraphQL
gh api graphql -f query="
{
repository(owner: \"$OWNER\", name: \"$REPO\") {
pullRequest(number: $NUMBER) {
reviewThreads(first: 100) {
nodes {
id
isResolved
comments(first: 10) {
nodes {
id
databaseId
body
path
line
author { login }
createdAt
}
}
}
}
}
}
}"
Filter to threads where isResolved == false. For each thread, the first comment is the original review comment. Subsequent comments are the reply chain.
If the PR's author.login !== $YOUR_LOGIN:
⚠️ This PR is not yours. Use /pr-review to review someone else's PR.
Stop unless user insists.
Exclude threads where the first comment's author is you — those are usually your own replies, not things to address. Keep threads where the first comment is from another reviewer (human or bot).
If there are zero unresolved external threads:
✅ PR #{NUMBER} has no unresolved review comments to address.
Stop.
Before starting the walkthrough, verify the working tree is clean:
git status --short
git branch --show-current
If there are uncommitted changes:
⚠️ Working tree has uncommitted changes:
{LIST}
Options:
(S)tash — stash changes, continue, unstash at end
(A)bort — stop and let you handle it
If the current branch doesn't match the PR's headRefName:
⚠️ Current branch is {CURRENT}, but PR #{NUMBER} is on {PR_BRANCH}.
(C)heckout {PR_BRANCH} / (A)bort?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PR #{NUMBER} — {TITLE}
Branch: {HEAD_REF}
Unresolved threads: {COUNT}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# | Severity | File:Line | Summary
----|----------|---------------------------------|-------------------------
1 | 🔴 | {PATH}:{LINE} | {FIRST_LINE}
2 | 🟡 | {PATH}:{LINE} | {FIRST_LINE}
Walk through each thread? (y/n)
Severity detection: Parse the first line of the comment body. If it starts with a severity emoji (🔴 🟡 ⚪) or a keyword (Critical, Important, Minor), use that. Otherwise default to ⚪ Minor (conservative — the user sees the full body anyway and can escalate via Fix).
If n, stop.
Use TodoWrite to track progress if there are 5+ threads.
For each unresolved thread, present:
[{INDEX}/{TOTAL}] {SEVERITY_EMOJI} {TITLE}
File: {PATH}:{LINE}
Author: {REVIEWER}
{COMMENT_BODY}
What do you want to do?
(F)ix — dispatch implementer subagent to address this
(P)ush back — reply with your reasoning, resolve thread
(D)efer — reply with "deferred", leave thread open
(S)kip — leave thread as-is, no reply, no resolve
(E)xplain — show surrounding file context, keep asking
Actions:
Fix (f): Queue this thread for fixing. Do NOT dispatch the implementer yet — batch all fixes and dispatch them in Step 6. Store the thread id, file, line, comment body, and the full comment for context.
Push back (p): Ask for the reasoning text:
Enter your reply (multi-line ok, empty line to finish):
Queue a reply with the user's text, and mark the thread for resolution.
Defer (d): Ask for an optional reason (TODO link, blocker, etc.). Queue a reply with ⏸️ Deferred: {reason}, do NOT resolve.
Skip (s): No-op. Do not reply, do not resolve. Log it so the final summary can show which threads were skipped.
Explain (e): Read the file around {LINE} (20 lines of context), show it, then re-prompt for the action.
Progress indicator:
✓ 1/7 — Fix queued
✓ 2/7 — Push back queued
→ 3/7 — Current
○ 4/7 — Pending
If at least one thread was marked Fix, dispatch one implementer subagent with all queued fixes. Do not dispatch one per fix — a single subagent implementing multiple related fixes produces a cleaner commit and lets the subagent spot cross-file duplication.
Before dispatching, detect the type check command:
# Find package manager
if [ -f "pnpm-lock.yaml" ]; then PKG="pnpm"
elif [ -f "yarn.lock" ]; then PKG="yarn"
elif [ -f "package-lock.json" ]; then PKG="npm"
fi
# Detect available scripts
if [ -f "package.json" ]; then
if grep -q '"check-types"' package.json; then TC_CMD="$PKG run check-types"
elif grep -q '"typecheck"' package.json; then TC_CMD="$PKG run typecheck"
elif grep -q '"type-check"' package.json; then TC_CMD="$PKG run type-check"
else
# Fall back to finding tsconfig.json at root
[ -f "tsconfig.json" ] && TC_CMD="$PKG exec tsc --noEmit"
fi
fi
Also detect the test command similarly (test script in package.json).
You are implementing PR review fixes for branch {BRANCH}.
Working directory: {REPO_ROOT}
Branch: {BRANCH}
The PR author accepted the following review findings. Implement each fix.
## Finding 1
**Reviewer:** {AUTHOR}
**File:** {PATH}:{LINE}
**Comment:**
{COMMENT_BODY}
## Finding 2
...
## Constraints
- Read each file before editing it
- Follow the project's existing code style and conventions
- Use the project's lint/format tools if configured
- Run `{TC_CMD}` after implementing all fixes to verify types are clean
- Do NOT commit — the parent controller will batch the commit after verification
Report back with:
- **Status:** DONE | BLOCKED
- What you implemented per finding
- Files changed
- Any findings you could not address (with reason)
{TC_CMD}. If it fails, show the error and ask the user:
git reset --hard {PREV_SHA}Before committing, show the user the staged diff:
git add -A
git diff --cached --stat
Then ask:
Review the full diff? (y/n/show-file <path>)
y → git diff --cached (paginated if large)n → proceed to commitshow-file <path> → git diff --cached -- {path} for a specific fileAfter review:
Proceed with commit? (Y/n)
If n, ask whether to:
git reset (uncommit the staging)Create a single commit with an AI-generated message summarizing the fixes.
git commit -m "$(cat <<'EOF'
fix: address PR review findings
{BULLET_PER_FIX}
Co-Authored-By: Claude <[email protected]>
EOF
)"
Each bullet is one sentence describing the fix. Order by severity: Critical → Important → Minor.
Then push:
git push
Capture the new commit SHA:
NEW_SHA=$(git rev-parse HEAD)
For each thread, post a reply to the original comment thread. Use the REST API's in_reply_to field.
Fix reply (default):
Fixed in {NEW_SHA_SHORT}. {IMPLEMENTATION_NOTE}
See [{FILE}:{LINE}](https://github.com/{OWNER}/{REPO}/blob/{NEW_SHA}/{FILE}#L{LINE}).
{NEW_SHA_SHORT} is the first 7 characters of the SHA{IMPLEMENTATION_NOTE} is a 1-2 sentence summary of what the fix does, based on the implementer's reportBefore posting each Fix reply, offer customization:
Reply preview:
{PREVIEW}
(P)ost as-is / (C)ustomize / (S)kip reply?
Push back reply:
{USER_REASONING}
Defer reply:
⏸️ Deferred: {USER_REASON}
gh api repos/{OWNER}/{REPO}/pulls/{NUMBER}/comments \
--method POST \
--field body="{REPLY_BODY}" \
--field in_reply_to={ORIGINAL_COMMENT_ID}
The in_reply_to is the databaseId of the first comment in the thread (not the thread node ID). These are different IDs — use the numeric REST ID.
For each thread marked Fix or Push back (NOT Defer or Skip), resolve it:
gh api graphql -f query="
mutation {
resolveReviewThread(input: { threadId: \"{THREAD_NODE_ID}\" }) {
thread { id isResolved }
}
}"
Resolution rules:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📊 PR #{NUMBER} review response complete
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# | Action | File:Line | Status
----|----------------|--------------------------|---------
1 | ✅ Fixed | {PATH}:{LINE} | Resolved
2 | 💬 Pushed back | {PATH}:{LINE} | Resolved
3 | ⏸️ Deferred | {PATH}:{LINE} | Open
4 | ⏭️ Skipped | {PATH}:{LINE} | Open
Commit: {NEW_SHA_SHORT} — {NEW_SHA}
URL: {PR_URL}
If changes were stashed in Step 3, restore them now:
git stash pop
PR has uncommitted changes on the working tree before you start. Step 3 handles this — stash or abort.
Push fails (rebased, conflict). Surface the error, show git status, ask the user how to proceed. Do not force-push.
Type check fails after fix. Show errors, ask: (1) re-dispatch implementer with error context, (2) abort all fixes and revert, (3) proceed anyway (strongly discouraged).
Thread has multiple comments already (chain of replies). The skill still posts a new reply to the chain using in_reply_to on the original comment. Do not try to reply to nested replies — GitHub's API threads all replies off the original comment.
Comment body is empty or unparseable. Default severity to ⚪, use "Review comment" as the title, show the raw body in the walkthrough.
Multi-package monorepo with multiple tsconfigs. The auto-detected type check command might not cover all packages. If the implementer touches files outside the default tsconfig scope, run turbo run check-types or equivalent if available.
Branch checkout in Step 3 fails because of conflicts. Abort and let the user resolve manually.
in_reply_to with the thread node ID instead of the first comment's databaseId. These are different IDs — use the numeric REST ID./pr-review and stop.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub colorpulse6/claude-skills --plugin pr-respond