| name | respond-to-comments |
| description | Read PR review comments, fix the code issues they raise, and resolve the conversations on GitHub. Handles the full respond-to-feedback cycle. |
| argument-hint | ["pr-number"] |
Respond to Comments — Fix Issues and Resolve PR Comments
Read review comments on your pull request, update the code to address them, and resolve the conversations on GitHub.
Arguments
$ARGUMENTS - PR number or URL (optional)
- If omitted: auto-detect from current branch
Prerequisites
gh CLI installed and authenticated
- Write access to the repository
- On the PR's feature branch (or will checkout)
Instructions
Step 0: Load Hero Configuration
ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
cat "$ROOT/HERO.md" 2>/dev/null || echo "NO_HERO_CONFIG"
Read HERO.md if it exists. This skill uses:
- Repository → commit convention
- Code Quality → linters, formatters, pre-commit
- Code Review Agent → agent name, trigger method, poll method, bot username (for review loop)
If HERO.md is missing, suggest hero-skills:init-hero but proceed with defaults.
Step 1: Identify the PR
If no argument provided, detect from current branch:
BRANCH=$(git branch --show-current)
gh pr list --head "$BRANCH" --json number,title,url,headRefName --jq '.[0]'
If argument provided:
gh pr view $PR_ARG --json number,title,url,headRefName,baseRefName,state
If PR is merged or closed: Report status and stop.
Step 2: Ensure on Correct Branch
CURRENT=$(git branch --show-current)
PR_BRANCH=$(gh pr view $PR_NUMBER --json headRefName --jq '.headRefName')
If not on the PR branch, first check for uncommitted changes:
git status --porcelain
If uncommitted changes exist, STOP and show:
You have uncommitted changes on '$CURRENT':
(list changed files from git status)
Need to switch to '$PR_BRANCH' to address PR comments.
Options:
1. Stash changes (saved as "respond-to-comments: WIP on $CURRENT") — will NOT auto-restore since you're moving to a different branch
2. Cancel — go back and commit or handle changes first
STOP and wait for user to choose. Do NOT switch branches without explicit confirmation.
If user chooses option 1 (stash):
git stash push -m "respond-to-comments: WIP on $CURRENT"
Report: Stashed as: stash@{0} — "respond-to-comments: WIP on $CURRENT". Restore later with: git checkout $CURRENT && git stash pop
Note: Since the user is switching to a different branch to do PR work, do NOT auto-pop the stash. Remind the user in the final summary how to restore.
Then switch to the PR branch:
git fetch origin $PR_BRANCH
git checkout $PR_BRANCH
git pull origin $PR_BRANCH
Step 3: Fetch All Review Comments
gh api repos/{owner}/{repo}/pulls/$PR_NUMBER/comments \
--jq '.[] | {
id: .id,
node_id: .node_id,
path: .path,
line: .line,
original_line: .original_line,
body: .body,
user: .user.login,
in_reply_to_id: .in_reply_to_id,
created_at: .created_at
}'
gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {
id
isResolved
comments(first: 10) {
nodes {
id
databaseId
body
path
line
author { login }
}
}
}
}
}
}
}
' -f owner="$OWNER" -f repo="$REPO" -F pr=$PR_NUMBER
Step 4: Categorize Comments
Group comments into:
- Actionable — code changes requested (bugs, improvements, missing handling)
- Questions — clarification needed, may or may not need code changes
- Acknowledged — nits, praise, or FYI comments that need no code change
- Already resolved — threads already marked resolved
Skip already-resolved threads. Focus on unresolved ones.
Present the categorized list to the user:
PR #123 Review Comments
=======================
Unresolved: N comments in M threads
Actionable (will fix):
1. [file.ts:42] @reviewer - "Handle the null case here"
2. [api.ts:15] @reviewer - "This should validate input before..."
Questions (need your input):
3. [utils.ts:88] @reviewer - "Why not use the existing helper?"
Acknowledged (no code change needed):
4. [README.md:5] @reviewer - "Nice docs 👍"
Already resolved: K threads
Ask the user to confirm the plan before proceeding. The user may:
- Agree with all actionable items
- Disagree with specific comments (skip those)
- Provide answers to questions
- Reclassify comments
Step 5: Fix Code Issues
For each confirmed actionable comment:
- Read the relevant file and surrounding context
- Understand what the reviewer is asking for
- Make the code change
Apply fixes one at a time. After each fix, verify the change makes sense in context.
If a comment is ambiguous: Ask the user for clarification rather than guessing.
Step 6: Run Quality Checks
After all fixes are applied:
if command -v pre-commit > /dev/null 2>&1; then
pre-commit run --all-files
else
echo "NO_PRECOMMIT"
fi
If checks fail, fix the issues before committing.
Step 7: Commit the Fixes
git add CHANGED_FILES
git commit -m "$(cat <<'EOF'
fix: address PR review feedback
- [Summary of fix 1]
- [Summary of fix 2]
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
EOF
)"
Group related fixes into a single commit. If fixes are logically independent and touch different areas, use separate commits.
Step 8: Push Changes
git push origin $(git branch --show-current)
Step 9: Reply to and Resolve Comments
For each addressed comment, reply with what was done and resolve the thread:
gh api repos/{owner}/{repo}/pulls/$PR_NUMBER/comments \
-f body="Fixed — [brief description of what changed]" \
-F in_reply_to=$COMMENT_ID
gh api graphql -f query='
mutation($threadId: ID!) {
resolveReviewThread(input: {threadId: $threadId}) {
thread { isResolved }
}
}
' -f threadId="$THREAD_NODE_ID"
For question comments where the user provided an answer:
gh api repos/{owner}/{repo}/pulls/$PR_NUMBER/comments \
-f body="[User's explanation or rationale]" \
-F in_reply_to=$COMMENT_ID
For acknowledged comments (praise, FYI):
gh api graphql -f query='
mutation($threadId: ID!) {
resolveReviewThread(input: {threadId: $threadId}) {
thread { isResolved }
}
}
' -f threadId="$THREAD_NODE_ID"
Step 9a: Always Post an Improvements Summary Comment
Per-thread replies (Step 9) explain each fix in isolation. They do not, on their own, give a reviewer a single place to see what changed across the whole respond cycle. Always post one consolidated improvements comment, in addition to (never instead of) the per-thread replies from Step 9. Even when no code changes were made (e.g., all comments were questions or were declined), still post the comment — silence on a no-fix cycle hides the fact that the cycle ran.
Group by category. For each fix, name the file:line, the original feedback, and the change in one sentence. For each declined item, give the reason — "low impact", "out of scope", "would conflict with X" — so reviewers can tell whether to re-raise it.
Rendering rules (apply before posting, not as part of the template):
- Omit any section whose count is zero, or write
- none inline. Do not post empty section bodies.
- Substitute every
FILE:LINE, REVIEWER_FEEDBACK, FIX_DESCRIPTION, SHA1, etc. placeholder with the real value before invoking gh pr comment. The literal placeholder strings must never appear in the posted comment.
COMMENT_BODY=$(cat <<'EOF'
**Code changes (X applied):**
- FILE:LINE — REVIEWER_FEEDBACK — FIX_DESCRIPTION
**Questions answered (Y):**
- FILE:LINE — QUESTION — ANSWER_OR_RATIONALE
**Declined (Z, with rationale):**
- FILE:LINE — REVIEWER_FEEDBACK — REASON_FOR_DECLINING
Commits: SHA1, SHA2
Remaining unresolved threads: N (LIST_OR_NONE)
---
_Generated using hero-skills._
EOF
)
if ! gh pr comment $PR_NUMBER --body "$COMMENT_BODY"; then
echo "ERROR: Failed to post improvements comment. Body follows — paste manually:"
printf '%s\n' "$COMMENT_BODY"
exit 1
fi
If nothing was applied or replied to (rare — the cycle should not run otherwise), still post the comment stating "No changes this cycle" and explaining why. Do not fall silent.
Step 9b: Update the PR Description When Scope or Behavior Changed
Reviewer feedback often expands a PR's scope — adding new files, hardening a code path, removing an option. When that happens, the PR title and body must reflect the new shape. Reviewers should not have to dig through commits to find what the PR now claims to do.
Decide whether to update by checking each:
- New files added in this respond cycle that weren't in the original description → update.
- Critical / important fixes that change observable behavior (security, data-loss, correctness, fail-closed defaults, API contracts) → update. Reviewer-driven changes are more likely to alter behavior than self-review fixes, so this rule is intentionally broader than
hero-skills:review-pr's.
- Reverted or removed features → update (and remove the corresponding line from the description).
- Pure typo / nit / comment fixes → leave the PR description alone.
- Tie-breaker: if uncertain, default to update. A spurious changeset entry is cheap; a missing one misleads reviewers.
When an update is warranted:
gh pr view $PR_NUMBER --json title,body --jq '{title, body}'
Draft the full new body (preserving the existing structure — Summary, Changesets, Test Plan — and appending entries for this iteration's work), ending with _Generated using hero-skills._ as the final line. Do not paste the heredoc literally — gh pr edit --body fully replaces the body, so the heredoc must contain the entire drafted Markdown:
gh pr edit $PR_NUMBER --title "NEW_TITLE_UNDER_70_CHARS" --body "$(cat <<'EOF'
DRAFTED_FULL_BODY_HERE
EOF
)"
Substitute DRAFTED_FULL_BODY_HERE with the actual drafted Markdown before running — the drafted body must end with _Generated using hero-skills._. Never run the snippet with the placeholder still in place — it would overwrite the PR description with the literal string DRAFTED_FULL_BODY_HERE.
Rules:
- Append new changeset entries; never delete prior ones (commit history is the source of truth).
- Update Test Plan checkboxes if respond fixes added new verification steps.
- Keep the title under 70 chars; use the body for detail.
- If the PR title's scope shifted (e.g., a "feat" PR now also has a critical "fix"), update the title.
If no description update is needed, note it in the Step 10 summary as "PR description left as-is — fixes were limited to surface tweaks, no scope change."
Step 10: Summary
Substitute the {...} placeholders before printing. Concrete examples for the PR description: line:
PR description: updated (added entry for "fail-closed on API error" to Changesets, refreshed Test Plan)
PR description: left as-is (typo and comment fixes only, no scope change)
Respond Summary
=======================
PR: #{number} - {pr-title}
Branch: {pr-branch}
Comments Addressed:
Fixed: X (code changes made)
Replied: Y (questions answered)
Acknowledged: Z (resolved without changes)
Skipped: W (user chose to skip)
Commits: N new commits pushed
Threads Resolved: M of T
PR description: {updated | left as-is} ({one-line reason})
Remaining unresolved: {list any, if applicable}
URL: {pr-url}
Next step: (pick exactly one)
- This cycle touched dependency files (
package.json, pyproject.toml, lockfiles, .github/workflows/*.yml version pins, or Dockerfile* — same definition as push-pr's equivalent bullet): Next step: hero-skills:harden (print only — model-invocation-restricted, cannot auto-run).
- Otherwise:
Next step: hero-skills:ship-pr — @auto-approve, merge, reset to default branch (blocks if any threads remain unresolved) (offer to auto-run: ask "Run it now? [y/N]", invoke via Skill tool on yes).
If changes were stashed in Step 2, remind the user:
Note: You have stashed changes from {original-branch}.
To restore: git checkout {original-branch} && git stash pop
Step 11: Review Loop (when Code Review Agent is configured)
After completing the initial respond cycle (Steps 3-10), check if HERO.md has a Code Review Agent configured (agent is not none). If so, and the user passed --loop or confirms they want to loop, enter the iterative review loop.
Skip this step entirely if:
- No Code Review Agent is configured in HERO.md
- The user explicitly declines the loop
Loop Setup
ITERATION=1
MAX_ITERATIONS=5
REVIEW_AGENT=$(grep '^\- agent:' "$ROOT/HERO.md" | sed 's/- agent: //' | head -1)
TRIGGER=$(grep '^\- trigger:' "$ROOT/HERO.md" | sed 's/- trigger: //' | head -1)
POLL_METHOD=$(grep '^\- poll-method:' "$ROOT/HERO.md" | sed 's/- poll-method: //' | head -1)
Report to user:
Review Loop enabled (agent: REVIEW_AGENT, max iterations: 5)
Starting iteration 1...
Loop Body (repeat until exit condition)
11a. Trigger external review
Trigger depends on the configured method:
-
Comment trigger (e.g., Greptile): Post a comment on the PR
gh pr comment $PR_NUMBER --body "TRIGGER_TEXT"
-
Label trigger (e.g., some CodeRabbit setups): Add a label
gh pr edit $PR_NUMBER --add-label "TRIGGER_LABEL"
-
Auto on push: No action needed — the push from Step 8 already triggers it
11b. Poll for review completion
Poll every 30 seconds based on configured poll-method:
-
check-runs: Wait for the review agent's check run to complete
gh api repos/OWNER/REPO/commits/HEAD_SHA/check-runs \
--jq '.check_runs[] | select(.app.slug == "AGENT_SLUG") | {status: .status, conclusion: .conclusion}'
-
comments: Wait for a new comment from the review agent bot
gh api repos/OWNER/REPO/pulls/$PR_NUMBER/comments \
--jq '[.[] | select(.user.login == "AGENT_BOT_USERNAME")] | last | {id: .id, created_at: .created_at, body: .body}'
-
pipeline-status: Wait for pipeline job to finish
gh api repos/OWNER/REPO/actions/runs?head_sha=HEAD_SHA \
--jq '.workflow_runs[] | select(.name | contains("AGENT_NAME")) | {status: .status, conclusion: .conclusion}'
Timeout: If no result after 5 minutes of polling, report timeout and ask user whether to retry or exit the loop.
11c. Parse results
After the external review completes:
gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {
id
isResolved
comments(first: 10) {
nodes {
body
author { login }
}
}
}
}
}
}
}
' -f owner="$OWNER" -f repo="$REPO" -F pr=$PR_NUMBER
Filter for threads authored by the review agent bot that are unresolved. Count them.
If the agent provides a confidence score (e.g., 5/5 in PR description or comment), extract it.
11d. Check exit conditions
Exit the loop if ANY of these are true:
- Zero unresolved comments from the review agent
- Confidence score is perfect (e.g.,
5/5)
- Iteration count >= MAX_ITERATIONS (5)
If exiting due to max iterations with remaining issues, warn the user:
Review loop reached maximum iterations (5).
Remaining unresolved comments: N
Manual review may be needed for the remaining items.
11e. Fix, commit, push, resolve
If not exiting, repeat the respond cycle for the new comments. The mandatory improvements comment and PR-description decision apply on every iteration — silently skipping them inside the loop would defeat the always-post contract.
- Categorize the agent's new comments (same as Step 4)
- Present to user for confirmation
- Fix actionable items (same as Step 5)
- Run quality checks (same as Step 6)
- Commit fixes (same as Step 7)
- Push (same as Step 8)
- Reply to and resolve addressed threads (same as Step 9)
- Post the consolidated improvements comment (same as Step 9a)
- Update PR title/body if the iteration's fixes shifted scope or behavior (same as Step 9b)
Increment iteration counter and loop back to 11a.
11f. Loop summary
After exiting the loop, report:
Review Loop Complete
====================
Agent: REVIEW_AGENT
Iterations: N of MAX_ITERATIONS
Total comments addressed: X
Total commits: Y
Per-iteration breakdown:
Iteration 1: A comments fixed, B resolved
Iteration 2: C comments fixed, D resolved
...
Final status: [Clean / N unresolved comments remain]
URL: PR_URL
Notes
- Always ask the user before making changes — don't blindly follow every review comment
- Some comments may conflict with each other; flag these to the user
- If a reviewer's suggestion would introduce a bug or regression, explain why to the user
- Never force-push unless the user explicitly requests it
- Reply to comments before resolving them so reviewers see what changed
- If the PR has many comments, work through them file-by-file for efficiency