Install
$ agentstack add skill-loomantix-claude-platform-copilot-review ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Copilot Review Resolver
You are helping a developer address GitHub Copilot code review comments on a pull request. Follow a systematic approach: fetch comments, create an isolated worktree, analyze each comment, address or defer appropriately, reply to confirm resolution, and iterate until complete.
Core Principles
- Analyze before acting: Understand the Copilot comment fully before making changes
- Quality over speed: Make thoughtful fixes that address the root cause
- Scope discipline: Defer scope-creep issues to separate GitHub issues
- Clear communication: Reply to each comment explaining how it was addressed
- Use TodoWrite: Track all comments and their resolution status
Phase 0: Initialization
Goal: Validate the PR and set up the working environment
Arguments: $ARGUMENTS (PR number)
Actions:
- Validate PR number from arguments
- If missing, ask user for the PR number
- Fetch PR details:
``bash gh pr view --json number,title,headRefName,baseRefName,state ``
- Verify PR is open and ready for review
- If closed or merged, notify user and exit
- Create todo list with all phases
Phase 1: Fetch Copilot Comments
Goal: Load and categorize all Copilot code review comments
IMPORTANT — jq quoting pitfalls:
- Complex
--jqexpressions passed directly togh apiare fragile because of shell/YAML quoting (especially with!=, embedded\n, and nested quotes) !=is a valid jq operator; the issues come from how the shell parsesgh api --jqarguments, not from jq itself- Prefer simple
--jqfilters (or none), save raw JSON to a temp file first, then run richerjqqueries against that file - The temp file approach also enables reuse across multiple queries without extra API calls
Actions:
- Fetch all PR comments to a temp file (single API call, reused for all queries):
``bash gh api --paginate repos/{owner}/{repo}/pulls//comments > /tmp/pr--comments.json ``
- Extract Copilot top-level comments (those without
in_reply_to_id):
``bash jq '[.[] | select((.user.login | test("copilot"; "i")) and (.in_reply_to_id == null or .in_reply_to_id == 0)) | {id, path, line, body: (.body | split("\n")[0][:120])}]' /tmp/pr--comments.json ``
- Extract reply target IDs (which comments already have human replies):
``bash jq '[.[] | select(.in_reply_to_id > 0) | .in_reply_to_id] | unique' /tmp/pr--comments.json ``
- Compute unaddressed comments (Copilot comments whose ID is not in the replied set):
``bash jq '[.[] | select((.user.login | test("copilot"; "i")) and (.in_reply_to_id == null or .in_reply_to_id == 0)) | {id, path, line, body: (.body | split("\n")[0][:120])}] as $all | [.[] | select(.in_reply_to_id > 0) | .in_reply_to_id] | unique as $replied | $all | map(select(.id as $cid | $replied | index($cid) | not))' /tmp/pr--comments.json ``
- Read full body of each unaddressed comment when needed:
``bash jq '.[] | select(.id == ) | .body' /tmp/pr--comments.json ``
- Categorize each unaddressed comment after reading the file:
- Actionable: Issues that should be fixed in this PR
- Scope-creep: Issues that are valid but outside the PR's scope
- Invalid/False-positive: Comments that don't apply or are incorrect
- Present summary to user:
- Total Copilot comments found
- Number unaddressed (no human reply yet)
- Brief preview of each unaddressed comment (file:line + first line of body)
- Ask user for confirmation before proceeding
Phase 2: Worktree Setup
Goal: Create an isolated environment for making changes
Actions:
- Get PR branch name from Phase 0 data (
headRefName)
- Check if worktree already exists:
``bash git worktree list | grep "copilot-review-" ``
- Create or reset worktree:
If worktree doesn't exist:
``bash git fetch origin git worktree add worktrees/copilot-review- origin/ ``
If worktree exists:
``bash git -C worktrees/copilot-review- fetch origin git -C worktrees/copilot-review- checkout git -C worktrees/copilot-review- reset --hard origin/ ``
- Verify worktree is ready:
``bash git -C worktrees/copilot-review- status git -C worktrees/copilot-review- log --oneline -3 ``
- Set worktree path for subsequent operations:
- All file reads/edits should use the worktree path:
worktrees/copilot-review-/ - All git commands should use:
git -C worktrees/copilot-review-
Phase 3: Comment Resolution Loop
Goal: Address each unaddressed Copilot comment systematically
For each unaddressed comment, perform the following cycle:
Step 3.1: Analyze Comment
- Read the file at the path specified in the comment
- Understand the context:
- What is Copilot pointing out?
- Is the concern valid?
- Is this within the PR's scope?
- Classify the resolution approach:
- Fix: Apply a code change to address the issue
- Defer: Valid concern but out of scope - create a GitHub issue
- Dismiss: False positive or incorrect suggestion - explain why
Step 3.2: Execute Resolution
If Fix:
- Make the necessary code changes
- Run relevant linting:
just lint-fix - Run relevant tests if applicable
- Prepare a clear explanation of what was changed
If Defer:
- Create a GitHub issue:
``bash gh issue create --title "" \ --label "tech-debt,from-copilot-review" \ --body "## Context\n\nThis issue was identified during Copilot code review of PR #.\n\n## Original Comment\n\n\n\n## Recommendation\n\n\n\n## Related\n\n- PR #" ``
- Note the issue number for the reply
If Dismiss:
- Prepare a clear explanation of why this is a false positive or doesn't apply
Step 3.3: Record the Resolution (no reply yet)
Do NOT post a reply here. The reply needs to reference the real commit SHA, which doesn't exist until after Phase 4's push. Posting now means the SHA is a placeholder the model substitutes with garbage (or skips the reply entirely). Replies are posted in Phase 4 step 6 using the real SHA.
- Build a resolution row in memory for this comment:
``text { "comment_id": , "path": "", "line": , "resolution": "fix" | "defer" | "dismiss", "explanation": "", "defer_issue_url": "" } ``
- Accumulate rows in an in-memory list as you iterate the comment loop. Do not try to append-write the JSON file row-by-row — naïve append produces invalid JSON. Phase 3 finishes by writing the full array once.
Step 3.4: Update Progress
- Mark the comment as resolved in TodoWrite (the resolution is recorded; only the reply post is deferred).
- Move to the next unaddressed comment.
- Repeat Steps 3.1-3.4 until all comments are processed.
Step 3.5: Persist resolutions before Phase 4
After the loop completes, write the accumulated array once:
Write(file_path="/tmp/copilot-review--resolutions.json",
content=)
Phase 4 step 6 reads this file and posts one reply per row.
Phase 4: Commit, Push, and Post Replies
Goal: Save changes, push to the PR branch, verify the push landed, then post one reply per recorded resolution using the real commit SHA.
Actions:
- Verify changes (in worktree):
``bash git -C worktrees/copilot-review- status git -C worktrees/copilot-review- diff ``
- Run quality checks (in worktree):
``bash cd worktrees/copilot-review- && just lint-fix cd worktrees/copilot-review- && just typecheck ``
- Commit changes (only if there are code changes from "fix" resolutions):
``bash cd worktrees/copilot-review- && git add -A && git commit -m "fix(review): address copilot feedback" ``
- Push to remote and capture the real SHA:
``bash git -C worktrees/copilot-review- push origin PUSHED_SHA=$(git -C worktrees/copilot-review- rev-parse HEAD) PUSHED_SHA_SHORT=$(git -C worktrees/copilot-review- rev-parse --short=8 HEAD) ``
If push is rejected (remote has new commits):
``bash git -C worktrees/copilot-review- pull --rebase origin git -C worktrees/copilot-review- push origin PUSHED_SHA=$(git -C worktrees/copilot-review- rev-parse HEAD) PUSHED_SHA_SHORT=$(git -C worktrees/copilot-review- rev-parse --short=8 HEAD) ``
- Verify the push landed on the PR head before posting replies — GitHub's PR API is eventually consistent, so
headRefOidcan lag the actual ref by a few seconds:
``bash verify_pr_head() { local attempt for attempt in 1 2 3 4; do local pr_head pr_head=$(gh pr view --json headRefOid --jq '.headRefOid') if [[ "$pr_head" == "$PUSHED_SHA" ]]; then return 0 fi sleep $(( attempt * 2 )) # 2s, 4s, 6s, 8s — total ~20s ceiling done echo "PR head ($pr_head) does not match pushed SHA ($PUSHED_SHA) after retries — investigate before replying" >&2 return 1 } verify_pr_head || exit 1 ``
- Post replies now that the real SHA is in hand. Read
/tmp/copilot-review--resolutions.jsonand post one reply per row, building each body with${PUSHED_SHA_SHORT}substituted inline.
Reply body templates:
For Fix:
`` Fixed in ${PUSHEDSHASHORT}`.
```
For Defer:
``` Deferred — tracking in .
```
For Dismiss:
``` Dismissing — false positive.
```
Post each reply via:
``bash gh api -X POST repos/{owner}/{repo}/pulls//comments//replies \ -f body="" ``
If any single reply POST fails, log it and continue with the rest — partial reply coverage is better than none. Surface the count of failed-reply POSTs in the Phase 7 completion summary.
Phase 5: Re-trigger Copilot Review
Goal: Get fresh Copilot feedback on the updated code
Actions:
- Check if Copilot auto-reviews on push
- Wait 30-60 seconds for automatic review
- If no automatic review, trigger manually via GraphQL.
IMPORTANT: Copilot is a Bot, not a User — gh pr edit --add-reviewer and the REST requested_reviewers endpoint do not work for Copilot. Use the GraphQL requestReviews mutation with botIds:
``bash PR_NODE=$(gh pr view --json id --jq '.id') gh api graphql \ -f query='mutation($prId:ID!,$botIds:[ID!]){requestReviews(input:{pullRequestId:$prId,botIds:$botIds,union:true}){pullRequest{id}}}' \ -f prId="$PR_NODE" \ -f botIds='BOT_kgDOCnlnWA' ``
Copilot bot node id is BOT_kgDOCnlnWA (constant). Verify with gh api repos/{owner}/{repo}/pulls//requested_reviewers --jq '.users[].login' → expected Copilot. The mutation is idempotent — safe to call across iterations.
- Wait for the new Copilot review to appear (filter by Copilot's login, then take the most recent):
``bash gh api repos/{owner}/{repo}/pulls//reviews \ --jq '[.[] | select(.user.login | test("copilot"; "i"))] | last | {id, submitted_at, user: .user.login}' ``
- Fetch new comments and check for any new unaddressed items
Phase 6: Iteration Decision
Goal: Determine if another resolution cycle is needed
Actions:
- Count new unaddressed comments from the latest Copilot review
- If new comments exist:
- Present summary to user
- Ask: "Found X new Copilot comments. Continue resolving?"
- If yes, return to Phase 3
- If no, proceed to Phase 7
- If no new comments:
- Proceed to Phase 7
Phase 7: Completion Summary
Goal: Summarize all work done and provide next steps
Actions:
- Generate summary report:
- Total comments addressed
- Comments fixed with code changes
- Comments deferred to GitHub issues (with issue links)
- Comments dismissed with reasons
- Commits created
- Cleanup worktree (optional, ask user):
``bash git worktree remove worktrees/copilot-review- ``
- Provide next steps:
- Review the PR to ensure all changes are correct
- Merge the PR when ready
- Follow up on any deferred GitHub issues
Posting Inline PR Review Comments (API Reference)
GitHub's PR comment API (POST repos/{owner}/{repo}/pulls/{pull_number}/comments) uses a oneOf schema. You must use exactly one of these patterns:
Line-level comment (single line)
jq -n \
--arg body "$BODY" \
--arg commit_id "$HEAD_SHA" \
--arg path "$FILE_PATH" \
--argjson line $LINE_NUM \
--arg side "RIGHT" \
'{body: $body, commit_id: $commit_id, path: $path, line: $line, side: $side}' \
| gh api -X POST "repos/{owner}/{repo}/pulls/{pull_number}/comments" --input -
Line-level comment (multi-line range)
jq -n \
--arg body "$BODY" \
--arg commit_id "$HEAD_SHA" \
--arg path "$FILE_PATH" \
--argjson line $END_LINE \
--argjson start_line $START_LINE \
--arg side "RIGHT" \
'{body: $body, commit_id: $commit_id, path: $path, line: $line, start_line: $start_line, side: $side, start_side: "RIGHT"}' \
| gh api -X POST "repos/{owner}/{repo}/pulls/{pull_number}/comments" --input -
File-level comment (no line anchor)
jq -n \
--arg body "$BODY" \
--arg commit_id "$HEAD_SHA" \
--arg path "$FILE_PATH" \
--arg subject_type "file" \
'{body: $body, commit_id: $commit_id, path: $path, subject_type: $subject_type}' \
| gh api -X POST "repos/{owner}/{repo}/pulls/{pull_number}/comments" --input -
Important constraints
linemust be within the PR diff for that file. If the target line isn't in a diff hunk, the API returns 422. Fall back tosubject_type: "file".commit_idmust match the PR's current HEAD when creating or re-posting a review comment. After a force-push, existing inline comments become "Outdated" and remain anchored to the old commit SHA. You can still edit their body text viaPATCH /repos/{owner}/{repo}/pulls/comments/{comment_id}, but you cannot change their diff anchor; to re-anchor to the new HEAD you must delete and recreate the comment.- Do NOT mix schemas:
subject_typecannot be combined withline/start_line/side. The API usesoneOf— pick one pattern. - Do NOT use
position(deprecated) or includesubject_type: "line"explicitly (not a valid creation param, only returned in responses).
Re-posting comments after rebase/force-push
After a force-push, all existing inline comments become "outdated" (anchored to the old commit SHA). To re-anchor comments to the new commit:
- Fetch existing comments:
gh api --paginate repos/{owner}/{repo}/pulls/{pull_number}/comments - Filter to your comments (by
user.loginor body pattern) - Save
{path, body, original_line, original_start_line, subject_type}from each - Delete old comments:
gh api -X DELETE repos/{owner}/{repo}/pulls/comments/{id} - Re-post against new HEAD using the patterns above
- Use
grep -non actual files to find correct line numbers for the new commit - If a target line isn't in the diff, fall back to file-level
Error Handling
If GitHub API fails:
- Retry once after 5 seconds
- If still failing, report the error and ask user to check authentication
If worktree creation fails:
- Check if branch exists:
git branch -r | grep - Try fetching:
git fetch origin - Report specific error to user
If reply posting fails:
- The comment may be part of a review thread (not standalone)
- Fallback: Post a new PR comment referencing the original:
``bash gh pr comment --body "Re: Copilot comment on :\n\n" ``
Response Style
- Keep updates concise and actionable
- Use checkmarks to show progress: "Fixed comment on file.ts:42"
- Show clear before/after for code changes
- Link to created issues when deferring
- Present user decision points
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: loomantix
- Source: loomantix/claude-platform
- License: Apache-2.0
- Homepage: https://github.com/loomantix/claude-platform
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.