What I do
- Accept a GitHub pull request URL or PR number
- Fetch PR review threads with GitHub GraphQL via
gh api graphql - Detect Copilot-authored review threads and separate unresolved ones
- Default to a cautious review-first flow: inspect first, resolve only explicitly selected threads
- Post a reply for each selected thread, then resolve it on GitHub
- Support two standard actions per thread:
addressedandignored - Return a compact action summary including thread ids, paths, and reply URLs when available
When to use me
Use this skill when:
- User has already handled some Copilot review comments and wants those threads resolved
- User wants to intentionally close selected Copilot suggestions as ignored with an explicit reply
- User wants a repeatable
gh+ GraphQL workflow for Copilot review-thread cleanup - User wants to act on Copilot review threads without touching human reviewer threads by default
Prerequisites
- GitHub CLI (
gh) must be installed and authenticated - If given only a PR number, the current repository must have a GitHub
originremote - The target PR must be accessible with the current GitHub credentials
- If
ghnot installed -> error:Error: GitHub CLI not found. Install: https://cli.github.com/ - If not authenticated -> error:
Error: Not authenticated with GitHub. Run: gh auth login
Input Handling
Accept either of these forms:
- Full GitHub PR URL
- Example:
https://github.com/owner/repo/pull/123
- Example:
- PR number
- Example:
123
- Example:
The user must also identify which Copilot threads to act on and how to handle each one.
Supported Per-Thread Actions
addressed- Use when the suggestion was implemented or otherwise handled in code or docs
- Default reply template:
Addressed in <commit> (<subject>).
ignored- Use when the suggestion is intentionally not being applied right now
- Default reply template:
Ignoring this suggestion for now.
Custom reply text is allowed per thread, but these two action labels should be the default language.
Resolution Rules
- If input is a full PR URL:
- Use it directly with
gh pr view <pr>and relatedghcommands.
- Use it directly with
- If input is a PR number:
- Resolve repository from
git remote get-url origin - Derive
owner/repo - Build the API path from that repository and PR number
- Resolve repository from
- If the input is neither a valid URL nor a PR number:
- Error:
Error: Provide a GitHub PR URL or PR number
- Error:
Cautious Default Behavior
- Default to inspection first, action second
- Only resolve explicitly selected Copilot threads by default
- Do not bulk-resolve all unresolved Copilot threads unless the user explicitly asks for bulk mode
- Do not resolve human-authored threads unless the user explicitly asks
- Skip already resolved threads
- If a thread is marked
ignored, still add a reply before resolving so the closure is explicit
Workflow
Validate GitHub access
gh auth statusResolve PR and fetch metadata
- For PR URL, use:
gh pr view <pr> --json number,title,url,author,baseRefName,headRefName - For PR number in current repo, verify remote:
git remote get-url origin gh pr view <number> --json number,title,url,author,baseRefName,headRefName
- For PR URL, use:
Fetch review threads with GraphQL
query=$(cat <<'EOF' query($owner: String!, $repo: String!, $number: Int!, $threadCursor: String) { repository(owner: $owner, name: $repo) { pullRequest(number: $number) { reviewThreads(first: 100, after: $threadCursor) { pageInfo { hasNextPage endCursor } nodes { id isResolved isOutdated path line startLine comments(first: 20) { pageInfo { hasNextPage endCursor } nodes { id body url createdAt author { login } } } } } } } } EOF ) gh api graphql -f query="$query" -F owner='<owner>' -F repo='<repo>' -F number=<number>- Include
pageInfo { hasNextPage endCursor }for bothreviewThreadsandcomments - Continue fetching
reviewThreadswith anaftercursor untilhasNextPageisfalse - If a thread's
commentsconnection has more pages, fetch the remaining comment pages with a dedicated per-thread query before deciding whether the thread is Copilot-authored
- Include
Identify Copilot-authored threads
- Match exact known Copilot identities first, including
Copilot,copilot-pull-request-reviewer, andcopilot-pull-request-reviewer[bot] - Then match comment authors whose login contains
copilot - Ignore human-only threads by default
- Match exact known Copilot identities first, including
Preview unresolved Copilot threads before acting
- Summarize each unresolved Copilot thread with:
thread idpathlineorstartLine/lineisOutdated- latest Copilot comment excerpt
- If line metadata is missing, still show the thread using
pathandisOutdated; do not fail preview just because the thread no longer has line coordinates - If the user has not clearly identified thread actions yet, stop here and ask only for the missing thread/action mapping
- Summarize each unresolved Copilot thread with:
Determine the reply wording source for
addressedthreads- If the user already provided the commit reference and subject, use it
- Otherwise, ask the user for the commit reference before using the full template
Addressed in <commit> (<subject>). - If the user does not want to supply a subject, fall back to
Addressed in <commit>.
Prepare replies for selected threads
- For
addressed, prefer a commit-aware reply such as:Addressed in 66022de (fix: harden dataset scripts).
- For
ignored, prefer:Ignoring this suggestion for now.
- If the user provided custom wording, use it exactly
- For
Reply to each selected review thread
query=$(cat <<'EOF' mutation($threadId: ID!, $body: String!) { addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) { comment { url } } } EOF ) gh api graphql -f query="$query" -F threadId='<thread-id>' -F body='<reply-body>'Resolve each selected thread after replying
query=$(cat <<'EOF' mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { id isResolved } } } EOF ) gh api graphql -f query="$query" -F threadId='<thread-id>'Report results
- Return which threads were resolved as
addressed - Return which threads were resolved as
ignored - Include reply URLs when available
- Note any partial failures clearly
Bulk Mode
Bulk mode is allowed only when the user explicitly asks for it.
- Safe bulk example: resolve these Copilot threads with one shared
addressedreply and these other Copilot threads with one sharedignoredreply - Unsafe default to avoid: resolving every unresolved Copilot thread automatically with one generic message
Even in bulk mode, separate the actions into at least these groups:
- threads marked
addressed - threads marked
ignored
Copilot Thread Detection
Treat a thread as Copilot feedback when at least one review-thread comment is authored by:
Copilotcopilot-pull-request-reviewercopilot-pull-request-reviewer[bot]- a login containing
copilot
If multiple comments exist in the same thread, treat the thread as Copilot-authored if any comment clearly matches Copilot.
Reply Message Guidelines
- Keep replies short and explicit
- Prefer commit-specific replies for
addressedthreads when a commit is known - Do not claim a suggestion was fixed if the thread is actually being ignored
- For
ignored, make the intentional choice clear without overexplaining
Examples:
Addressed in 66022de (fix: harden dataset scripts).Addressed in 66022de.Ignoring this suggestion for now.Keeping the current behavior for now; not applying this suggestion in this PR.
Suggested Action Summary Format
Return a concise result like:
Resolved 3 Copilot threads on PR #1.
- addressed: PRRT_xxx `scripts/download_artifact_files.py` -> <reply-url>
- addressed: PRRT_yyy `scripts/resolve_maven_dependencies.py` -> <reply-url>
- ignored: PRRT_zzz `AGENTS.md` -> <reply-url>
GraphQL Operations Reference
List review threads
query=$(cat <<'EOF'
query($owner: String!, $repo: String!, $number: Int!, $threadCursor: String) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
reviewThreads(first: 100, after: $threadCursor) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
isResolved
isOutdated
path
line
startLine
comments(first: 20) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
body
url
createdAt
author {
login
}
}
}
}
}
}
}
}
EOF
)
gh api graphql -f query="$query" -F owner='<owner>' -F repo='<repo>' -F number=<number>
Repeat the reviewThreads query until thread pagination is exhausted, and fetch additional comment pages for any thread whose comments.pageInfo.hasNextPage is true before classifying it.
Fetch additional comments for one thread
query=$(cat <<'EOF'
query($threadId: ID!, $commentCursor: String) {
node(id: $threadId) {
... on PullRequestReviewThread {
id
comments(first: 20, after: $commentCursor) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
body
url
createdAt
author {
login
}
}
}
}
}
}
EOF
)
gh api graphql -f query="$query" -F threadId='<thread-id>'
Use this per-thread query when the nested comments connection reports more pages than the initial reviewThreads query returned.
Reply to a review thread
query=$(cat <<'EOF'
mutation($threadId: ID!, $body: String!) {
addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: $threadId, body: $body}) {
comment {
url
}
}
}
EOF
)
gh api graphql -f query="$query" -F threadId='<thread-id>' -F body='<reply-body>'
Resolve a review thread
query=$(cat <<'EOF'
mutation($threadId: ID!) {
resolveReviewThread(input: {threadId: $threadId}) {
thread {
id
isResolved
}
}
}
EOF
)
gh api graphql -f query="$query" -F threadId='<thread-id>'
Error Handling
- Invalid input ->
Error: Provide a GitHub PR URL or PR number ghmissing ->Error: GitHub CLI not found. Install: https://cli.github.com/- Not authenticated ->
Error: Not authenticated with GitHub. Run: gh auth login - No GitHub remote for PR number mode ->
Error: No GitHub remote found. Provide a full PR URL or set origin - PR not found or inaccessible ->
Error: Unable to access the pull request with current GitHub credentials - No Copilot threads found ->
No Copilot-authored review threads found on this pull request - No unresolved Copilot threads found ->
No unresolved Copilot review threads found on this pull request - No explicit thread/action mapping in cautious default mode -> ask only for the missing thread ids and whether each is
addressedorignored - Reply succeeded but resolve failed -> report partial success and include the affected thread id
Notes
- Use
ghCLI only; no separate GitHub SDK is required - This skill changes GitHub review-thread state but does not edit repository files
- This skill is intentionally the action companion to
gh-copilot-review-read - Prefer explicit per-thread intent over aggressive automation