$npx -y skills add tobihagemann/turbo --skill reply-to-pr-threadsDraft, confirm, and post replies to GitHub PR review threads. Handles per-category reply formatting, re-fetches thread resolution state so auto-resolved threads are skipped, and posts via GraphQL. Use when the user asks to \"reply to PR threads\", \"post PR thread replies\", or \
| 1 | # Reply to PR Threads |
| 2 | |
| 3 | Draft replies for a processed review-thread list, confirm with the user, and post the surviving drafts. |
| 4 | |
| 5 | ## Step 1: Run `/github-voice` Skill |
| 6 | |
| 7 | Run the `/github-voice` skill to load voice rules and the insider-vs-outsider detection. |
| 8 | |
| 9 | ## Step 2: Re-fetch Thread State |
| 10 | |
| 11 | Auto-detect owner, repo, and PR number from the current branch if not provided, then query the current resolution state: |
| 12 | |
| 13 | ```bash |
| 14 | gh api graphql -f query=' |
| 15 | query($owner: String!, $repo: String!, $pr: Int!) { |
| 16 | repository(owner: $owner, name: $repo) { |
| 17 | pullRequest(number: $pr) { |
| 18 | reviewThreads(first: 100) { |
| 19 | nodes { id isResolved } |
| 20 | } |
| 21 | } |
| 22 | } |
| 23 | }' -f owner='{owner}' -f repo='{repo}' -F pr={pr_number} |
| 24 | ``` |
| 25 | |
| 26 | Drop threads whose `isResolved` is now true. Reviewers or bots such as CodeRabbit may resolve threads after the original fetch, and drafting replies for them is wasted work. |
| 27 | |
| 28 | ## Step 3: Draft Replies |
| 29 | |
| 30 | Use the processed-thread list from conversation context. Each entry has: thread id, file path, line, category (`fix`, `skip`, `answer`, or `clarify`), and per-category payload. |
| 31 | |
| 32 | Keep every reply to one or two sentences. No bullet-point reasoning. No bolded labels. |
| 33 | |
| 34 | **fix**: payload is a commit SHA, optionally with a divergence note. |
| 35 | |
| 36 | ``` |
| 37 | Fixed in <commit-sha>. |
| 38 | ``` |
| 39 | |
| 40 | Only add a brief sentence after the SHA when the fix meaningfully diverges from what the reviewer suggested. Otherwise the SHA alone is enough. |
| 41 | |
| 42 | **skip**: payload is the skip reasoning. State the reasoning directly. Do not apologize or hedge. |
| 43 | |
| 44 | **answer**: payload is raw answer text from `/answer-reviewer-questions`. Tighten to one or two sentences and apply `/github-voice` rules. Do not cite transcripts or mention Claude. The reply reads as the implementer's own explanation. |
| 45 | |
| 46 | **clarify**: payload is a user-directed question. Draft it as directed. |
| 47 | |
| 48 | ## Step 4: Present Drafts and Confirm |
| 49 | |
| 50 | Output all drafts as text, grouped by file: |
| 51 | |
| 52 | ``` |
| 53 | ### <file-path> |
| 54 | |
| 55 | **Thread <id>** (<category>, line <line>) |
| 56 | Reviewer: <original comment, truncated if long> |
| 57 | Reply: <drafted reply> |
| 58 | ``` |
| 59 | |
| 60 | Then use `AskUserQuestion` to ask whether to post. Offer: |
| 61 | |
| 62 | - **Post** — post all drafts as shown |
| 63 | - **Cancel** — skip posting |
| 64 | |
| 65 | ## Step 5: Post Replies |
| 66 | |
| 67 | For each approved draft, write the drafted reply to `.turbo/pr/thread-<thread-id>.md` with the Write tool, then post via the reply mutation: |
| 68 | |
| 69 | ```bash |
| 70 | gh api graphql -f query=' |
| 71 | mutation($threadId: ID!, $body: String!) { |
| 72 | addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) { |
| 73 | comment { id } |
| 74 | } |
| 75 | }' -f threadId='<thread-id>' -F body=@.turbo/pr/thread-<thread-id>.md |
| 76 | ``` |
| 77 | |
| 78 | Substitute `<thread-id>` with the thread's id for each post. Report what was posted and what was skipped (due to auto-resolution between re-fetch and posting). |
| 79 | |
| 80 | Then use the TaskList tool and proceed to any remaining task. |
| 81 | |
| 82 | ## Rules |
| 83 | |
| 84 | - Never resolve or dismiss a review thread. Only reply. Let the reviewer resolve. |
| 85 | - If a post mutation fails because the thread is already resolved, log the skip and continue with the rest. |