AgentSkillsCN

pr-review-workflow

使用 GitHub CLI 和 API 处理 PR 创建、评论审核、反馈收集以及 CI 状态监控。

SKILL.md
--- frontmatter
name: pr-review-workflow
description: Handle PR creation, review comments, feedback, and CI status using GitHub CLI and APIs

Skill: PR Review Workflow

This skill defines how to handle pull request workflows: creation, review comments, and CI status.

Activation context

This skill activates when:

  • Creating a PR (see section 8 for PowerShell quoting)
  • The agent has just pushed commits to a PR branch.
  • The developer asks about PR comments, review feedback, or CI status.
  • The developer mentions "check comments", "review feedback", "CI status", or "workflow status".

1. Post-push workflow

After every git push to a PR branch, do ALL of the following in order:

Step 1: Copilot review (automated)

Copilot review is triggered automatically by the copilot-review.yml GitHub Actions workflow on PR open and ready_for_review. There is no reliable CLI command to request it manually.

See Section 8 for details.

Step 2: Fetch and present review comments

Always run BOTH of these commands:

  1. Top-level PR comments: gh pr view --json comments,reviews
  2. Inline code review comments: gh api repos/<OWNER>/<REPO>/pulls/<PR>/comments --paginate

The inline comments API call is where Copilot and human reviewers leave code-level feedback. Never skip it.

After fetching comments, summarize them (see section 3) and ask which to address.

For CI status, offer to check:

CI passed. Would you like me to check the workflow details?

Completion checklist for each comment (all boxes required):

  • Code fix implemented
  • Committed with descriptive message
  • Pushed to remote
  • Replied to comment with commit SHA
  • Thread resolved via GraphQL API immediately after replying

⚠️ CRITICAL: Reply + Resolve are INSEPARABLE. After replying to a comment, you MUST resolve the thread in the same operation. Never batch replies without also batching resolutions. The resolve loop must run immediately after the reply loop - not "later" or "at the end".

Do not consider a comment "handled" until ALL steps are complete. After fixing and pushing, return to sections 4 and 6 to reply and resolve.


2. Fetching PR comments

⚠️ CRITICAL: NEVER use built-in IDE tools for PR comments. Tools like github-pull-request_activePullRequest or similar VS Code/Copilot integrations may return stale/cached data and miss recent review comments. This has caused missed feedback in production. ALWAYS use gh api or gh api graphql directly.

CRITICAL: You MUST check BOTH top-level comments AND inline review comments. The gh pr view --json comments,reviews command does NOT return inline code comments. Skipping inline comments means missing Copilot's code suggestions and human review feedback.

2.0 PREFERRED: GraphQL reviewThreads (comments + resolution status)

This is the most reliable method. It returns all review threads with their resolution status in one query:

bash
gh api graphql -f query='
  query($owner: String!, $repo: String!, $pr: Int!) {
    repository(owner: $owner, name: $repo) {
      pullRequest(number: $pr) {
        reviewThreads(first: 100) {
          nodes {
            id
            isResolved
            comments(first: 5) {
              nodes {
                body
                path
                author { login }
              }
            }
          }
        }
      }
    }
  }
' -f owner="<OWNER>" -f repo="<REPO>" -F pr=<PR>

Why this is preferred:

  • Returns both comments AND resolution status in one call
  • Never stale - queries GitHub directly
  • Includes the threadId needed for resolution (the id field)
  • Shows which threads are already resolved vs still open

Filter for unresolved threads only:

bash
gh api graphql -f query='...' | jq '.data.repository.pullRequest.reviewThreads.nodes | map(select(.isResolved == false))'

2.1 Identify PR context

Determine the PR number and repository from:

  1. The current branch name and gh pr view output.
  2. Repository information from git remote.

Propose running the following commands (or use a shell tool if available and the developer agrees):

bash
# Get current PR number
gh pr view --json number -q '.number'

# Get repo owner/name
gh repo view --json owner,name -q '"\(.owner.login)/\(.name)"'

2.2 Fetch conversation comments

Propose running gh pr view for top-level conversation comments:

bash
gh pr view <PR> --json comments -q '.comments[] | {author: .author.login, body: .body, createdAt: .createdAt}'

2.3 Fetch inline review comments (REST API alternative)

Note: The GraphQL method in section 2.0 is preferred because it includes resolution status. Use this REST API method only when you need the REST-style comment IDs for replies.

gh pr view --json comments and gh pr view --json reviews do NOT return inline code review comments. They only return PR-level conversation comments and review summaries. The REST API returns inline comments:

bash
gh api repos/<OWNER>/<REPO>/pulls/<PR>/comments

This returns an array of review comments with:

  • path: File path the comment is on.
  • line / original_line: Line number.
  • body: Comment text.
  • user.login: Author.
  • id: Comment ID (needed for replies/resolution).
  • in_reply_to_id: If this is a reply to another comment.

Limitation: This does NOT show whether threads are resolved. Use GraphQL (section 2.0) to check resolution status.

2.4 Fetch review summaries

Propose fetching overall review status:

bash
gh pr view <PR> --json reviews -q '.reviews[] | {author: .author.login, state: .state, body: .body}'

3. Assessing and summarizing comments

After fetching comments, provide a structured summary:

3.1 Categorize comments

Group comments into:

  1. Actionable: Suggestions for code changes, bug fixes, or improvements.
  2. Questions: Clarifications needed from the author.
  3. Informational: FYI comments, praise, or general observations.
  4. Blocking: Changes requested that must be addressed before merge.

3.2 Summary format

Present a summary like:

PR #123 Review Summary

Actionable (3):

Questions (1):

  • README.md#L20: @reviewer asks: Why was this section removed?

Informational (1):

  • General: @reviewer: "Nice refactoring of the database module!"

3.3 Critically assess automated suggestions

When comments are from copilot-pull-request-reviewer, github-actions, or other bots:

  1. Do not blindly apply suggestions. Bots lack full codebase and domain context.
  2. Verify correctness: Check if the suggestion applies to this project's conventions and APIs.
  3. Watch for false positives:
    • API payload format suggestions (e.g., Jira field names, REST body structure) - the bot may not know the actual API.
    • Security warnings that don't apply to the deployment context.
    • Style suggestions that conflict with project conventions or tooling.
  4. When uncertain: Present the suggestion to the developer with your assessment. Do not auto-apply.
  5. When the suggestion is wrong: Point out why it's incorrect rather than silently skipping it.

Example assessment:

Bot suggestion: "Use {"name": "username"} instead of {"assignee": "email"}"

3.4 Present before acting

Before making any changes based on review comments:

  1. Present all comments to the developer with your assessment of each (agree, disagree, need clarification).
  2. Wait for developer confirmation on which comments to address.
  3. Do not proceed to commit/push until the developer explicitly confirms the plan.

This prevents silently skipping valid feedback or applying incorrect suggestions.


4. Responding to comments

4.1 When agreeing with a comment

  1. Make the fix: Edit the file to address the feedback.
  2. Stage and commit: Use a descriptive commit message referencing the feedback.
  3. Push the changes.
  4. Reply to the comment - propose running:
bash
# Use -F (not -f) for the numeric in_reply_to parameter
gh api repos/<OWNER>/<REPO>/pulls/<PR>/comments \
  -X POST -f body="Fixed in <COMMIT_SHA>. Thanks for catching this!" \
  -F in_reply_to=<COMMENT_ID>

Important: Use -F for in_reply_to because it's a numeric parameter. Using -f causes a type error.

  1. Resolve the conversation (if the comment is on a review thread) - propose running:

WARNING: Replying does NOT resolve the thread. You MUST call the GraphQL mutation separately to resolve it.

bash
gh api graphql -f query='
  mutation {
    resolveReviewThread(input: {threadId: "<thread_id>"}) {
      thread { isResolved }
    }
  }
'

Note: The threadId is a GraphQL node ID, not the REST API comment_id. To obtain it, first fetch review threads:

bash
gh api graphql -f query='
  query($owner: String!, $repo: String!, $pr: Int!) {
    repository(owner: $owner, name: $repo) {
      pullRequest(number: $pr) {
        reviewThreads(first: 100) {
          nodes { id isResolved comments(first: 1) { nodes { body } } }
        }
      }
    }
  }
' -f owner="<OWNER>" -f repo="<REPO>" -F pr=<PR>

Match the thread by its first comment body, then use the id field as threadId.

4.2 When disagreeing with a comment

  1. Analyze the suggestion against the codebase context.
  2. Reply with reasoning - propose running:
bash
gh api repos/<OWNER>/<REPO>/pulls/<PR>/comments \
  -X POST -f body="I considered this, but <REASONING>. The current approach <JUSTIFICATION>." \
  -F in_reply_to=<COMMENT_ID>
  1. Resolve the conversation after explaining the disagreement (see thread resolution in 4.1).

4.3 Batch processing

When multiple comments need addressing:

  1. Group related fixes into logical commits.
  2. Push all changes at once.
  3. Reply to all comments in a loop - collect comment IDs and iterate:
    bash
    # Use -F for numeric in_reply_to parameter
    for id in ID1 ID2 ID3; do
      gh api repos/<OWNER>/<REPO>/pulls/<PR>/comments \
        -X POST -f body="Fixed in <COMMIT_SHA>." -F in_reply_to=$id
    done
    
  4. Resolve all threads in a loop - fetch thread IDs, then iterate:
    bash
    for thread_id in THREAD1 THREAD2 THREAD3; do
      gh api graphql -f query="mutation { resolveReviewThread(input: {threadId: \"$thread_id\"}) { thread { isResolved } } }"
    done
    

REMINDER: Replying and resolving are separate operations. Never skip the resolve loop.


5. Checking CI/workflow status

5.1 Fetch workflow runs

Propose running the following commands (or use a shell tool if available and the developer agrees):

bash
# List recent workflow runs for the current branch
gh run list --branch <branch_name> --limit 5

# Get detailed status of a specific run
gh run view <run_id>

# Get failed jobs and their logs
gh run view <run_id> --log-failed

5.2 Assess workflow failures

When a workflow fails:

  1. Identify the failing job from the run output.
  2. Fetch the logs for the failed step.
  3. Analyze the error:
    • Is it a test failure? → Identify which test and why.
    • Is it a lint/format issue? → Run the linter locally and fix.
    • Is it an infrastructure issue? → Check if it's a flaky test or transient error.
  4. Suggest or implement a fix.

5.3 Status summary format

Present CI status like:

CI Status for PR #123

WorkflowStatusDurationDetails
test.ymlPass3m 42s
lint.ymlFail1m 15sruff check failed
security.ymlPass2m 08s

Failure Analysis: lint.yml failed due to unused import in src/api/routes.py:15. Would you like me to fix this?


6. Iterative workflow

After addressing comments and fixing CI issues:

  1. Push the fixes.
  2. Reply to each addressed comment explaining how it was fixed (see section 4.1).
  3. Resolve the review threads for comments that were addressed.
  4. Offer to re-check: "Changes pushed. Want me to check for new comments or CI status?"
  5. Repeat until the PR is ready for merge.

Critical: Do not consider a comment "handled" just because you pushed a commit that addresses it. The review thread remains open until explicitly replied to and resolved.

The workflow is incomplete if you stop after pushing. Reviewers see unresolved threads as outstanding issues. Always complete the reply→resolve loop before moving on or reporting completion to the developer.


7. Command reference

TaskCommand
Get PR numbergh pr view --json number -q '.number'
Get PR comments (top-level only)gh pr view <PR> --json comments
Get review threads + resolution (PREFERRED)gh api graphql -f query='{ repository(...) { pullRequest(...) { reviewThreads(first:100) { nodes { id isResolved comments(first:5) { nodes { body path } } } } } } }'
Get inline review comments (REST, no resolution)gh api repos/<OWNER>/<REPO>/pulls/<PR>/comments --paginate
Get review statusgh pr view <PR> --json reviews
Reply to inline commentgh api repos/<OWNER>/<REPO>/pulls/<PR>/comments -X POST -f body="..." -F in_reply_to=<ID>
Resolve threadgh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "<ID>"}) { thread { isResolved } } }'
List workflow runsgh run list --branch <branch> --limit 5
View failed workflow logsgh run view <run_id> --log-failed
Re-run failed workflowgh run rerun <run_id> --failed

8. Creating PRs

Copilot review automation

Copilot review is requested automatically via GitHub Actions (.github/workflows/copilot-review.yml). There is no reliable CLI command to request Copilot review:

  • gh pr create --reviewer copilot → fails with "'copilot' not found", aborts PR creation
  • gh pr edit <PR> --add-reviewer copilot → unreliable, sometimes works but not guaranteed

The workflow uses the gh-copilot-review extension and triggers on opened, ready_for_review, and reopened events.

Requires a fine-grained PAT secret (GH_TOKEN_COPILOT_REVIEW) with Pull requests: Read and write permission.

Multi-line bodies (PowerShell)

NEVER pass backtick-containing text via --body in PowerShell — backticks are parsed as escape characters causing Unicode parse errors.

Required approach — write to temp file:

powershell
# 1. Write body to temp file (use create_file tool)
# 2. Create PR with --body-file
gh pr create --title "feat: ..." --body-file tmp_pr_body.md
# 3. Delete temp file
Remove-Item tmp_pr_body.md

9. Multi-repo workflow

When working across multiple repositories with the same or similar files (e.g., shared skills, copilot-instructions):

  1. Fetch comments from ALL repos first. Do not assume identical files receive identical feedback. Different reviewers or bots may flag different issues.
  2. Aggregate and deduplicate comments. Group similar feedback across repos to avoid redundant work.
  3. Apply fixes to the source-of-truth repo first. Make changes in one place, then sync to others.
  4. Sync files before committing. Ensure all repos have identical content for shared files.
  5. Commit and push to all repos. Use a loop to ensure consistency.
  6. Reply to and resolve comments in ALL repos. Each repo's PR has its own review threads that must be addressed individually.

10. Skåne Trails project context

For this project (Skåne Trails Streamlit app on GCP free tier):

  • Streamlit multi-page app: Many PRs change UI behavior and page interactions (app/_Home_.py, app/pages/). When reviewing, consider both code quality and user experience impacts.
  • Firestore + Cloud Run on GCP free tier: All data persistence uses Firestore and the app runs on Cloud Run. PR changes must respect free-tier limits (reads/writes, requests, CPU/memory) and avoid introducing costly polling or chatty database patterns.
  • Infrastructure via Terraform only: Any change that affects GCP resources must be implemented in infra/ with Terraform. Do not suggest or approve manual console or gcloud-only changes.
  • Security and CI workflows: CI runs Trivy scans, SBOM and license checks, and tests via GitHub Actions. When reviewing PRs, ensure new dependencies, workflows, or patterns align with these existing checks and keep the project zero-cost and public-repo safe.
  • Pre-commit hooks may auto-format files—always run git diff after committing to verify the actual changes.
  • mdformat can alter markdown structure—be aware that deeply indented markdown after code blocks may be reformatted.