Addresses all unresolved review threads on a PR you authored. Fetches every unresolved thread (bot and human reviewers), applies code fixes, posts a reply to each thread, resolves it, then re-requests review from all reviewers who had open comments. Saves a full action summary to .vibeos/review.md. Use for the pr_review_address_comments workflow — when changes have been requested on your PR.
Address all unresolved review threads on a PR you authored. Fix the code, reply to every thread, resolve it, re-request review. No human checkpoints — runs to completion.
Arguments: {PR_NUMBER} {org/repo} — e.g. 42 idesi/my-repo
If no arguments provided, auto-detect from the current branch.
# Detect repo and PR if not provided
REPO=$(gh repo view --json nameWithOwner --jq .nameWithOwner)
PR_NUMBER=$(gh pr view --json number --jq .number)
OWNER=${REPO%/*}
NAME=${REPO#*/}
# Verify current branch matches the PR's head branch
PR_BRANCH=$(gh pr view $PR_NUMBER --repo $REPO --json headRefName --jq .headRefName)
CURRENT_BRANCH=$(git branch --show-current)
# If branches don't match, note it in the output but proceed anyway —
# VibeOS dispatches agents into the correct worktree.
Use GraphQL to get every unresolved thread in one call — this is the authoritative source:
gh api graphql -f query="
query {
repository(owner: \"$OWNER\", name: \"$NAME\") {
pullRequest(number: $PR_NUMBER) {
title
headRefName
baseRefName
reviewThreads(first: 100) {
nodes {
id
isResolved
comments(first: 20) {
nodes {
databaseId
author { login }
body
path
line
originalLine
createdAt
}
}
}
}
}
}
}" | jq '[
.data.repository.pullRequest.reviewThreads.nodes[]
| select(.isResolved == false)
]'
Also fetch review-level comments (top-level review body, not attached to a line):
gh api repos/$REPO/pulls/$PR_NUMBER/reviews --paginate \
--jq '.[] | select(.state == "CHANGES_REQUESTED" or .state == "COMMENTED") | {
id, author: .user.login, state, body, submitted_at
}'
Each unresolved thread has:
.id — thread node ID (GraphQL — for resolving).comments.nodes[0].databaseId — comment ID (REST — for replying).comments.nodes[0].author.login — who wrote the original comment.comments.nodes[0].path / .line / .body — location and content.comments.nodes[*] — full thread, including any replies already madeFor each unresolved thread, classify it before acting. Read the full thread (all .comments.nodes) to understand what was concluded:
Actionable — make a code change:
Answer only — reply but no code change:
Decline — reply explaining why, no code change:
Skip — already handled:
Build a working list before acting. Do not skip threads silently — every unresolved thread must be replied to.
Work through threads in this order: Actionable → Answer only → Decline
3a. Read the file
Read the full file at .comments.nodes[0].path
Also read any related test files if the fix may affect them
3b. Apply the fix
Use Edit to make the targeted change. Follow these rules:
3c. Post a reply
gh api /repos/$REPO/pulls/$PR_NUMBER/comments \
-X POST \
-f body="Done — {one sentence describing what was changed and why}" \
-F in_reply_to=<databaseId of first comment in thread>
Reply style: concise and factual. Describe what changed, not what the reviewer said. E.g.:
validateInput() function.".user.id."3d. Resolve the thread
gh api graphql -f query="
mutation {
resolveReviewThread(input: {threadId: \"<thread .id>\"}) {
thread { isResolved }
}
}"
Confirm isResolved: true before moving to the next thread.
Post a reply explaining the decision, then resolve:
gh api /repos/$REPO/pulls/$PR_NUMBER/comments \
-X POST \
-f body="{explanation}" \
-F in_reply_to=<databaseId>
# then resolve same as above
Post a reply acknowledging the concern and explaining why no change was made, then resolve:
"Thanks for flagging this. I've decided to keep the current approach because {reason}.
Happy to address this in a follow-up if you'd like."
If a reviewer left a top-level review body with actionable content, address those the same way. Post a reply to the review using:
gh api /repos/$REPO/issues/$PR_NUMBER/comments \
-X POST \
-f body="{reply to the review-level comment}"
After all threads are resolved, re-request review from every human reviewer who had at least one unresolved thread.
Collect unique reviewer logins from Phase 1 (excluding bots — any login containing [bot] or known bot names like copilot-pull-request-reviewer, dependabot).
# Re-request from each human reviewer
gh api /repos/$REPO/pulls/$PR_NUMBER/requested_reviewers \
-X POST \
--input - <<EOF
{"reviewers": ["<reviewer1>", "<reviewer2>"]}
EOF
Also re-request Copilot if it had unresolved threads:
gh api /repos/$REPO/pulls/$PR_NUMBER/requested_reviewers \
-X POST \
-f 'reviewers[]=copilot-pull-request-reviewer[bot]'
.vibeos/review.mdAfter all threads are processed and review re-requests are sent, write the full summary:
# PR Comment Address: {PR title}
**Repo:** {org/repo} | **PR:** #{N}
**Date:** YYYY-MM-DD
**Threads processed:** {total} | **Fixed:** {N} | **Answered:** {N} | **Declined:** {N}
---
## Summary
{2-3 sentences. How many threads were there? Were they addressed successfully? Any notable decisions?}
---
## Thread Log
### ✅ Fixed
#### `{file}:{line}` — @{reviewer}
**Comment:** {original comment text}
**Action:** {what was changed}
**Reply posted:** "{reply text}"
### 💬 Answered (no code change)
#### @{reviewer} — {brief topic}
**Comment:** {original comment text}
**Reply posted:** "{explanation}"
### ❌ Declined
#### `{file}:{line}` — @{reviewer}
**Comment:** {original comment text}
**Reason:** {why no change was made}
**Reply posted:** "{reply text}"
---
## Re-review Requested From
- @{reviewer1}
- @{reviewer2}
{or: "No re-review requests sent (no reviewers had unresolved threads)"}
.id for replies — replies use the comment's .databaseId (REST), not the thread .id (GraphQL).databaseId to resolve — resolution uses the thread node .id (GraphQL)