pr-address-feedback

v2026.09.24

Use this skill whenever the user wants to address, respond to, work through, or handle GitHub PR review comments — including code review feedback, reviewer suggestions, bot comments, or requested changes — even if they don't say "address". Handles the full end-to-end workflow: fetch threads, plan fixes, commit, push, and reply. Requires `gh` CLI.

GitHub
Install command
npx skhub add robdefeo/pr-address-feedback
Markdown
SKILL.md

PR Address Feedback

Workflow

  1. Fetch unresolved review threads, general PR comments, and bot comments (filter noise).
  2. Evaluate each comment and assign a disposition: fix, doc-only, skip, or defer.
  3. Propose plan as a numbered table; wait for user approval. User edits by row number.
  4. Apply changes grouped by concern (docs / tests / api / refactor / error-handling).
  5. Commit each group with Conventional Commits; push once when all groups are done.
  6. File follow-up issues for deferred items, reply to every thread, and resolve fixed/doc/deferred threads.

Step 1 — Fetch

Get PR metadata:

gh pr view --json number,title,headRefName,baseRefName,url,author

Fetch review threads (with resolution state, author, body, location):

gh api graphql -f query='
query($owner:String!,$repo:String!,$pr:Int!){
  repository(owner:$owner,name:$repo){
    pullRequest(number:$pr){
      reviewThreads(first:100){
        nodes{
          id isResolved isOutdated
          comments(first:50){
            nodes{ id databaseId author{login} body path line originalLine url createdAt }
          }
        }
      }
    }
  }
}' -F owner=OWNER -F repo=REPO -F pr=PR_NUMBER

After fetching, filter to unresolved threads only (isResolved == false) before evaluation. Threads with isOutdated == true should be flagged [outdated] and defaulted to skip.

Known limit: reviewThreads(first:100) and comments(first:50) do not paginate. PRs with more than 100 threads or 50 comments per thread will be silently truncated.

Fetch general PR comments:

gh api repos/OWNER/REPO/issues/PR_NUMBER/comments

Filter noise. Drop bodies matching known boilerplate before evaluating:

  • thank you for your contribution
  • reviewing this pull request and will post my feedback shortly
  • not currently linked to an issue
  • Empty bodies or pure emoji reactions.

Step 2 — Evaluate each comment

Assign one of four dispositions:

DispositionWhen to use
fixClear code change, low risk, in scope.
doc-onlyOnly comments/docstrings/README change — no logic touched.
skipDesign intent, disagreement with premise, or invalid suggestion. Always give a reason.
deferValid but out of scope for this PR. Will become a follow-up issue.

Flag outdated inline comments (line no longer exists in the diff) as [outdated] in the Notes column; default disposition skip with reason "comment refers to code no longer in this diff".


Step 3 — Propose plan

Present one concise table. Do not touch code yet.

#File:lineAuthorDispositionNotes
1src/foo.rs:42@alicefixUse try_init() instead of init()
2tests/bar.rs:16@bobfixReplace /tmp with tempfile::tempdir()
3src/foo.rs:96@aliceskipUnbounded channel is intentional — must not block agent loop
4src/db.rs:120@coderabbitdeferAdd connection pooling — larger refactor, separate PR
5README.md:8@bobdoc-onlyClarify install prerequisites

Wait for approval. The user edits by row number — e.g. 2 skip: flaky on CI, 5 defer. Update the table and re-confirm before proceeding.


Step 4 — Apply changes grouped by concern

Group related changes into logical units. Canonical groups:

  • docs — doc strings, comments, README/markdown fixes
  • tests — test portability, coverage, cleanup
  • api — public signature, return type, parameter changes
  • error-handling — logging, error propagation, retry
  • refactor — internal restructuring with no behavior change

Do not mix unrelated concerns in a single commit. Many small comments on the same concern → one commit. Two fixes in different concerns → two commits.


Step 5 — Commit and push once

Conventional Commits: type(scope): subject

Capture the full 40-character commit hash with git rev-parse HEAD (not --short). Replies in Step 6 must use the full hash so the link stays stable even if GitHub's short-hash collision threshold shifts.

git add crates/tracer/src/lib.rs
git commit -m "docs(tracer): clarify async write and flush guarantees"
HASH_DOCS=$(git rev-parse HEAD)

git add crates/tracer/tests/
git commit -m "test(tracer): replace /tmp with tempfile::tempdir()"
HASH_TESTS=$(git rev-parse HEAD)

git add crates/tracer/src/lib.rs
git commit -m "fix(tracer): use try_init() and log on dropped send"
HASH_API=$(git rev-parse HEAD)

git push

Record a comment_id → hash map as you commit. Used in Step 6.


Step 6 — File follow-ups, reply, resolve

These run in order. Do not start 6b until 6a is complete for every defer row — a deferred reply without a real issue URL is invalid.

6a — File follow-up issues (deferred items only)

For every defer row, create the issue first and capture its URL. Build a comment_id → issue_url map alongside the comment_id → hash map from Step 5.

ISSUE_URL=$(gh issue create \
  --title "Add connection pooling to db layer" \
  --body "Follow-up from #PR_NUMBER (thread: COMMENT_URL).\n\nContext: reviewer flagged missing pooling in src/db.rs:120. Out of scope for the current PR — tracking here." \
  | tail -1)

If gh issue create fails or returns no URL, stop — do not post the deferred reply with a placeholder.

6b — Reply to every thread

Reply templates — {hash} is the full 40-character commit SHA from Step 5; {issue_url} is the URL captured in 6a:

  • Fixed: Fixed in {hash} — {one-line description of what changed}.
  • Doc-only: Clarified in {hash} — {what was clarified}.
  • Skipped: Won't fix — {concise reason rooted in design intent or scope}.
  • Deferred: Out of scope for this PR — tracked in {issue_url}. — {issue_url} is required and must be a real GitHub issue URL from 6a. Never substitute a phrase like "tracked for follow-up" or "will address later".

Post replies (inline review threads):

gh api repos/OWNER/REPO/pulls/PR_NUMBER/comments/COMMENT_ID/replies \
  -X POST --field body="Fixed in $HASH_API — switched to try_init() and added a warn! on dropped send."

Note: COMMENT_ID must be the numeric REST API ID — use the databaseId field from the GraphQL response, not the node id (e.g. PRRC_kwDO…).

Post replies (general PR comments — no thread; post a new top-level comment):

gh api repos/OWNER/REPO/issues/PR_NUMBER/comments \
  -X POST --field body="Fixed in $HASH — {one-line description}."

Reply to every evaluated thread — no comment should be left without a response.

6c — Resolve threads

Resolve threads for fix, doc-only, and defer. Leave skip threads open so the reviewer can push back.

gh api graphql -f query='
mutation($id:ID!){
  resolveReviewThread(input:{threadId:$id}){ thread{ isResolved } }
}' -F id=THREAD_ID

Edge cases

  • Branch out of sync with remote → surface to user, do not auto-rebase or force-push.
  • PR has merge conflicts → stop before Step 4, ask user how to proceed.
  • Outdated inline comments → [outdated] tag in plan, default skip.
  • Pure bot boilerplate → filtered in Step 1; never reach the plan table.
  • User rejects the whole plan → no commits, no replies, no issues created.
  • Large PRs (>100 threads or >50 comments/thread) → fetch is silently truncated; warn the user and process only what was returned. See issue #2 for full pagination support.
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

Not specified

Source path

skills/pr-address-feedback

Default branch

main

Latest commit

0b9da00

Tree SHA

57ff1bf