{"slug":"reply-to-pr-threads-2","title":"reply-to-pr-threads","summary":"Draft, 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 \"draf","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-08-23T08:58:56.831103Z","repo":{"url":"https://github.com/tobihagemann/turbo","stars":403,"forks":31,"license":"MIT","updatedAt":"2026-09-24T14:46:11Z"},"bodyHtml":"<hr>\n<h2>name: reply-to-pr-threads\ndescription: \"Draft, 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 \"draft PR reply messages\".\"</h2>\n<h1>Reply to PR Threads</h1>\n<p>Draft replies for a processed review-thread list, confirm with the user, and post the surviving drafts.</p>\n<h2>Step 1: Re-fetch Thread State</h2>\n<p>Auto-detect owner, repo, and PR number from the current branch if not provided, then query the current resolution state:</p>\n<pre><code>gh api graphql -f query='\nquery($owner: String!, $repo: String!, $pr: Int!) {\n  repository(owner: $owner, name: $repo) {\n    pullRequest(number: $pr) {\n      reviewThreads(first: 100) {\n        nodes { id isResolved }\n      }\n    }\n  }\n}' -f owner='{owner}' -f repo='{repo}' -F pr={pr_number}\n</code></pre>\n<p>Drop threads whose <code>isResolved</code> is now true. Reviewers or bots such as CodeRabbit may resolve threads after the original fetch, and drafting replies for them is wasted work.</p>\n<h2>Step 2: Run <code>$github-voice</code> Skill</h2>\n<p>Run the <code>$github-voice</code> skill to load voice rules and the insider-vs-outsider detection.</p>\n<h2>Step 3: Draft Replies</h2>\n<p>Use the processed-thread list from conversation context. Each entry has: thread id, file path, line, category (<code>fix</code>, <code>skip</code>, <code>answer</code>, or <code>clarify</code>), and per-category payload.</p>\n<p>Keep every reply to one or two sentences. No bullet-point reasoning. No bolded labels.</p>\n<p><strong>fix</strong>: payload is a commit SHA, optionally with a divergence note.</p>\n<pre><code>Fixed in &lt;commit-sha&gt;.\n</code></pre>\n<p>Only add a brief sentence after the SHA when the fix meaningfully diverges from what the reviewer suggested. Otherwise the SHA alone is enough.</p>\n<p><strong>skip</strong>: payload is the skip reasoning. State the reasoning directly. Do not apologize.</p>\n<p><strong>answer</strong>: payload is raw answer text from <code>$answer-reviewer-questions</code>. Tighten to one or two sentences and apply <code>$github-voice</code> rules. Do not cite transcripts or mention Claude. The reply reads as the implementer's own explanation.</p>\n<p><strong>clarify</strong>: payload is a user-directed question. Draft it as directed.</p>\n<h2>Step 4: Present Drafts and Confirm</h2>\n<p>Output all drafts as text, grouped by file:</p>\n<pre><code>### &lt;file-path&gt;\n\n**Thread &lt;id&gt;** (&lt;category&gt;, line &lt;line&gt;)\nReviewer: &lt;original comment, truncated if long&gt;\nReply: &lt;drafted reply&gt;\n</code></pre>\n<p>Then use <code>request_user_input</code> to ask whether to post. Offer:</p>\n<ul>\n<li><strong>Post</strong> — post all drafts as shown</li>\n<li><strong>Cancel</strong> — skip posting</li>\n</ul>\n<h2>Step 5: Post Replies</h2>\n<p>For each approved draft, write the drafted reply to <code>.turbo/pr/thread-&lt;thread-id&gt;.md</code> with <code>apply_patch</code>, then post via the reply mutation:</p>\n<pre><code>gh api graphql -f query='\nmutation($threadId: ID!, $body: String!) {\n  addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {\n    comment { id }\n  }\n}' -f threadId='&lt;thread-id&gt;' -F body=@.turbo/pr/thread-&lt;thread-id&gt;.md\n</code></pre>\n<p>Substitute <code>&lt;thread-id&gt;</code> with the thread's id for each post.</p>\n<p>Report the outcome as a table, one row per thread, keeping every cell to a single line:</p>\n<table>\n<thead>\n<tr>\n<th>Thread</th>\n<th>Category</th>\n<th>Outcome</th>\n</tr>\n</thead>\n</table>\n<p>Where Outcome is one of:</p>\n<ul>\n<li><strong>Posted</strong> — the reply was posted</li>\n<li><strong>Skipped</strong> — name the reason, such as auto-resolution between the re-fetch and the post</li>\n</ul>\n<p>Keep the report to the table. When the table would be empty, report one line stating that no replies were posted and why instead.</p>\n<p>Then call <code>update_plan</code> to mark this step completed and continue with the next step of the active workflow.</p>\n<h2>Rules</h2>\n<ul>\n<li>Never resolve or dismiss a review thread. Only reply. Let the reviewer resolve.</li>\n<li>If a post mutation fails because the thread is already resolved, log the skip and continue with the rest.</li>\n</ul>\n","files":[{"path":"SKILL.md","sizeBytes":3797,"isText":true}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-08-23T09:05:47.604793Z","sha256":"50F4C8C548637DACB42C37DBAAE57F086B711C3EB83A33BF23EA5FEEF7396A66","sizeBytes":1902},"review":null,"source":{"repositoryUrl":"https://github.com/tobihagemann/turbo","path":"codex/skills/reply-to-pr-threads","license":"MIT","commit":"733e9833b3f2bbd29c5811a5eeb22ff358ec2b78","subtreeSha":"BCA29189F1E72C38D1A31FDEFF9C5F92418D3DE8DF2C120E69E8396379053D22","lastSyncedAt":"2026-09-25T07:38:01.358394Z"},"reviewedAt":"2026-08-23T09:18:45.143499Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/tobihagemann/turbo/tree/main/codex/skills/reply-to-pr-threads"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install tobihagemann-turbo@llmmart"},{"target":"git","command":"git clone https://github.com/tobihagemann/turbo.git"}]}