Reply pr
Answer a PR's review comments the right way, in one pass — triage the UNRESOLVED feedback from review bots (Codex, Claude, CodeRabbit, or any other) and human reviewers, then post a reply on each reviewer's own channel in your voice and resolve every thread so nothing is left hanging. Reply-only: it never edits code, and never posts anything without your approval. Use it whenever someone wants to respond to, reply to, answer, or triage the review comments on a PR — even phrased loosely, like "reply to the PR comments", "go through the CodeRabbit and Codex notes and reply", "respond to the reviewers on #1200 and leave nothing unresolved", or "which review comments should I tackle vs skip?". Do NOT use it to edit the code a reviewer flagged — fixing what a reviewer caught is a separate coding task.From its SKILL.md
npx -y skills add deployhq/reply-pr --skill reply-prAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
13.8 KB, ~3.4k tokens by cl100k_base, as published. Nobody here has run it
reply-pr
Reviewers (bots and humans) leave comments on a PR. Answering them well means: reply on the right channel for each reviewer, go item by item, give a rationale for both what you tackled and what you skipped, and leave no unresolved thread behind. Doing that by hand every time is tedious and easy to get subtly wrong (wrong channel, missing ping, silent skips). This skill makes the correct etiquette the default.
Two hard rules
- Reply-only. Never edit code. Any code fixes were already made in the normal dev flow; the replies just reference those commits. If the user hasn't made a fix yet, note it as a "to fix" in the gate — don't fix it here.
- Never post without the deliberation gate. Always stop, show the user your tackle/skip proposal, and wait for their steer before posting anything. This is the single most important behaviour — the user has repeatedly said "do not action, just critically check" / "do not act, let me know first".
The workflow — three phases
Phase 1 — Analyse (read-only)
- Find the PR and know your role. Use the argument if given (
reply-pr 1016), else the open PR on the current branch. Getowner/repoand the PR number.gather.shreturnsself(the login you post as) andpr_author(the PR's author). The common case isself == pr_author— you're the author answering reviewers; treat your own (self) comments as prior replies and skip them. But don't assume it: ifself != pr_author, you're running as someone else (a reviewer or collaborator), so your own comments may be review findings, not replies — say so at the gate ("running as<self>, who isn't the author<pr_author>") and confirm intent (replying on the author's behalf vs. responding to threads as a reviewer), then adapt. The user can also state their role in the prompt (e.g. "I'm reviewing this, not the author"). - Gather the unresolved feedback. Run the bundled script:
bash scripts/gather.sh <PR_NUMBER> [owner/repo]It fails loudly on any fetch error (never a false "no comments"), paginates, and returns JSON with:self— the login you post as (so you can recognise your own past replies) — andpr_author— the PR's author (compare the two to know your role; step 1).unresolved_threads— inline review threads withisResolved == false(filtered client-side; GraphQL has no server-sideisResolvedargument). Each hasthread_id(for resolving),comment_id(the root comment, forin_reply_to),reviewer(bare login) +is_bot,path,line,url,body(the finding),is_outdated,on_changed_line(did this PR add/remove the flagged line, or is it unchanged context — a pre-existing hint; see step 4), and the thread's recent history:comment_count,last_comment_author,last_comment_body.top_level_comments— PR conversation comments (where Claude-bot posts its whole review). Readscripts/gather.shto see or adjust the queries.
- Classify + dedup. For each item, bucket it by reviewer and channel:
- Reviewer → bucket by
is_bot+ login.is_bot: falseis a human (highest priority). For bots (is_bot: true), match the login (already normalised — the[bot]suffix is stripped):chatgpt-codex-connector= Codex,claude= Claude,coderabbitai= CodeRabbit — use the matrix below. An unknown bot → default to an inline reply with no ping and flag it in the gate for the user to confirm the channel; never silently treat a bot as a human. (Classify byis_bot, not the login suffix: GitHub's GraphQL drops the[bot]suffix while REST keeps it, so the suffix is unreliable.) - Dedup/group across reviewers — multiple bots often flag the same thing. Present it once, noting who raised it.
- Drop CodeRabbit nitpicks. Ignore anything CodeRabbit itself badges as a nitpick / trivial / minor. But a substantive bug CodeRabbit flags is NOT a nitpick — keep it.
- Skip what's already handled (this is the only thing stopping a re-reply
loop, since top-level comments have no "resolved" state). A thread whose
last_comment_authorisselfis one we've already answered — don't re-reply. Amongtop_level_comments, ignore any authored byself(our own past replies), and skip a bot's review that a later comment already answers.
- Reviewer → bucket by
- Critically assess each — don't take it at face value. Verify the claim
against the actual code/diff. Reviewers (especially bots) are often wrong,
out of date, or flagging pre-existing code. Decide a proposed stance:
tackle or skip, with a one-line rationale.
- Spotting pre-existing code (two signals — both advisory; you confirm at the
gate, they never auto-skip):
on_changed_line: falsemeans the bot commented on an unchanged context line — code this PR did not touch. Strong "pre-existing" hint: lean toward skip-as-pre-existing (reply it's not part of this PR's work).true/null→ treat as this PR's code and engage normally.- A finding can be a source-PR duplicate even on an added line — the PR
re-includes code already reviewed in another (merged) PR, so it shows as
added here and
on_changed_linewon't catch it. If you suspect it, confirm cheaply: find the commit behind the line (git blame, or the comment's commit) and rungh api repos/<owner>/<repo>/commits/<sha>/pulls— if it returns another PR, the code came from there. Reply that it's from PR #X (link the originating thread) and skip.
- Common skip rationales: pre-existing / unrelated to this diff, out of scope / scope creep, accepted trade-off (flagged in the PR description for sign-off), or already-shipped (either case above — reply pointing at the origin, don't change anything here).
- Spotting pre-existing code (two signals — both advisory; you confirm at the
gate, they never auto-skip):
Phase 2 — Deliberation gate (STOP here)
Present a single ranked list for the user to steer. Order it humans first
(their feedback outranks the bots), then Codex/Claude, then CodeRabbit. For each
item show: reviewer · channel · a one-line summary · your proposed tackle/skip
· the rationale. Tag anything that looks pre-existing (on_changed_line: false, or a suspected source-PR duplicate) with a "likely pre-existing" marker so
it stands out at a glance. Then stop and wait. Do not post anything yet.
Phrase it as a proposal they edit, e.g. "here's each unresolved comment with what
I'd tackle vs skip and why — tell me what to change." If the user gave a mode
(reply-pr analyse), stop here permanently and don't offer to post.
Phase 3 — Reply (only after the user approves)
For each item the user confirmed, post on the correct channel (matrix below), item by item, with the rationale — for both tackled and skipped items. A silent skip is never acceptable; if we're not doing something, say why. For a tackled item, name the commit SHA and what changed.
Write each reply to a file first, then pass the file to the helper —
post-reply.sh reads the body from a file path (that's what keeps backticks,
quotes and newlines intact); passing the reply text directly will fail with "No
such file". For example: write the reply to /tmp/reply-<id>.md, then
bash scripts/post-reply.sh inline <pr> <comment_id> /tmp/reply-<id>.md (or
toplevel <pr> /tmp/reply-claude.md). See the script header for all args.
Then resolve threads (see "Leaving no thread behind" below).
How to reply to each reviewer
The general rule works for ANY reviewer — known bot, unknown bot, or human. Don't hardcode to a fixed list; reply in kind and ping only when it helps:
- Channel = reply where they commented. An inline review comment → a threaded
inline reply. A top-level PR comment → a top-level comment.
gather.shalready splits these (unresolved_threads= inline,top_level_comments= top-level), so the right channel falls out for every reviewer automatically. - Ping = only reviewers known to want re-notifying (default: no ping). Pinging re-triggers a bot to re-review — some want it, some don't, so when in doubt, don't.
- Humans first — a human's feedback outranks the bots; address it first.
Known-reviewer specifics. Anything not listed falls back to the general rule (reply in kind, no ping) — and flag it in the gate so the user can confirm the channel/ping for a bot you haven't seen before:
Reviewer (is_bot + login) | Channel | Ping |
|---|---|---|
Human (is_bot: false) | in kind (inline or top-level) — highest priority | optional @-mention |
Codex (chatgpt-codex-connector) | inline, threaded under the finding | @codex |
Claude (claude) | top-level comment — its review is a flat PR conversation comment, which GitHub can't thread | @claude — link the #issuecomment-<id> comment, then go point by point |
CodeRabbit (coderabbitai) | inline (substantive items only; nitpicks dropped) | none |
| Any other bot | in kind (wherever it commented) | none — flag in the gate to confirm |
Why Claude is the exception (and its reply can't be threaded): Claude posts its
whole review as one flat PR conversation comment (a GitHub issue comment).
GitHub only threads inline code-review comments — issue comments have no
threading — so a reply to Claude physically cannot be nested under its comment; a
top-level @claude reply that links the #issuecomment is the only thing that
lands. This is also how the Claude bot works by design: an @claude conversation is
a back-and-forth of top-level comments, not a thread. So the detached top-level
reply is intentional, not a limitation to work around. Every other reviewer attaches
findings to code lines, so threaded inline replies keep the conversation where they
put it — which is exactly what "reply where they commented" gives you for free.
Reply voice
Match the user's style (from replies he's posted verbatim). Substantive, technical, concise-but-complete. No emojis.
- Bold per-item headers with the verdict IN the header, e.g.
**1. Nuxt tier-1 observation — skipped (intentionally, with rationale).** - Openers: inline →
**Addressed in \<sha>`.; top-level (Claude) →@claude — replying to your review.**` then a one-line summary of your overall stance, then the per-point list. (Link the review as clean markdown — a pasted raw URL reads clunky in a big top-level comment.) - Verdict vocabulary: "acknowledged, no code change" · "agreed, no code change" · "addressed" · "correct observation, and we deliberately left it as-is".
- Skip rationale leans on: "pre-existing … unrelated to this diff" · "folding it into this PR would be scope creep" · "out of scope" · "flagged in the PR description for conscious sign-off".
- Tackled: name the SHA and what changed ("Added a spec for a
.deployignore-sourced culprit"). - Closers: a brief thank-you — "Thanks for the catch."
Leaving no thread behind
The end state the user wants is zero unresolved/pending threads. Use each
thread's last_comment_author / last_comment_body (from gather.sh) to decide
without re-fetching:
- After replying to an inline thread where our reply is the last word (a skip
we've explained, or a fix we've described and nothing more is expected), resolve
it:
bash scripts/post-reply.sh resolve <thread_id>. - If
last_comment_authoris the reviewer (they replied after us — common with Codex, which often confirms), readlast_comment_body: if it's a confirmation, resolve; if they raised a new point, handle it. If the thread is still awaiting the reviewer's follow-up, do not resolve yet — note it as "waiting on <reviewer>" and settle it on a later run. When in doubt, leave it open and flag it rather than resolving prematurely. - If a thread is
is_outdated: true, the code moved since the comment was made, so the point may no longer apply — check whether it's still valid, say so in the reply if it's moot, and it's usually safe to resolve.
Scripts
scripts/gather.sh <PR> [owner/repo]— fetch unresolved review threads (paginated GraphQL, filtered client-side) + top-level comments as classified JSON, includingselfand each thread's recent history. Fails loudly on any fetch error rather than returning an empty result. Read it if you need to tweak the queries or bot-author filters.scripts/post-reply.sh <mode> ...— post an inline reply (inline <pr> <comment_id> <body_file>), a top-level comment (toplevel <pr> <body_file>), or resolve a thread (resolve <thread_id>). It builds the JSON safely withjqand usesgh api.
Both require an authenticated gh CLI. The scripts are the deterministic
plumbing; the judgement (classify, assess, gate, voice) stays here in the skill.
What ships with it: 2 files
8.3 KB alongside SKILL.md, 2 of them executable
scripts/
- gather.shruns6.1 KB
- post-reply.shruns2.2 KB