agents/skills/address-comments/SKILL.md
Walk every unresolved review thread on a PR, triage each one, reply with a rationale of whether or not the comment will be acted upon, make the code change if warranted, and mark the thread resolved. Use when the user asks to address only the open PR comments without re-running CI, respond to review feedback, resolve review threads, or clear bot comments on a PR.
npx skillsauth add drn/dots address-commentsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
$ARGUMENTS — Optional. PR number, PR URL, or branch name. Defaults to the PR for the current branch.git branch --show-currentgit status --shortgit branch -r 2>/dev/null | grep -oE 'origin/(main|master)' | head -1git remote 2>/dev/null | head -5Walk every unresolved review thread on the target PR. For each thread: read the comment in context, decide whether to act on it, make the code change if warranted, post a reply explaining the rationale, then resolve the thread. End with a clean PR (zero unresolved threads) and a one-paragraph summary of what was done.
This skill does not push the branch into CI babysitting or merge it — use /pr or /merge for that. The focused job here is comment hygiene.
First, derive the upstream repo slug. Prefer the upstream remote if it exists, otherwise origin. The rules below all need it.
REPO_SLUG=$(git remote get-url upstream 2>/dev/null | sed -E 's|.*[:/]([^/]+/[^/.]+)(\.git)?$|\1|')
[ -z "$REPO_SLUG" ] && REPO_SLUG=$(git remote get-url origin | sed -E 's|.*[:/]([^/]+/[^/.]+)(\.git)?$|\1|')
Then determine the target PR by applying these rules in order:
$ARGUMENTS is a GitHub PR URL (https://github.com/<owner>/<repo>/pull/<n>) → parse owner/repo/number from the URL.$ARGUMENTS matches ^[0-9]+$ (pure digits) → run gh pr view <args> --repo "$REPO_SLUG" --json number to verify it is a real PR. If that succeeds, treat as a PR number. If it fails, fall through to the branch-name rule (a branch literally named 42 should still resolve correctly).$ARGUMENTS is any other non-empty string → treat as a branch name and look up the PR for that branch.$ARGUMENTS is empty → use the current branch (git branch --show-current).Find the PR number with gh pr list --repo <slug> --head '<branch>' --state open --json number,url.
Cross-fork PRs. If gh pr list --head '<branch>' returns zero results AND an upstream remote exists, the branch may live on a fork. Resolve the fork owner from the origin remote and retry with the qualified --head '<fork-owner>:<branch>':
FORK_OWNER=$(git remote get-url origin | sed -E 's|.*[:/]([^/]+)/[^/.]+(\.git)?$|\1|')
gh pr list --repo "$REPO_SLUG" --head "$FORK_OWNER:<branch>" --state open --json number,url
If no open PR is found after both attempts, stop and tell the user: No open PR found for <branch>. Open one with /pr first. Do not try to open one — that is /pr's job.
Use the GraphQL API to get all threads in one shot — REST does not expose isResolved. The first: 100 page size is GitHub's connection maximum; pagination handles PRs larger than that (see below). The first: 50 comment window per thread is generous enough for the idempotency-guard scan in Step 3(b) on normal threads. Two ID fields matter here and they are easy to confuse:
reviewThreads.nodes[].id is the thread node ID (an opaque string). Used by the resolveReviewThread mutation in Step 3(f).reviewThreads.nodes[].comments.nodes[].databaseId is the comment integer ID (REST-style). Used by the reply endpoint in Step 3(e).Do not swap them — the URL in Step 3(e) takes the comment's databaseId, not the thread's id.
gh api graphql -F owner=<owner> -F repo=<repo> -F number=<n> -f query='
query($owner: String!, $repo: String!, $number: Int!, $after: String) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
reviewThreads(first: 100, after: $after) {
pageInfo { hasNextPage endCursor }
nodes {
id
isResolved
isOutdated
path
line
comments(first: 50) {
nodes {
databaseId
body
author { login }
path
line
originalLine
diffHunk
}
}
}
}
}
}
}'
Filter to threads where isResolved == false. If there are zero, stop and report: No unresolved review threads on PR #<n>.
Pagination. If pageInfo.hasNextPage is true, the PR has more than 100 review threads. Re-issue the same query with -F after=<endCursor> (the $after variable is already declared in the signature above) and concatenate the pages before continuing. PRs with very large bot-comment volumes (e.g. a CodeRabbit pass on a sweeping refactor) hit this case; do not silently process only the first page.
For each unresolved thread, work through it in this order. Do not parallelize — review threads can suggest conflicting edits to the same lines, and you need to apply them sequentially with the latest file state in mind.
(a) Read the code in context. Open path and read the function or block that contains line — not just the diff hunk. Bot comments often miss surrounding context that changes the verdict.
(b) Identify the author and decide whether to skip.
Capture the viewer login once at the start of Step 3 with gh api user -q .login (call it VIEWER). Then run these two checks in order — the first comment's author and the remaining comments are checked separately and produce different outcomes:
comments.nodes[]. If comments.nodes[0].author.login == VIEWER, this is a thread the agent itself authored. Skip the thread entirely — no reply, no resolve, do not run check 2. (Caveat: on a fork PR where the maintainer and the contributor share a GitHub user, this can over-skip; if the agent is reviewing its own PR, that risk is acceptable.)comments.nodes[1..] (every comment after the first) for any whose author.login == VIEWER. If one exists, the agent already replied on a previous run and the resolve mutation was the step that failed — follow the idempotency path below. If no VIEWER comment appears past index 0, this is a fresh thread — fall through to (c)/(d)/(e)/(f) as normal.Bot context (informational): bot usernames typically end in [bot] (e.g. coderabbitai[bot], qltysh[bot], github-actions[bot], copilot-pull-request-review[bot]). Bots produce a high false-positive rate — evaluate each suggestion on its merits, do not blindly apply.
Idempotency path (only when check 2 fired):
Fixed —, Leaving as-is —, Acknowledged —) is a useful hint but the working tree may have changed since the prior run, so the fresh classification wins.Leaving as-is — but the current code now warrants a fix, or vice versa) → post a follow-up reply in (e) that briefly explains the reclassification (e.g. Reclassified — <one line on what changed since the prior reply>. Fixed at <path>:<line>.). This preserves the audit trail.fixed / left / acknowledged) the fresh classification put it in.(c) Classify. Pick exactly one bucket:
Heuristics for bots:
(d) If (1), make the code change. Edit the file. Keep the change minimal — fix the specific issue, do not refactor surrounding code. If multiple threads point at the same code, batch the related edits before replying so the reply can reference the final state.
(e) Reply with rationale. Post the reply before resolving so there is always an audit trail. The reply must state the verdict and the reasoning in one or two sentences. Do not write "Thanks for the feedback" or other filler.
Reply templates (use these structures, then fill in every <…> placeholder before posting):
Fixed — <one line describing the change>. <path>:<line>.Leaving as-is — <one-sentence reason rooted in the existing pattern or constraint>.Acknowledged — <one-sentence reason this is informational and not actionable>.If you cannot fill in a placeholder (e.g. the fix touched several files and you cannot name one path:line, or the rationale needs context you do not have), stop and surface the thread to the user in the Step 5 summary instead of posting raw <…> placeholder text. Posted replies are visible to reviewers — a literal <one line describing the change> reply is worse than no reply. This applies to every reply this skill posts, including the outdated-thread template referenced in the Notes section below.
Post the reply with the REST endpoint (no native gh command exists, but gh api works). The path uses <n> (pull number) and the comment's databaseId — not the thread's id:
gh api -X POST \
"/repos/<owner>/<repo>/pulls/<n>/comments/<first_comment_databaseId>/replies" \
-f body="<reply text>"
(f) Resolve the thread via GraphQL mutation:
gh api graphql -F threadId=<thread node id> -f query='
mutation($threadId: ID!) {
resolveReviewThread(input: {threadId: $threadId}) {
thread { isResolved }
}
}'
Track a running tally as you go: fixed, left, acknowledged.
If any files changed during Step 3:
git status --short to confirm only the expected files were touched.git add <path1> <path2> ... — list paths explicitly, do not use git add -A, git add ., git commit -a, or git commit --all. The agent may have unrelated dirty state in the working tree and the policy is to ship only the comment-driven edits.Address review: <short summary>Address review comments with a bullet list in the body, one bullet per fix.git push (use git push -u origin HEAD if no upstream is set).If no files changed, skip this step entirely. Do not create an empty commit.
Print a tight summary the user can scan:
PR: <url>
Addressed N threads — F fixed, L left as-is, A acknowledged.
<one-line per fix referencing path:line, if F > 0>
If anything blocked progress (a thread you could not classify, a file you could not edit, a push that was rejected), surface it explicitly instead of silently finishing.
isOutdated == true): still reply and resolve. An outdated thread is often a sign the code already changed in a later commit; the reply should say Already addressed in <sha or "a later commit"> — <one-line on what changed>. then resolve. Fill in both <…> placeholders before posting — same rule as Step 3(e). If you cannot identify the commit or describe the change, escalate to the user instead of posting raw placeholder text.resolveReviewThread requires write access to the repo. If it fails with a permissions error on a fork PR, fall back to replying-only and tell the user the threads need to be resolved manually by a maintainer. (resolveReviewThread on an already-resolved thread is idempotent — it returns the thread with isResolved: true and no error — so a retry after a partial failure in Step 3(f) is safe.)tools
Iteratively run /rereview, fix the findings, and loop until reviewers approve clean. Use for iterative automated review, when you want /rereview to loop until clean, or for a paranoid pre-merge review that auto-addresses every blocker.
development
Generate self-contained HTML visualizations with Plannotator theming. Use for implementation plans, PR explainers, architecture diagrams, data tables, slide decks, and any visual explanation of technical concepts. Plans and PR explainers follow Plannotator's prescriptive approach; all other visual content delegates to nicobailon/visual-explainer.
development
Create reviewed Codex goal setup packages for long-running /goal work. Use when the user wants to turn an idea, backlog, project mission, or vague objective into durable goal files under a project goals slug folder, with Plannotator review gates for brief, narrative plan with acceptance criteria, verification, blockers, and the final /goal prompt.
development
Open Plannotator's browser-based code review UI for the current worktree or a pull request URL, then act on the feedback that comes back.