CtrlK
BlogDocsLog inGet started
Tessl Logo
SkillPublic

pr-feedback

Address review feedback on a PR Josh authored - fetch the review comments, understand them, implement the fixes locally, and resolve the threads it addressed once Josh has reviewed, approved, and pushed. Never posts on his behalf. Invoke when Josh says things like 'we got some feedback on our PR', 'address the review comments', 'the reviewer said X', 'my PR has comments on it', 'can you handle the feedback on #12', 'fix what the review flagged', or mentions someone reviewing a PR of his. Disambiguation with pr-review: authorship decides. A PR Josh authored with review comments to act on belongs here - cues are 'our PR', 'my PR', 'feedback we got', 'address the review', 'reviewer said'. A PR authored by someone else that he wants read belongs to pr-review - cues are 'review this PR', "take a look at Dave's PR", 'can you review #12'. When the phrasing is ambiguous ('look at PR 12'), check the PR author with gh before choosing. Takes an optional PR ref and note; with no ref, resolves the PR from the conversation or the current branch.

Invalid
This skill can't be scored yet
Validation errors are blocking scoring. Review and fix them to unlock Quality, Impact and Security scores. See what needs fixing →
SKILL.md
Quality
Evals
Security

Task: Address review feedback on PR $ARGUMENTS

If you are in plan mode, exit it now. This is an active task — feedback needs to be addressed, not planned.

CRITICAL: No posting without approval

NEVER post comments, reviews, or any content on Josh's behalf. Not even if asked, unless Josh explicitly says to AND approves the exact text first.

You MUST NOT:

  • Submit reviews (gh pr review)
  • Post comments (gh pr comment, gh api comment endpoints)
  • Reply to review threads (addPullRequestReviewThreadReply)
  • Approve, request changes, or merge
  • Run any other mutation or write request against the PR

One write is permitted: the resolveReviewThread mutation. Resolving a thread is a state change with no content authored in Josh's name. Posting text is speaking for him, and that never happens without his explicit approval of the exact words. Resolve only threads whose feedback was actually addressed and pushed — Step 10 governs.

You implement fixes locally. Josh decides what to post and when to push.


Step 0: Parse arguments

$ARGUMENTS contains an optional PR ref and an optional note.

PR ref detection: The first token is a PR ref if it matches any of these patterns:

  • A plain number: 123
  • owner/repo#number: foo/bar#123
  • repo#number: bar#123

If the first token is NOT a PR ref, the entire $ARGUMENTS is a note. $ARGUMENTS may also be empty. Step 1 covers resolving the PR when no ref is given.

Examples:

InputPR refNote
123 look at just johnsmith review123look at just johnsmith review
foo/bar#123foo/bar#123(none)
look at just johnsmith review(auto)look at just johnsmith review
(empty)(auto)(none)

If a note is present, treat it as guidance — it may narrow which reviews to address, highlight priorities, or provide context.

Step 1: Resolve the PR reference

Resolve in this order, stopping at the first that produces a PR:

  1. A PR ref in $ARGUMENTS

  2. A PR ref or URL Josh named in the conversation

  3. The PR for the current branch:

    GH_TOKEN=$(gh auth token --user "$DOTFILES_GITHUB_USERNAME") \
      gh pr view --json number,headRepository -q '.number'

If none of these resolve, ask in one line — no preamble: Which PR?

Parse a ref to determine the repo and PR number:

FormatExampleMeaning
Number only123Current repo, PR 123
owner/repo#numberfoo/bar#123Repo foo/bar, PR 123
repo#numberbar#123Current owner + repo bar, PR 123
URLhttps://github.com/foo/bar/pull/123Repo foo/bar, PR 123

Get current repo owner if needed:

GH_TOKEN=$(gh auth token --user "$DOTFILES_GITHUB_USERNAME") \
  gh repo view --json owner -q '.owner.login'

Step 2: Confirm authorship, then announce

This skill handles PRs Josh authored. Check the author before doing anything else:

GH_TOKEN=$(gh auth token --user "$DOTFILES_GITHUB_USERNAME") \
  gh pr view <number> --repo <owner/repo> --json author,title,headRefName

Compare .author.login against $DOTFILES_GITHUB_USERNAME:

AuthorAction
$DOTFILES_GITHUB_USERNAMEContinue here
Anyone elseSay so in one line and invoke pr-review with the same ref

The handoff line when the author is someone else:

PR #123 is octocat's — switching to pr-review.

Otherwise announce the run in one line, then proceed without waiting for confirmation:

Addressing review feedback on PR #123 — Add bandwidth endpoint

Step 3: Save state and checkout PR branch

ORIGINAL_BRANCH=$(git branch --show-current)
STASH_RESULT=$(git stash --include-untracked 2>&1)
GH_TOKEN=$(gh auth token --user "$DOTFILES_GITHUB_USERNAME") \
  gh pr checkout <number> --repo <owner/repo>

Track whether stash saved anything (check if $STASH_RESULT contains "No local changes").

Step 4: Fetch all review feedback

# PR metadata and body
GH_TOKEN=$(gh auth token --user "$DOTFILES_GITHUB_USERNAME") \
  gh pr view <number> --repo <owner/repo> --json title,body,state,author,baseRefName,headRefName,reviewDecision,labels

# Reviews (approval status, review bodies)
GH_TOKEN=$(gh auth token --user "$DOTFILES_GITHUB_USERNAME") \
  gh api repos/<owner>/<repo>/pulls/<number>/reviews

# Issue-level comments
GH_TOKEN=$(gh auth token --user "$DOTFILES_GITHUB_USERNAME") \
  gh api repos/<owner>/<repo>/issues/<number>/comments

Inline feedback comes from GraphQL, which carries the thread ID and resolution state alongside each comment:

GH_TOKEN=$(gh auth token --user "$DOTFILES_GITHUB_USERNAME") \
  gh api graphql -f owner=<owner> -f repo=<repo> -F number=<number> -f query='
query($owner: String!, $repo: String!, $number: Int!) {
  repository(owner: $owner, name: $repo) {
    pullRequest(number: $number) {
      reviewThreads(first: 100) {
        pageInfo { hasNextPage endCursor }
        nodes {
          id
          isResolved
          isOutdated
          path
          line
          viewerCanResolve
          comments(first: 20) {
            nodes { author { login } body url }
          }
        }
      }
    }
  }
}'

Each node is one conversation on the diff:

FieldUse
idPRRT_… node ID — what Step 10 resolves
isResolvedStep 5 categorization
path, lineWhere the feedback points
isOutdatedThe thread points at code the diff has since moved past
comments.nodesThe feedback itself, author first to last
viewerCanResolveWhether resolving will succeed

first: 100 is this connection's ceiling. If pageInfo.hasNextPage is true, fetch the rest with after: $endCursor — a truncated fetch silently drops feedback.

Carry each item's thread id alongside the feedback from here on. Step 10 needs it.

Step 5: Categorize feedback

Separate feedback into:

  • Unresolved items — isResolved: false, active feedback that needs attention
  • Resolved items — isResolved: true, already addressed, skip these

For unresolved items, group by reviewer and present a summary to Josh:

  • What each reviewer is asking for
  • Which items are clear vs. need clarification

Step 6: Feedback handling is the implementer's job

The implementer you dispatch in Step 7 evaluates each point technically before applying it — tracing it against the actual code, pushing back with reasoning on anything wrong or unsafe through its BLOCKED: path, and YAGNI-checking suggested additions rather than adding them reflexively. You don't pre-verify or pre-implement here; the dispatch (Step 7) and the gate (Step 8) carry the work.

What's yours at this step is what the implementer can't do from a cold start:

  • Clarify anything unclear with Josh first. If any item is ambiguous, present what you understand and what needs clarification before dispatching. Don't hand over a partial or guessed-at scope.
  • Carry Josh's direction on each item into the dispatch prompt.

Step 7: Dispatch the implementer

For items Josh agrees should be addressed, dispatch an implementer agent. It starts cold, so everything bearing on the fixes goes in the prompt:

Address review feedback on PR #<n> in <owner/repo>: <title>
## The feedback
<each agreed item verbatim, with the file and line it points at>
## Direction
<the note, and anything Josh decided about how to handle an item>
## Order
Blocking issues (breaks, security), then simple fixes (typos, imports), then complex fixes (refactoring, logic). One at a time, tested individually, no regressions.
## Your scope
Only these items, in the current working tree on branch <branch>. Do not push.

BLOCKED: <question> comes back — 100% confident from the conversation or the review thread → answer it and resume the same agent with SendMessage so its context survives. Anything less → put the question to Josh verbatim with the context needed to answer it; don't soften it, don't answer around it, don't hand him a guess to confirm.

Step 8: Run the gate

Dispatch a code-reviewer agent with the diff (the uncommitted working tree on branch <branch>), the list of changed files, the PR URL, and the implementer's claims quoted verbatim.

  • Code findings → back to the implementer via SendMessage. All of them, no cherry-picking. If one looks wrong, dispatch an agent to check it before dismissing it.
  • Claims findings → to Josh, in your own message: in full when it bears on the topic open with him, otherwise first once that topic is concluded.

One pass: the implementer fixes every finding, then the work goes to Josh. No second reviewer. Tests failing means the feedback isn't addressed — it does not go to Josh with a caveat.

Step 9: Present results and record what Josh approves

For each addressed item, show:

  • What the reviewer asked for
  • What was changed (file:line)
  • Brief explanation of the fix

For items you recommend pushing back on:

  • What the reviewer asked for
  • Why it shouldn't be done (technical reasoning)
  • Draft reply text for Josh to review and post himself if he agrees

Then stop and wait for his review. Changes requested → implementer via SendMessage, feedback verbatim, then back through steps 8 and 9. Signed off → commit only when he explicitly asks.

Keep an approved set as he goes: one entry per item he signs off on, carrying the item, the file and line, and the thread id from Step 4. An item he pushes back on, defers, or wants draft reply text for does not enter the set, and neither does one he hasn't reached yet. This set is the whole input to Step 10 — it is built here, from what he actually said, and never reconstructed later from the diff.

Step 10: Resolve the threads Josh approved

Josh pushes, in his own shell. His git push appearing in the conversation is the go signal. Until it does, wait — nothing resolves, no polling, no asking again.

Once it lands, run one mutation per entry in the approved set:

GH_TOKEN=$(gh auth token --user "$DOTFILES_GITHUB_USERNAME") \
  gh api graphql -f threadId=<thread-id> -f query='
mutation($threadId: ID!) {
  resolveReviewThread(input: { threadId: $threadId }) {
    thread { id isResolved }
  }
}'

Which threads get resolved:

ItemResolve
In the approved set from Step 9Yes
Josh pushed back on, deferred, or wants draft reply textNo — leave it open
Not yet approved, even alongside items that wereNo

Resolution only. No reply comment, no "fixed in abc123". The draft reply text from Step 9 stays Josh's to post.

When a call fails — a stale ID, a thread already resolved, permission you don't have — gh exits non-zero and prints the GraphQL error. Say so plainly, move to the next thread, don't abort the step.

Then report what closed and what stayed open, with the reason for each one left open, so Josh reads it here rather than finding it on GitHub:

Resolved 3 threads: src/auth.ts:42, src/auth.ts:88, README.md:12. Left open: the Cargo.toml pinning thread — you're replying to that one yourself, draft is above.

Josh can also ask for the threads to be closed at any point — a later session, or after the conversation has moved on. Pick up from the approved set and resolve exactly those. If that set is no longer in hand, re-fetch the threads per Step 4, show him the unresolved ones, and ask which to resolve. Never infer the set from the diff or the commits.

Step 11: Restore original state (if needed)

If Josh wants to return to the original branch:

git checkout "$ORIGINAL_BRANCH"
# Only if stash saved something:
git stash pop

Note: Josh may want to stay on the PR branch to continue working. Only restore if asked.

Repository

joshmanders/dotfiles

Path

claude/skills/pr-feedback/SKILL.md

Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.