用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/loomantix/codex-platform --skill copilot-review命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | copilot-review |
| description | Address GitHub Copilot code review comments on a PR systematically |
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, fix every valid finding, reply to confirm resolution, and iterate until complete.
Goal: Validate the PR and set up the working environment
Arguments: $ARGUMENTS (PR number)
Actions:
Validate PR number from arguments
Fetch PR details:
gh pr view <pr-number> --json number,title,headRefName,baseRefName,state
Verify PR is open and ready for review
Create todo list with all phases
Goal: Load and categorize all Copilot code review comments
IMPORTANT — jq quoting pitfalls:
--jq expressions passed directly to gh api are 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 parses gh api --jq arguments, not from jq itself--jq filters (or none), save raw JSON to a temp file first, then run richer jq queries against that fileActions:
Fetch all PR comments to a temp file (single API call, reused for all queries):
gh api --paginate repos/{owner}/{repo}/pulls/<pr-number>/comments > /tmp/pr-<pr-number>-comments.json
Extract Copilot top-level comments (those without in_reply_to_id):
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-<pr-number>-comments.json
Extract reply target IDs (which comments already have human replies):
jq '[.[] | select(.in_reply_to_id > 0) | .in_reply_to_id] | unique' /tmp/pr-<pr-number>-comments.json
Compute unaddressed comments (Copilot comments whose ID is not in the replied set):
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-<pr-number>-comments.json
Read full body of each unaddressed comment when needed:
jq '.[] | select(.id == <comment-id>) | .body' /tmp/pr-<pr-number>-comments.json
Categorize each unaddressed comment after reading the file:
Present summary to user:
Ask user for confirmation before proceeding
Goal: Create an isolated environment for making changes
Actions:
Get PR branch name from Phase 0 data (headRefName)
Check if worktree already exists:
git worktree list | grep "copilot-review-<pr-number>"
Create or reset worktree:
If worktree doesn't exist:
git fetch origin <branch-name>
git worktree add worktrees/copilot-review-<pr-number> origin/<branch-name>
If worktree exists:
git -C worktrees/copilot-review-<pr-number> fetch origin
git -C worktrees/copilot-review-<pr-number> checkout <branch-name>
git -C worktrees/copilot-review-<pr-number> reset --hard origin/<branch-name>
Verify worktree is ready:
git -C worktrees/copilot-review-<pr-number> status
git -C worktrees/copilot-review-<pr-number> log --oneline -3
Set worktree path for subsequent operations:
worktrees/copilot-review-<pr-number>/git -C worktrees/copilot-review-<pr-number>Goal: Address each unaddressed Copilot comment systematically
For each unaddressed comment, perform the following cycle:
Read the file at the path specified in the comment
Understand the context:
Classify the resolution approach:
If Fix:
package.json, Justfile, Makefile, or repo docs); in pnpm repos this is often pnpm lint:fix or pnpm format.If Defer:
Only use this path for valid but extremely large follow-up refactors, roughly 300+ lines or cross-cutting rewrites. Do not defer ordinary valid findings.
gh issue create --title "<issue-title>" \
--label "tech-debt,from-copilot-review" \
--body "## Context\n\nThis issue was identified during Copilot code review of PR #<pr-number>.\n\n## Original Comment\n\n<copilot-comment-body>\n\n## Recommendation\n\n<suggested-approach>\n\n## Related\n\n- PR #<pr-number>"
If Dismiss:
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:
{
"comment_id": <numeric id from GitHub>,
"path": "<file path>",
"line": <line number or null>,
"resolution": "fix" | "defer" | "dismiss",
"explanation": "<one-line: what changed / why deferred / why dismissed>",
"defer_issue_url": "<URL or null — only set on defer>"
}
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.
After the loop completes, write the accumulated array once:
Write(file_path="/tmp/copilot-review-<pr-number>-resolutions.json",
content=<json-array-of-all-resolution-rows>)
Phase 4 step 6 reads this file and posts one reply per row.
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):
git -C worktrees/copilot-review-<pr-number> status
git -C worktrees/copilot-review-<pr-number> diff
Run quality checks (in worktree):
cd worktrees/copilot-review-<pr-number>
# Pick commands that actually exist in this repo. Examples:
pnpm lint:fix
pnpm typecheck
Commit changes (only if there are code changes from "fix" resolutions):
cd worktrees/copilot-review-<pr-number> && git add -A && git commit -m "fix(review): address copilot feedback"
Push to remote and capture the real SHA:
git -C worktrees/copilot-review-<pr-number> push origin <branch-name>
PUSHED_SHA=$(git -C worktrees/copilot-review-<pr-number> rev-parse HEAD)
PUSHED_SHA_SHORT=$(git -C worktrees/copilot-review-<pr-number> rev-parse --short=8 HEAD)
If push is rejected (remote has new commits):
git -C worktrees/copilot-review-<pr-number> pull --rebase origin <branch-name>
git -C worktrees/copilot-review-<pr-number> push origin <branch-name>
PUSHED_SHA=$(git -C worktrees/copilot-review-<pr-number> rev-parse HEAD)
PUSHED_SHA_SHORT=$(git -C worktrees/copilot-review-<pr-number> rev-parse --short=8 HEAD)
Verify the push landed on the PR head before posting replies — GitHub's PR API is eventually consistent, so headRefOid can lag the actual ref by a few seconds:
verify_pr_head() {
local attempt
for attempt in 1 2 3 4; do
local pr_head
pr_head=$(gh pr view <pr-number> --json headRefOid --jq '.headRefOid')
if [[ "$pr_head" == "$PUSHED_SHA" ]]; then
return 0
$(( attempt * ))
>&2
1
}
verify_pr_head || 1
Goal: Get fresh Copilot feedback on the updated code
Actions:
Check if Copilot auto-reviews on push
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:
PR_NODE=$(gh pr view <pr-number> --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/<n>/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):
gh api repos/{owner}/{repo}/pulls/<pr-number>/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
Goal: Determine if another resolution cycle is needed
Actions:
Count new unaddressed comments from the latest Copilot review
If new comments exist:
If no new comments:
Goal: Summarize all work done and provide next steps
Actions:
Generate summary report:
Cleanup worktree (optional, ask user):
git worktree remove worktrees/copilot-review-<pr-number>
Provide next steps:
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:
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 -
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 -
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 -
line must be within the PR diff for that file. If the target line isn't in a diff hunk, the API returns 422. Fall back to subject_type: "file".commit_id must 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 via PATCH /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.subject_type cannot be combined with line/start_line/side. The API uses oneOf — pick one pattern.position (deprecated) or include subject_type: "line" explicitly (not a valid creation param, only returned in responses).After a force-push, all existing inline comments become "outdated" (anchored to the old commit SHA). To re-anchor comments to the new commit:
gh api --paginate repos/{owner}/{repo}/pulls/{pull_number}/commentsuser.login or body pattern){path, body, original_line, original_start_line, subject_type} from eachgh api -X DELETE repos/{owner}/{repo}/pulls/comments/{id}grep -n on actual files to find correct line numbers for the new commitIf GitHub API fails:
If worktree creation fails:
git branch -r | grep <branch-name>git fetch origin <branch-name>If reply posting fails:
gh pr comment <pr-number> --body "Re: Copilot comment on <file>:<line>\n\n<reply-message>"
Post replies now that the real SHA is in hand. Read /tmp/copilot-review-<pr-number>-resolutions.json and post one reply per row, building each body with ${PUSHED_SHA_SHORT} substituted inline.
Reply body templates:
For Fix:
Fixed in `${PUSHED_SHA_SHORT}`.
<explanation from resolutions.json>
For Defer:
Deferred — tracking in <defer_issue_url>.
<explanation: why this is being deferred rather than fixed in this PR>
For Dismiss:
Dismissing — false positive.
<explanation: why the reviewer's reasoning doesn't apply>
Post each reply via:
gh api -X POST repos/{owner}/{repo}/pulls/<pr-number>/comments/<comment_id>/replies \
-f body="<assembled 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.