| name | copilot-review |
|---|---|
| description | Review GitHub Copilot PR comments, evaluate and apply valid ones, resolve all threads |
| argument-hint | [PR-number-or-url] |
| disable-model-invocation | true |
| allowed-tools | Bash(gh *), Read, Edit, Write, Grep, Glob, Agent |
Fetch all GitHub Copilot review comments on a PR, evaluate each one, apply the valid suggestions, and resolve all Copilot threads.
If $ARGUMENTS is a full URL, extract the owner/repo and PR number.
If $ARGUMENTS is just a number, use the current repo.
If $ARGUMENTS is empty, detect the current branch's open PR:
gh pr view --json number,url,headRefName,baseRefName,titlePrint the PR title, number, and branch. Confirm you are on the correct branch (check out if needed).
Extract owner and repo:
OWNER=$(gh repo view --json owner -q '.owner.login')
REPO=$(gh repo view --json name -q '.name')Use the GraphQL API to get all review threads with their thread IDs (needed for resolution):
gh api graphql -F owner="$OWNER" -F repo="$REPO" -F prNumber=$PR_NUMBER -f query='
query($owner: String!, $repo: String!, $prNumber: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
reviewThreads(first: 100) {
edges {
node {
id
isResolved
isOutdated
path
line
startLine
originalLine
originalStartLine
diffSide
comments(first: 50) {
edges {
node {
id
body
author { login }
createdAt
}
}
}
}
}
}
}
}
}'Filter for threads where the first comment author login contains "copilot" (case-insensitive). Known logins: Copilot, copilot-pull-request-reviewer. Discard threads that are already resolved (isResolved: true).
Collect all Copilot thread IDs — you will need them ALL in Step 7.
Also fetch REST comment IDs (needed for replies in Step 7 — the GraphQL node IDs do NOT work with the REST reply endpoint):
gh api repos/$OWNER/$REPO/pulls/$PR_NUMBER/comments \
--jq '.[] | select(.user.login | test("copilot"; "i")) | {rest_id: .id, node_id: .node_id, path, line}'Build a mapping of GraphQL node ID → REST numeric ID. You will need the REST IDs to post replies.
For each unresolved Copilot thread:
-
Read the referenced file around the indicated lines to understand context
-
Read the Copilot comment body carefully
-
Assess the suggestion:
- Valid — real bug fix, meaningful improvement, correct style/security/performance concern
- Reject — false positive, doesn't apply in context, overly pedantic, would break things, or contradicts project conventions
Be pragmatic. Apply the same judgment a senior engineer would: fix real issues, skip noise.
Show the user a numbered table:
# | File:Line | Summary of suggestion | Verdict (Valid/Reject) | Reasoning
1 | src/foo.py:42 | Add null check for edge case | Valid | Param can be None from caller
2 | src/bar.py:10 | Use const instead of let | Reject | Variable is reassigned on L15
3 | src/baz.py:99 | Add error handling for API call | Valid | Unhandled exception risk
Ask the user (via AskUserQuestion) which comments to apply:
- All valid (recommended) — apply all items marked Valid
- Let me pick — user selects specific numbers
- Skip all — don't apply any changes, just resolve threads
For each accepted comment:
- Read the file around the referenced lines for full context
- Make the minimal, focused fix that addresses the suggestion
- Do NOT over-engineer or make changes beyond what the comment asks for
If any fixes were applied:
- Run fast validation if applicable (tests for modified files, linter/formatter)
- Stage only modified files
- Commit:
Address Copilot review suggestions
- <one-line summary per fix applied>
- Push to the PR's head branch
For each Copilot thread that was applied, post a reply using the numeric REST comment ID (from the mapping built in Step 2), then resolve.
IMPORTANT: The REST reply endpoint requires numeric IDs (e.g. 2982458496), NOT GraphQL node IDs (e.g. PRRC_kwDOROHpZc6xxLQe). Using node IDs will return 404.
# Use the numeric REST comment ID from the Step 2 mapping
gh api repos/$OWNER/$REPO/pulls/$PR_NUMBER/comments/{REST_COMMENT_ID}/replies \
-f body="Applied — fixed in <short-sha>"Then resolve every Copilot thread (both applied and rejected) using the GraphQL mutation.
IMPORTANT: Do NOT use -F threadId="$tid" — GraphQL will try to parse $tid as a GraphQL variable and fail. Instead, inline the thread ID directly in the query string:
gh api graphql -f query="mutation { resolveReviewThread(input: {threadId: \"THREAD_NODE_ID\"}) { thread { isResolved } } }"Loop through ALL collected Copilot thread IDs from Step 2 and resolve each one. This clears all Copilot review noise from the PR.
Copilot posts a top-level review summary comment on the PR (not an inline thread). Minimize it to reduce noise.
Fetch PR comments to find the Copilot summary comment (it's an issue comment, not a review comment):
gh api repos/$OWNER/$REPO/issues/$PR_NUMBER/comments \
--jq '.[] | select(.user.login | test("copilot"; "i")) | {id: .node_id, login: .user.login}'For each Copilot comment found, minimize it using GraphQL:
gh api graphql -f query="mutation { minimizeComment(input: {subjectId: \"COMMENT_NODE_ID\", classifier: OUTDATED}) { minimizedComment { isMinimized } } }"Also minimize review-level comments (the review body itself, not inline threads):
gh api repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews \
--jq '.[] | select(.user.login | test("copilot"; "i")) | {id: .node_id, state: .state}'For each Copilot review found, minimize it using the same GraphQL mutation:
gh api graphql -f query="mutation { minimizeComment(input: {subjectId: \"REVIEW_NODE_ID\", classifier: OUTDATED}) { minimizedComment { isMinimized } } }"Print a final summary: how many threads resolved, how many fixes applied, how many comments minimized.