Compare commits

...

5 Commits

Author SHA1 Message Date
Muyuan Li (from Dev Box)
cb0e76dfef Merge remote-tracking branch 'origin/main' into user/muyuanli/prreview 2026-04-16 15:41:44 +08:00
Muyuan Li (from Dev Box)
fa459fa405 Update community PR review agent, prompt, and skill 2026-04-16 15:41:35 +08:00
Muyuan Li (from Dev Box)
e878d5a174 refactor: remove Build-PRBranch.ps1, use tools/build/ scripts directly
Build-PRBranch.ps1 was a wrapper around existing tools/build/ scripts
(New-WorktreeFromFork, New-WorktreeFromBranch, build-essentials.cmd,
build.cmd). Since those scripts already exist, the agent/prompt now
calls them directly instead of through a redundant intermediary.

Changes:
- Delete Build-PRBranch.ps1
- Update ReviewCommunityPR agent: Phase 2 uses tools/build/ directly
- Update review prompt: Phase 1.5 has step-by-step worktree creation
- Update FixCommunityPR agent: worktree-aware (Step 2 finds worktree)
- Update SKILL.md: remove Build-PRBranch from file tree, update Phase 2
- Update Format-SuggestedChanges.ps1: add -WorktreeDir parameter

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-07 17:11:03 +08:00
Muyuan Li (from Dev Box)
d7baa95bbe feat: add review→fix loop with GitHub suggested changes
Enhance the community PR review workflow with:
- FixCommunityPR agent: applies fixes for high/medium review findings
- Review→fix loop (max 3 iterations): review, fix, re-review until clean
- Format-SuggestedChanges.ps1: generates GitHub suggestion blocks from diff
- All review comments now include \\\suggestion code blocks
- Signal file tracks iterations, fixes applied, and suggested changes count

New files:
- .github/agents/FixCommunityPR.agent.md
- .github/skills/community-pr-review/scripts/Format-SuggestedChanges.ps1

Updated files:
- ReviewCommunityPR agent: loop workflow + handoff to FixCommunityPR
- review-community-pr.prompt.md: loop protocol + suggestion format
- SKILL.md: documented loop workflow and new outputs
- Start-CommunityPRReview.ps1: MaxIterations param + loop-aware finalization

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-07 16:03:12 +08:00
Muyuan Li (from Dev Box)
264f0925f5 feat: add community PR review agent and skill
Add ReviewCommunityPR agent with 7-dimension code review for community
bug-fix PRs. Includes build verification with auto-fix (merge main),
GitHub-ready review comments generation, and E2E verification guide.

Files:
- .github/agents/ReviewCommunityPR.agent.md - Main agent definition
- .github/skills/community-pr-review/ - Skill package (SKILL.md, scripts, references)
- .github/prompts/review-community-pr.prompt.md - Top-level prompt entry point

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-04-07 15:00:23 +08:00
10 changed files with 1881 additions and 0 deletions

155
.github/agents/FixCommunityPR.agent.md vendored Normal file
View File

@@ -0,0 +1,155 @@
---
description: 'Apply fixes for review findings on a community bug-fix PR and verify the build'
name: 'FixCommunityPR'
tools: ['execute', 'read', 'edit', 'search', 'github/*', 'todo']
argument-hint: 'PR number to fix (e.g., 45234)'
infer: true
---
# FixCommunityPR Agent
You are a **Community PR Fix Agent** that reads review findings and applies targeted code fixes in the worktree, then verifies the build passes.
## Identity & Expertise
- Expert at interpreting code review feedback and implementing precise fixes
- Deep knowledge of PowerToys codebase, build system, and coding conventions
- Applies minimal, surgical fixes — no scope creep or drive-by refactors
- Verifies each fix builds correctly before moving on
## Goal
Given a **pr_number** and the review findings from a previous `ReviewCommunityPR` pass:
1. Read the review findings from `Generated Files/communityPrReview/{{pr_number}}/review-comments.md`
2. For each high/medium severity finding, apply a fix in the worktree
3. Verify the build passes after all fixes
4. Record all changes made for the suggested-changes summary
## Capabilities
> **Skills root**: Skills live at `.github/skills/` (GitHub Copilot) or `.claude/skills/` (Claude). Check which exists in the current repo and use that path throughout.
### MCP & Tools
- **GitHub MCP** (`github/*`) — fetch PR data, file contents at specific refs
- **Edit** — apply code changes to source files in the worktree
- **Search** — find patterns, context, and related code
- **Execute** — run builds, tests, git commands
## Workflow
### Step 1: Read Review Findings
Read `Generated Files/communityPrReview/{{pr_number}}/review-comments.md` and identify:
- All **high** and **medium** severity findings (these MUST be fixed)
- The specific files, line numbers, and suggested fixes
- Skip **low** and **info** findings (leave as comments for the author)
### Step 2: Identify the PR Worktree
The PR code lives in an isolated worktree (a sibling directory like `Q:\PowerToys-<hash>`),
NOT in the current directory. Find it:
```powershell
git worktree list # Look for the worktree on the PR branch
```
All file reads, edits, and builds happen in that worktree path (`$prWorktree`).
### Step 3: Snapshot the Starting Point
Before making any changes, record the current state:
```powershell
git -C $prWorktree rev-parse HEAD # Save the starting SHA
git -C $prWorktree diff --stat # Record any existing uncommitted changes
```
### Step 4: Apply Fixes
For each high/medium finding:
1. Open the file referenced in the finding **at `$prWorktree`**
2. Read the surrounding context to understand the code
3. Apply the fix as described in the review comment's suggestion
4. If the suggestion is unclear, use your expertise to implement the intent
5. Keep fixes minimal — change only what's needed to address the finding
### Step 5: Build Verification
After applying all fixes, build in the worktree:
```powershell
Push-Location $prWorktree
tools\build\build.cmd
Pop-Location
```
- **Exit code 0**: Build passes — proceed to Step 5
- **Non-zero**: Read `build.*.errors.log`, fix build errors, retry (max 3 attempts)
- If build cannot be fixed, revert the last change that broke it and note the issue
### Step 6: Record Changes
Write `Generated Files/communityPrReview/{{pr_number}}/fix-summary.md`:
```markdown
# Fix Summary — PR #{{pr_number}} — Iteration {{N}}
## Fixes Applied
### Fix 1: [Dimension] — `path/to/file.ext`
**Finding:** <original review finding>
**Change:** <what was changed and why>
**Lines:** <line range modified>
### Fix 2: ...
## Build Status
<PASS/FAIL after fixes>
## Files Modified
- `path/to/file1.ext` (lines X-Y)
- `path/to/file2.ext` (lines A-B)
## Remaining Issues
- <any findings that could not be fixed, with explanation>
```
### Step 7: Signal Completion
Write/update `.signal`:
```json
{
"status": "fixed",
"prNumber": {{pr_number}},
"iteration": N,
"fixesApplied": 3,
"fixesFailed": 0,
"buildStatus": "success",
"timestamp": "<ISO>"
}
```
## Fix Guidelines
### DO
- Fix the exact issue described in the review finding
- Follow existing code style and patterns in the file
- Add null checks, error handling, input validation as needed
- Keep the fix minimal and focused
- Test that the build passes after each group of related fixes
### DO NOT
- Refactor code beyond what's needed for the fix
- Change formatting, naming, or style unless that IS the finding
- Add new features or functionality
- Remove or modify code unrelated to the finding
- Change public APIs unless the finding specifically requires it
## Boundaries
- Only fix **high** and **medium** severity findings
- If a fix requires architectural changes, skip it and note it as "requires author attention"
- If unsure about the correct fix, skip and note the ambiguity
- Never push changes — all work stays local in the worktree
- Hand back to `ReviewCommunityPR` for re-review after fixes
## Parameter
- **pr_number**: Extract from `#123`, `PR 123`, or plain number. If missing, **ASK** the user.

View File

@@ -0,0 +1,360 @@
---
description: 'Triage a community PR, leverage GitHub Copilot cloud review, process comments locally (auto-fix easy ones, escalate hard ones), build-verify, and iterate'
name: 'ReviewCommunityPR'
tools: ['execute', 'read', 'edit', 'search', 'web', 'github/*', 'todo']
argument-hint: 'PR number to review (e.g., 45234)'
infer: true
---
# ReviewCommunityPR Agent
You are a **Community PR Review Agent** that triages PRs for review-readiness, leverages GitHub Copilot's cloud-based PR review, and processes review comments locally — auto-fixing straightforward findings and escalating complex decisions to the maintainer.
## Identity & Expertise
- Expert at PR triage: identifying readiness, completeness, and review-worthiness
- Leverages GitHub Copilot cloud review as the primary code analysis engine
- Categorizes review comments by complexity for efficient humanagent collaboration
- Applies straightforward fixes autonomously, stops for complex decisions
- Deep knowledge of PowerToys architecture, build system, and coding conventions
## Goal
Given a **pr_number**, orchestrate a cloud-reviewed, locally-fixed iteration loop:
1. Triage the PR for review readiness
2. Request GitHub Copilot cloud review on the PR
3. Wait for and fetch Copilot's review comments
4. Auto-fix easy comments, present hard ones for human review
5. Build-verify after fixes
6. Push fixes and request Copilot re-review — iterate until clean
**Output folder**: `Generated Files/communityPrReview/{{pr_number}}/`
## Capabilities
### MCP & Tools
- **GitHub MCP** (`github/*`) — fetch PR data, diffs, file contents, linked issues, CI status
- **Execute** — run `gh` CLI, build scripts, git commands
- **Edit** — apply code fixes in the PR worktree
- **Search** — find related patterns, conventions, and prior art in the codebase
- **Web** — research external references when needed
## Workflow
### Phase 1: Triage the PR
Before requesting review, evaluate whether the PR is appropriate for code review.
1. Fetch PR metadata:
```powershell
$pr = gh pr view {{pr_number}} --json number,title,body,author,state,isDraft,labels,headRefName,baseRefName,additions,deletions,changedFiles,reviewRequests,mergeStateStatus,url | ConvertFrom-Json
```
2. **Skip review** (report to user and stop) if ANY of these are true:
- PR is in **draft** state (`isDraft` is true)
- PR is **closed** or **merged**
- PR title or body contains incompleteness markers: `WIP`, `DO NOT MERGE`, `work in progress`, `experimental`, `proof of concept`, `POC`
- PR has labels indicating it is not ready: `work-in-progress`, `do-not-merge`, `experimental`, `draft`
3. **Flag for lighter review** (inform user, ask whether to proceed) if:
- PR is a **feature PR** (labels contain `feature`, `enhancement`, `new-feature`, or title suggests new functionality) AND appears to be in early stages (description mentions "initial", "first pass", "RFC", or small file count)
- PR has very large scope (>50 changed files or >2000 lines changed) — may need manual triage first
- PR has merge conflicts (`mergeStateStatus` is not clean)
4. If the PR passes triage, **confirm with the user** before proceeding:
```
PR #{{pr_number}}: "{{title}}" by @{{author}}
{{additions}}+ / {{deletions}}- across {{changedFiles}} files
Labels: {{labels}}
Status: Ready for Copilot cloud review.
Proceed? [Y/n]
```
### Phase 2: Setup Local Worktree
> **Worktree isolation**: The PR code is checked out into an ISOLATED worktree — the current
> worktree is never modified. All file edits and builds happen in the new worktree.
> Output files are written back to THIS worktree's `Generated Files/`.
5. Determine if the PR is from a fork or same repo:
```powershell
$prMeta = gh pr view {{pr_number}} --json isCrossRepository,headRepositoryOwner,headRefName,headRepository,maintainerCanModify | ConvertFrom-Json
```
6. Create an isolated worktree:
```powershell
if ($prMeta.isCrossRepository) {
$forkSpec = "$($prMeta.headRepositoryOwner.login):$($prMeta.headRefName)"
tools/build/New-WorktreeFromFork.ps1 -Spec $forkSpec -ForkRepo $prMeta.headRepository.name
} else {
git fetch origin $prMeta.headRefName
tools/build/New-WorktreeFromBranch.ps1 -Branch $prMeta.headRefName
}
```
7. Find the worktree path via `git worktree list` and save as `$prWorktree`.
8. Initialize submodules:
```powershell
Push-Location $prWorktree
git submodule update --init --recursive
Pop-Location
```
### Phase 3: Request GitHub Copilot Cloud Review
9. Get the repo identifier and assign Copilot as a reviewer:
```powershell
$repo = (gh repo view --json nameWithOwner --jq '.nameWithOwner')
gh api "repos/$repo/pulls/{{pr_number}}/requested_reviewers" -X POST -f 'reviewers[]=copilot'
```
If the API call fails (e.g., Copilot review not enabled for the repo), inform the user and stop.
### Phase 4: Wait for Copilot Review
10. Poll for Copilot's review to appear. Check periodically (do NOT use `Start-Sleep`;
instead, run the check command, use `get_terminal_output` to check later if needed):
```powershell
$reviews = gh api "repos/$repo/pulls/{{pr_number}}/reviews" | ConvertFrom-Json
$copilotReview = $reviews | Where-Object {
$_.user.login -match 'copilot' -or
($_.user.type -eq 'Bot' -and $_.user.login -match 'copilot')
} | Sort-Object -Property submitted_at -Descending | Select-Object -First 1
```
Wait until a Copilot review appears with state `CHANGES_REQUESTED` or `COMMENTED`.
**Timeout**: If no review appears after 5 minutes of polling, inform the user and ask
whether to continue waiting or abort.
### Phase 5: Fetch and Categorize Comments
11. Fetch all review comments from Copilot's review:
```powershell
# Inline review comments
$allComments = gh api "repos/$repo/pulls/{{pr_number}}/comments" | ConvertFrom-Json
$copilotComments = $allComments | Where-Object { $_.user.login -match 'copilot' }
# Top-level review body
$reviewBody = $copilotReview.body
```
12. For each Copilot comment, categorize as **easy** or **hard**:
**Easy** (auto-fix without asking):
- Style / formatting fixes (naming, whitespace, casing)
- Adding missing null checks or simple input validation
- Simple refactors (rename variable, extract constant, remove dead code)
- Adding or removing `using` / `#include` statements
- Removing unused imports or variables
- Simple string or comment fixes
- Adding missing braces, parentheses, or semicolons
- Copilot provides a concrete code suggestion that is clearly correct and localized
**Hard** (requires human decision):
- Architectural or design changes
- Logic changes that could affect runtime behavior
- Performance trade-offs with no clear winner
- Suggestions that conflict with existing PowerToys patterns
- Changes that span multiple files or components
- Security-related changes that need careful consideration
- Ambiguous suggestions where multiple valid approaches exist
- Suggestions the agent disagrees with or finds incorrect
13. Write categorized comments to `Generated Files/communityPrReview/{{pr_number}}/copilot-comments.md`:
```markdown
# Copilot Review Comments — PR #{{pr_number}} — Iteration N
## Easy (will auto-fix)
| # | File | Line | Comment Summary | Planned Fix |
|---|------|------|-----------------|-------------|
| 1 | ... | ... | ... | ... |
## Hard (needs human review)
| # | File | Line | Comment Summary | Why It's Hard |
|---|------|------|-----------------|---------------|
| 1 | ... | ... | ... | ... |
```
### Phase 6: Fix Easy Comments
14. In the worktree (`$prWorktree`), apply fixes for all **easy** comments:
- Read the file and surrounding context
- Apply the fix as suggested by Copilot (or as the agent deems correct)
- Keep fixes minimal and surgical — no scope creep
15. After all easy fixes are applied, commit them:
```powershell
Push-Location $prWorktree
git add -A
git commit -m "Address Copilot review: auto-fix simple comments (PR #{{pr_number}})"
Pop-Location
```
### Phase 7: Build Verification
16. Build the project in the worktree:
```powershell
Push-Location $prWorktree
tools\build\build-essentials.cmd
tools\build\build.cmd
Pop-Location
```
17. If build fails (exit code non-zero):
- Read `build.*.errors.log`
- If errors are simple (typos, missing semicolons, obvious type mismatches introduced by
the fixes), fix them and rebuild (max 3 attempts)
- If errors are complex or pre-existing, document them for the user
- Commit any build-fix changes separately
18. Record build results in `Generated Files/communityPrReview/{{pr_number}}/build-report.md`
### Phase 8: Present Results and STOP
19. Present a summary to the user:
```
## PR #{{pr_number}} — Copilot Review Summary (Iteration N)
### Copilot Review
- Total comments: X
- Easy (auto-fixed): Y
- Hard (needs your review): Z
### Auto-Fixes Applied
1. file.cs:42 — <what was fixed>
2. file.cpp:108 — <what was fixed>
...
### Build Status
✅ Build passed / ❌ Build failed (see build-report.md)
### Hard Comments — Need Your Input
1. **file.cs:42** — Copilot: "<summary>" → <why it's hard>
2. **file.cpp:108** — Copilot: "<summary>" → <why it's hard>
...
For each hard comment, tell me:
- "fix N" — accept and implement Copilot's suggestion
- "skip N" — dismiss the comment
- Or provide your own guidance
```
20. **STOP here and wait for the user's response** on hard comments.
Do NOT proceed until the user provides guidance.
### Phase 9: Process User Decisions on Hard Comments
21. After the user provides guidance:
- Apply fixes for comments the user approved (`fix N`)
- Skip dismissed comments (`skip N`)
- Implement user-provided alternative approaches
22. Commit the changes:
```powershell
Push-Location $prWorktree
git add -A
git commit -m "Address Copilot review: fix complex comments per maintainer guidance (PR #{{pr_number}})"
Pop-Location
```
23. Rebuild to verify:
```powershell
Push-Location $prWorktree
tools\build\build-essentials.cmd
tools\build\build.cmd
Pop-Location
```
### Phase 10: Push and Request Re-Review
24. Push the fixes to the PR branch:
```powershell
Push-Location $prWorktree
git push
Pop-Location
```
If push fails (e.g., fork doesn't allow maintainer edits), inform the user and provide
the diff as a patch or suggested changes instead (fall back to
`{skills_root}/community-pr-review/scripts/Format-SuggestedChanges.ps1`).
25. Request Copilot re-review:
```powershell
gh api "repos/$repo/pulls/{{pr_number}}/requested_reviewers" -X POST -f 'reviewers[]=copilot'
```
26. **Iterate**: Go back to **Phase 4** (wait for new review) and repeat.
- Max **3 full iterations** of the review-fix cycle
- If after 3 iterations there are still unresolved comments, stop and present the final state
### Phase 11: Finalize
27. After the loop completes (Copilot approves, no more actionable comments, or max iterations reached),
write final outputs:
- `Generated Files/communityPrReview/{{pr_number}}/final-summary.md`
- `Generated Files/communityPrReview/{{pr_number}}/.signal`
28. Present a final summary:
```
## Final Review Status — PR #{{pr_number}}
- Iterations: N
- Total Copilot comments addressed: X
- Auto-fixed: Y
- Human-guided fixes: Z
- Skipped / dismissed: W
- Build: ✅ / ❌
The PR branch has been updated with all fixes.
Remaining action: verify E2E and approve/merge when ready.
```
29. **Cleanup note**: The PR worktree can be removed with:
```powershell
tools/build/Delete-Worktree.ps1 -Pattern "<branch>" -Force
```
## Output Files
All outputs go to `Generated Files/communityPrReview/{{pr_number}}/`:
| File | Description |
|------|-------------|
| `copilot-comments.md` | Categorized Copilot review comments per iteration |
| `fix-summary.md` | Record of all fixes applied (auto + human-guided) |
| `build-report.md` | Build verification results |
| `final-summary.md` | Complete review record across all iterations |
| `.signal` | Completion signal |
## Signal File Format
```json
{
"status": "success",
"prNumber": 0,
"iterations": 2,
"copilotComments": { "total": 12, "autoFixed": 8, "humanGuided": 3, "skipped": 1 },
"buildStatus": "success",
"timestamp": "2026-04-14T00:00:00Z"
}
```
Status values: `success`, `partial` (review done, build failed), `failure`
## Boundaries
- **Never approve or merge PRs** — leave final approval to the human maintainer
- **Never force-push** — always use regular `git push`
- **Stop for hard comments** — do not make complex or ambiguous decisions autonomously
- **Max 3 iterations** — do not loop indefinitely on Copilot re-reviews
- **Confirm triage** — always confirm triage results with the user before starting the review cycle
- If push fails and cannot be resolved, fall back to generating suggested changes for the PR author
- Stop when human interaction is needed (E2E verification, subjective design decisions)
- If the PR is not a bug fix (feature, refactor, etc.), note this but still review
- Maximum 3 review→fix iterations — if issues persist, report remaining issues for human decision
## Parameter
- **pr_number**: Extract from `#123`, `PR 123`, or plain number. If missing, **ASK** the user.

View File

@@ -0,0 +1,46 @@
---
agent: 'ReviewCommunityPR'
description: 'Triage a community PR, request GitHub Copilot cloud review, process comments locally (auto-fix easy ones, escalate hard ones), build-verify, and iterate'
tools: ['execute', 'read', 'edit', 'search', 'web', 'github/*']
argument-hint: 'PR number (e.g., #45234 or 45234)'
---
# Review Community PR
Review a community-contributed PR using GitHub Copilot cloud review. This prompt invokes the `ReviewCommunityPR` agent.
## What It Does
Given a PR number, this workflow:
1. **Triages the PR** — checks if it's ready for review (skips drafts, WIP, incomplete PRs; flags early-stage feature PRs)
2. **Requests Copilot cloud review** — assigns GitHub Copilot as a reviewer on the PR
3. **Waits for Copilot comments** — polls until Copilot posts its review
4. **Categorizes comments** — separates easy (auto-fixable) from hard (needs human decision)
5. **Auto-fixes easy comments** — applies straightforward fixes without asking
6. **Builds the project** — runs `build-essentials` then `build` to verify fixes don't break anything
7. **Stops for hard comments** — presents complex comments for your review and guidance
8. **Iterates** — pushes fixes and requests Copilot re-review (up to 3 cycles)
9. **Generates outputs**:
- `copilot-comments.md` — Categorized Copilot review comments per iteration
- `fix-summary.md` — Record of all fixes applied (auto + human-guided)
- `build-report.md` — Build status and actions taken
- `final-summary.md` — Complete review record across all iterations
## Usage
Provide a PR number:
```
Review community PR #45234
```
Or run the script directly:
```powershell
.github/skills/community-pr-review/scripts/Start-CommunityPRReview.ps1 -PRNumber 45234
```
## Output Location
`Generated Files/communityPrReview/<PR>/`

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) Microsoft Corporation.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,173 @@
---
name: community-pr-review
description: Triage and review community PRs using GitHub Copilot cloud review. Use when asked to review a community PR, triage a PR for review readiness, request Copilot review on GitHub, process Copilot review comments locally, auto-fix simple review findings, or iterate on PR review feedback.
license: Complete terms in LICENSE.txt
---
# Community PR Review Skill
**Triage**, **request Copilot cloud review**, **process comments locally**, **build-verify**, and **iterate** on community PRs. Auto-fixes straightforward findings, escalates complex decisions to the maintainer.
## Skill Contents
```
.github/skills/community-pr-review/
├── SKILL.md # This file
├── LICENSE.txt # MIT License
├── scripts/
│ ├── Start-CommunityPRReview.ps1 # Main orchestrator (loop-aware)
│ ├── Format-SuggestedChanges.ps1 # Generate GitHub suggestion blocks from diff (fallback)
│ └── ReviewLib.ps1 # Shared helpers
└── references/
├── review-community-pr.prompt.md # Detailed review prompt
└── review-dimensions.md # Review criteria reference (for manual use)
```
## When to Use This Skill
- Triage a PR to decide if it's ready for code review
- Review a community-contributed PR using GitHub Copilot cloud review
- Process Copilot review comments: auto-fix easy ones, escalate hard ones
- Verify a PR builds cleanly after applying fixes
- Iterate: push fixes and re-request Copilot review until clean
## Prerequisites
- GitHub CLI (`gh`) installed and authenticated
- GitHub Copilot code review enabled for the repository
- PowerShell 7+
- Visual Studio 2022 17.4+ or Visual Studio 2026 (for build verification)
- Git submodules initialized (`git submodule update --init --recursive`)
## Quick Start
### Option A: Use the agent directly
Invoke the `ReviewCommunityPR` agent with a PR number:
```
Review community PR #45234
```
### Option B: Run the orchestrator script
```powershell
.github/skills/community-pr-review/scripts/Start-CommunityPRReview.ps1 -PRNumber 45234
```
### Option C: Use the prompt
Run `.github/prompts/review-community-pr.prompt.md` with a PR number.
## Workflow Overview
### Phase 1: Triage the PR
Evaluate whether the PR is appropriate for review:
- **Skip** drafts, closed/merged PRs, WIP/experimental PRs
- **Flag** early-stage feature PRs, very large PRs, PRs with merge conflicts
- **Confirm** with user before proceeding
### Phase 2: Setup Local Worktree
1. Determine fork vs same-repo: `gh pr view <PR> --json isCrossRepository,...`
2. Fork: `tools/build/New-WorktreeFromFork.ps1 -Spec <user>:<branch>`
3. Same-repo: `tools/build/New-WorktreeFromBranch.ps1 -Branch <branch>`
4. Initialize submodules
### Phase 3: Request GitHub Copilot Cloud Review
Assign Copilot as a reviewer on the PR via the GitHub API:
```powershell
$repo = (gh repo view --json nameWithOwner --jq '.nameWithOwner')
gh api "repos/$repo/pulls/<PR>/requested_reviewers" -X POST -f 'reviewers[]=copilot'
```
### Phase 4: Wait for and Fetch Comments
Poll until Copilot posts a review, then fetch all inline comments.
### Phase 5: Categorize and Fix
```
┌──────────────────────────────┐
│ Fetch Copilot review │
│ comments from GitHub │
└──────────┬───────────────────┘
┌──────────────────────────────┐
│ Categorize each comment │
│ as EASY or HARD │
└──────────┬───────────────────┘
┌──────┴──────┐
│ │
▼ ▼
┌─────────┐ ┌──────────────┐
│ EASY │ │ HARD │
│ Auto- │ │ Present to │
│ fix │ │ user, STOP │
└────┬────┘ └──────┬───────┘
│ │
▼ │ (user provides guidance)
┌──────────────┐ │
│ Build check │ ▼
│ (essentials │ ┌──────────────┐
│ + modules) │ │ Apply user │
└──────┬───────┘ │ decisions │
│ └──────┬───────┘
│ │
▼ ▼
┌──────────────────────────────┐
│ Push fixes, request │
│ Copilot re-review │
└──────────┬───────────────────┘
(loop back, max 3x)
```
**Easy comments** (auto-fix): style fixes, missing null checks, unused imports, simple refactors, concrete Copilot suggestions.
**Hard comments** (need human): architecture changes, logic changes, performance trade-offs, multi-file changes, security decisions, ambiguous suggestions.
### Phase 6: Build Verification
After fixes, build in the worktree:
```powershell
Push-Location $prWorktree
tools\build\build-essentials.cmd
tools\build\build.cmd
Pop-Location
```
Fix simple build-breaking changes automatically (max 3 attempts).
### Phase 7: Push and Iterate
Push fixes → request Copilot re-review → repeat (max 3 full iterations).
## Output
All outputs go to `Generated Files/communityPrReview/<PR>/`:
| File | Description |
|------|-------------|
| `copilot-comments.md` | Categorized Copilot review comments per iteration |
| `fix-summary.md` | Record of all fixes applied (auto + human-guided) |
| `build-report.md` | Build status, errors encountered, fix-up actions taken |
| `final-summary.md` | Complete review record across all iterations |
| `.signal` | Completion signal for tooling |
### Signal File Format
```json
{
"status": "success",
"prNumber": 45234,
"iterations": 2,
"copilotComments": { "total": 12, "autoFixed": 8, "humanGuided": 3, "skipped": 1 },
"buildStatus": "success",
"timestamp": "2026-04-14T10:05:23Z"
}
```
Status values: `success`, `partial` (review done, build failed), `failure`
## Related Skills
| Skill | Purpose |
|-------|---------|
| `pr-fix` | Fix review comments after review identifies issues |

View File

@@ -0,0 +1,383 @@
---
agent: 'agent'
description: 'Review a community bug-fix PR: 7-dimension review, review→fix loop, build verification, and GitHub suggested changes'
tools: ['execute', 'read', 'edit', 'search', 'web', 'github/*']
argument-hint: 'PR number (e.g., #45234 or 45234)'
---
# Review Community Bug-Fix PR
**Goal**: Given `{{pr_number}}`, run a review→fix loop: review across 7 dimensions, fix high/medium issues, re-review until clean, verify build, then generate GitHub suggested changes and verification instructions.
**Output folder**: `Generated Files/communityPrReview/{{pr_number}}/`
## PR Selection
Resolve the target PR:
1. Parse the invocation text for an explicit PR number (first integer following `#` or `PR`).
2. If not found, run `gh pr view --json number` for the current branch.
3. If still unknown, **ASK** the user.
## Phase 1: Understand the PR
### 1.1 Fetch PR Metadata
```
gh pr view {{pr_number}} --json number,title,body,author,baseRefName,headRefName,baseRefOid,headRefOid,labels,url,state
```
### 1.2 Identify the Bug
- Parse the PR description for linked issue references (`Fixes #XXXX`, `Closes #XXXX`)
- If linked issue found, fetch it: `gh issue view <issue_number> --json title,body,labels`
- Understand: what is the bug? what is the expected behavior? what is the actual behavior?
### 1.3 Fetch Changed Files
```
gh pr diff {{pr_number}}
```
Also fetch the file list:
```
gh pr view {{pr_number}} --json files
```
### 1.4 Record Original Head SHA
Save the PR's head commit SHA as `originalHeadSha` — this is the baseline for generating suggested changes later.
```powershell
$originalHeadSha = (gh pr view {{pr_number}} --json headRefOid --jq .headRefOid)
```
### 1.5 Create Worktree and Initial Build
> **Worktree isolation**: Do NOT use `gh pr checkout` — it would overwrite the current branch.
> Instead, create an ISOLATED worktree for the PR's branch using the existing scripts in
> `tools/build/`. All file reads, edits, and builds happen in the new worktree.
> Output files go to the original worktree's `Generated Files/`.
#### Step 1: Determine fork vs same-repo
```powershell
$prMeta = gh pr view {{pr_number}} --json isCrossRepository,headRepositoryOwner,headRefName,headRepository | ConvertFrom-Json
```
#### Step 2: Create the worktree
```powershell
# Fork PR:
if ($prMeta.isCrossRepository) {
$forkSpec = "$($prMeta.headRepositoryOwner.login):$($prMeta.headRefName)"
tools/build/New-WorktreeFromFork.ps1 -Spec $forkSpec -ForkRepo $prMeta.headRepository.name
}
# Same-repo PR:
else {
git fetch origin $prMeta.headRefName
tools/build/New-WorktreeFromBranch.ps1 -Branch $prMeta.headRefName
}
```
#### Step 3: Find the new worktree path
```powershell
# Parse git worktree list to find the newly created worktree
git worktree list
# The new worktree is a sibling directory, e.g., Q:\PowerToys-<hash>
# Save it as $prWorktree
```
#### Step 4: Initialize and build
```powershell
Push-Location $prWorktree
git submodule update --init --recursive
tools\build\build-essentials.cmd
tools\build\build.cmd
Pop-Location
```
**IMPORTANT**: From this point on, ALL file operations on PR code use `$prWorktree` paths:
- Reading files: `$prWorktree\src\modules\...`
- Editing fixes: edit files at `$prWorktree\...`
- Building: run `build.cmd` from `$prWorktree`
- Git operations: `git -C $prWorktree ...`
If the initial build fails, try merging main in the worktree:
```powershell
Push-Location $prWorktree
git fetch origin main
git merge origin/main --no-edit
tools\build\build.cmd
Pop-Location
```
Record the build result. Even if it fails, proceed to code review.
## Phase 2: Review→Fix Loop (max 3 iterations)
For each iteration, perform the code review then fix. Track `iteration` starting at 1.
### Step 2a: Code Review (7 Dimensions)
For each dimension below, analyze ALL changed files and produce findings. Skip a dimension only if no changed files are relevant to it.
### Dimension 1: Correctness
- Does the fix actually solve the reported bug?
- Are all code paths to the bug covered?
- Are edge cases handled (null, empty, boundary values, concurrent access)?
- Could the fix introduce new bugs or regressions?
- Are tests updated/added to cover the fix?
- Is the fix complete or only partial?
### Dimension 2: Security
- Is user input validated before use?
- Are file paths canonicalized to prevent traversal?
- Are shell commands avoided or properly escaped?
- Is elevation (UAC) used only when necessary and scoped minimally?
- Are credentials, tokens, or PII never logged or exposed?
- For native code: buffer overflow prevention, format string safety, P/Invoke correctness?
- Are IPC messages validated before processing?
- Reference: OWASP Top 10, CWE Top 25, Microsoft SDL
### Dimension 3: Performance
- Are hot paths (hooks, tight loops, event handlers) kept efficient?
- No unnecessary allocations in frequently called code?
- Are async patterns used correctly (no sync-over-async, proper cancellation)?
- Are collections appropriately sized?
- Are expensive operations (file I/O, registry, network) minimized?
- No logging in performance-critical paths?
### Dimension 4: Reliability
- Are errors handled gracefully (try/catch, HRESULT checks, null guards)?
- Are resources properly disposed (IDisposable, COM objects, handles)?
- Are race conditions prevented (thread safety, locking)?
- Are event subscriptions balanced (subscribe ↔ unsubscribe)?
- Does the fix handle process/module lifecycle correctly?
- Are retries and timeouts appropriate?
### Dimension 5: Design
- Is the fix appropriately scoped (not over-engineered)?
- Does it follow SOLID principles?
- Is the abstraction level appropriate?
- Could the fix be simpler while still being correct?
- Are there any code smells introduced (magic numbers, god methods, deep nesting)?
- Is the fix in the right layer/module?
### Dimension 6: Compatibility
- Are there breaking changes to public APIs or IPC contracts?
- Is backward compatibility maintained for settings/config files?
- Does the fix work across supported Windows versions (10 1803+)?
- Are there implications for the installer or upgrade path?
- If modifying shared code (`src/common/`), is ABI stability preserved?
### Dimension 7: Repo Patterns
- Does the code follow PowerToys naming conventions?
- Is the style consistent with surrounding code (check `.editorconfig`, `.clang-format`)?
- Are new strings localized (`.resx` files)?
- Is logging following the PowerToys pattern (spdlog for C++, Logger for C#)?
- Are module interface contracts preserved?
- Is the PR atomic (one logical change)?
### Step 2b: Write Review Comments
Write findings to `Generated Files/communityPrReview/{{pr_number}}/review-comments.md`.
For **high** and **medium** findings, ALWAYS include a ` ```suggestion ` block with replacement code:
```markdown
# Review Comments — PR #{{pr_number}} — Iteration {{iteration}}
## Summary
- **High severity**: <count>
- **Medium severity**: <count>
- **Low severity**: <count>
- **Info**: <count>
## Overall Assessment
<2-3 sentence assessment>
---
### [HIGH] Correctness — `path/to/file.ext`:42-48
<Clear description of the issue>
` ```suggestion `
<replacement code that fixes the issue>
` ``` `
---
### [MEDIUM] Security — `path/to/file.ext`:15-18
<Clear description of the issue>
` ```suggestion `
<replacement code that fixes the issue>
` ``` `
---
```
### Step 2c: Check Exit Condition
**Exit the loop if ANY of these are true:**
- No **high** or **medium** severity findings in this iteration
- This is iteration 3 (maximum reached)
- All high/medium findings are architectural or require author decision (cannot be auto-fixed)
If exiting → go to Phase 3.
### Step 2d: Apply Fixes
For each **high** and **medium** finding from Step 2b:
1. Open the file at the referenced line range **in `$prWorktree`**
2. Apply the fix from the ` ```suggestion ` block (or implement the intent if suggestion is conceptual)
3. Keep fixes minimal — only what's needed
After all fixes:
- Run `tools\build\build.cmd` **from `$prWorktree`**
- If build fails, read `build.*.errors.log` and fix build errors (max 3 attempts)
- Record all fixes in `fix-summary.md`
### Step 2e: Increment and Loop
Increment `iteration` and go back to Step 2a to re-review the fixed code.
---
## Phase 3: Generate Suggested Changes
After the review→fix loop completes, generate GitHub suggested changes from the diff:
```powershell
# Compare worktree (with fixes) against original PR head
git -C $prWorktree diff <originalHeadSha> HEAD
```
For each changed hunk, write a suggested change using GitHub's native format.
Write `Generated Files/communityPrReview/{{pr_number}}/suggested-changes.md`:
```markdown
# Suggested Changes — PR #{{pr_number}}
These changes address review findings from {{iteration}} review→fix iteration(s).
Each suggestion uses GitHub's suggested changes format — post as PR review comments.
## Summary
- **Total suggestions**: <count>
- **Files affected**: <count>
- **Iterations needed**: <count>
## Fixes Applied
<Brief list of what each fix addresses>
---
## `path/to/file.ext`
### Suggestion 1 (lines X-Y)
**Addresses:** [SEVERITY] Dimension — <brief finding description>
` ```suggestion `
<replacement code>
` ``` `
---
```
If no fixes were needed (clean review), write:
```markdown
# Suggested Changes — PR #{{pr_number}}
No code changes needed. The review found no high/medium issues.
```
## Phase 4: Build Report
Write `Generated Files/communityPrReview/{{pr_number}}/build-report.md`:
```markdown
# Build Report — PR #{{pr_number}}
## Build Status: SUCCESS | FAILURE | SUCCESS_AFTER_MERGE
## Environment
- Branch: <head branch>
- Base: <base branch>
- Head SHA: <sha>
- Build date: <ISO timestamp>
- Review iterations: <count>
## Build Steps
1. <Step taken> — <result>
2. <Step taken> — <result>
## Actions Taken to Fix Build (if any)
- <action 1>
- <action 2>
## Remaining Build Errors (if any)
` ``` `
<error details>
` ``` `
## Suggestions for Author
- <suggestion for the PR author if build issues need their attention>
```
## Phase 5: Verification Guide
Write `Generated Files/communityPrReview/{{pr_number}}/verification-guide.md`:
```markdown
# Verification Guide — PR #{{pr_number}}
## Bug Summary
<1-2 sentence description of the bug from the linked issue>
## Steps to Reproduce the Original Bug
1. <step>
2. <step>
3. <step>
## How to Verify the Fix
1. <step>
2. <step>
3. <step>
## Expected Behavior After Fix
- <what should happen>
## Edge Cases to Test
- <edge case 1>
- <edge case 2>
## Regression Areas to Smoke-Test
- <area 1 — why it might be affected>
- <area 2 — why it might be affected>
## Module/Feature Affected
- **Module**: <module name>
- **Settings path**: <if applicable>
- **Hotkey**: <if applicable>
```
## Phase 6: Signal File
Write `Generated Files/communityPrReview/{{pr_number}}/.signal`:
```json
{
"status": "success|partial|failure",
"prNumber": {{pr_number}},
"originalHeadSha": "<original PR head SHA before any fixes>",
"iterations": <number of review-fix iterations>,
"reviewFindings": { "high": 0, "medium": 0, "low": 0, "info": 0 },
"fixesApplied": <count of fixes applied>,
"suggestedChanges": <count of GitHub suggestions generated>,
"buildStatus": "success|failure|success_after_merge",
"buildActions": ["list of actions taken"],
"timestamp": "<ISO timestamp>"
}
```
## Constraints
- Keep fixes minimal — only address high/medium review findings
- **Build verification may modify** the worktree (merge main, apply fixes) but these changes are NOT pushed
- All fixes are presented as **suggested changes** for the PR author to accept
- Keep comments specific, actionable, and fix-oriented
- Reference file paths and line numbers in all findings
- Stop and present results when human interaction is needed
- Maximum 3 review→fix iterations — report remaining issues if loop doesn't converge

View File

@@ -0,0 +1,210 @@
# Review Dimensions Reference
Detailed criteria for each of the 7 review dimensions used in community bug-fix PR review.
## 1. Correctness
**Goal**: Verify the fix solves the reported bug without introducing regressions.
### Checklist
- [ ] Fix addresses the root cause described in the linked issue
- [ ] All code paths to the bug are covered
- [ ] Edge cases handled: null, empty, boundary values, max/min, concurrent access
- [ ] No new bugs introduced by the fix
- [ ] Tests added or updated to cover the fix
- [ ] Fix is complete (not partial — all scenarios in the bug report addressed)
- [ ] Conditional branches handle all expected cases
- [ ] Loops terminate correctly (no infinite loops, off-by-one errors)
- [ ] State properly initialized, used, and cleaned up
### PowerToys-Specific
- [ ] Module interface contract remains intact
- [ ] Hotkey registration and unregistration balanced
- [ ] Feature works correctly with Runner lifecycle (enable/disable)
- [ ] Settings UI changes reflected in module behavior
### Severity Guide
| Severity | Criteria |
|----------|----------|
| High | Fix doesn't solve the bug, introduces crash, data loss |
| Medium | Partial fix, edge cases broken, degraded UX |
| Low | Minor issues, cosmetic, suboptimal but working |
| Info | Suggestions for improvement |
---
## 2. Security
**Goal**: Identify security vulnerabilities and unsafe practices.
### Checklist
- [ ] User input validated before use
- [ ] File paths canonicalized (no `..` traversal)
- [ ] Shell commands avoided or properly escaped
- [ ] Elevation (UAC) used only when necessary, scoped minimally
- [ ] Credentials, tokens, PII never logged or exposed
- [ ] Temporary files created securely
- [ ] Named pipes/shared memory secured with ACLs
- [ ] IPC messages validated before processing
- [ ] DLL search paths secured
- [ ] No format string vulnerabilities
### Native Code (C/C++)
- [ ] Buffer overflow prevention
- [ ] P/Invoke signatures correct (buffer sizes)
- [ ] Memory zeroed before freeing (for secrets)
- [ ] No use-after-free patterns
- [ ] RAII patterns for resource management
### Severity Guide
| Severity | Criteria |
|----------|----------|
| High | RCE, privilege escalation, data breach possible |
| Medium | Local exploit, information disclosure, weak crypto |
| Low | Defense in depth improvement, hardening opportunity |
| Info | Security best practice suggestions |
### References
- OWASP Top 10: https://owasp.org/www-project-top-ten/
- CWE Top 25: https://cwe.mitre.org/top25/
- Microsoft SDL: https://www.microsoft.com/en-us/securityengineering/sdl
---
## 3. Performance
**Goal**: Ensure no performance regressions, especially in hot paths.
### Checklist
- [ ] Hot paths (hooks, tight loops, event handlers) kept efficient
- [ ] No unnecessary allocations in frequently called code
- [ ] Async patterns correct (no sync-over-async, proper cancellation tokens)
- [ ] Collections appropriately sized (no repeated resizing)
- [ ] Expensive operations (file I/O, registry, network) minimized
- [ ] No logging in performance-critical paths
- [ ] LINQ used appropriately (no repeated enumeration)
- [ ] String operations efficient (StringBuilder for loops)
- [ ] No blocking on UI thread
### PowerToys-Specific
- [ ] Hook callbacks execute quickly (< 1ms)
- [ ] Settings reads cached appropriately
- [ ] Module enable/disable is fast
- [ ] No startup time regression
### Severity Guide
| Severity | Criteria |
|----------|----------|
| High | Noticeable lag, UI freeze, O(n²) in common path |
| Medium | Subtle perf regression, unnecessary work |
| Low | Minor optimization opportunity |
| Info | Performance best practice |
---
## 4. Reliability
**Goal**: Verify robust error handling and resource management.
### Checklist
- [ ] Errors handled gracefully (try/catch, HRESULT checks, null guards)
- [ ] Resources properly disposed (IDisposable, COM objects, handles)
- [ ] Race conditions prevented (thread safety, proper locking)
- [ ] Event subscriptions balanced (subscribe ↔ unsubscribe)
- [ ] Process/module lifecycle handled correctly
- [ ] Retries and timeouts appropriate
- [ ] Graceful degradation on failure (don't crash the whole app)
- [ ] Exception types appropriate (not catching all Exception)
- [ ] Finalizers/destructors not relying on other managed objects
### PowerToys-Specific
- [ ] Module crash doesn't take down Runner
- [ ] Named pipe disconnection handled
- [ ] Settings file corruption handled
- [ ] Multi-monitor/DPI changes handled
### Severity Guide
| Severity | Criteria |
|----------|----------|
| High | Crash, hang, resource leak causing system impact |
| Medium | Intermittent failure, poor error recovery |
| Low | Missing error handling in rare path |
| Info | Reliability improvement suggestion |
---
## 5. Design
**Goal**: Assess code quality and architectural fit.
### Checklist
- [ ] Fix appropriately scoped (not over-engineered)
- [ ] SOLID principles followed
- [ ] Abstraction level appropriate
- [ ] Could be simpler while still correct?
- [ ] No code smells (magic numbers, god methods, deep nesting)
- [ ] Fix is in the right layer/module
- [ ] No unnecessary coupling introduced
- [ ] API surface changes are intentional and minimal
### Severity Guide
| Severity | Criteria |
|----------|----------|
| High | Architectural violation, wrong abstraction layer |
| Medium | Design smell, maintainability concern |
| Low | Minor refactoring opportunity |
| Info | Design improvement suggestion |
---
## 6. Compatibility
**Goal**: Ensure no breaking changes or compatibility issues.
### Checklist
- [ ] No breaking changes to public APIs
- [ ] IPC contracts (named pipes, JSON) preserved
- [ ] Backward compatibility for settings/config files
- [ ] Works across Windows 10 1803+ and Windows 11
- [ ] Installer/upgrade path not affected
- [ ] If modifying `src/common/`: ABI stability preserved
- [ ] Schema migrations handled (if settings format changed)
- [ ] GPO/policy paths not broken
### Severity Guide
| Severity | Criteria |
|----------|----------|
| High | Breaking change, data loss on upgrade |
| Medium | Partial compat issue, workaround exists |
| Low | Minor compat concern, future risk |
| Info | Compatibility best practice |
---
## 7. Repo Patterns
**Goal**: Verify adherence to PowerToys conventions.
### Checklist
- [ ] Naming conventions followed (check `.editorconfig`, `.clang-format`)
- [ ] Style consistent with surrounding code
- [ ] New strings localized (`.resx` files)
- [ ] Logging follows pattern (spdlog for C++, Logger for C#)
- [ ] Module interface contracts preserved
- [ ] PR is atomic (one logical change)
- [ ] No drive-by refactors mixed in
- [ ] New dependencies listed in `NOTICE.md`
- [ ] Test coverage appropriate
### Style References
- C#: `src/.editorconfig`, StyleCop.Analyzers
- C++: `src/.clang-format`
- XAML: XamlStyler
### Severity Guide
| Severity | Criteria |
|----------|----------|
| High | Contract violation, ABI break |
| Medium | Convention violation, inconsistent pattern |
| Low | Minor style issue |
| Info | Pattern improvement suggestion |

View File

@@ -0,0 +1,228 @@
<#
.SYNOPSIS
Generate GitHub suggested changes from the diff between original PR and fixed code.
.DESCRIPTION
Compares the current worktree state (with fixes applied) against the original PR
head commit. For each changed hunk, generates a GitHub review comment with a
```suggestion``` code block that the PR author can apply directly.
.PARAMETER PRNumber
The PR number (required).
.PARAMETER OriginalSha
The original HEAD SHA of the PR before fixes were applied.
If not provided, reads from the .signal file.
.PARAMETER OutputDir
Directory containing review outputs. Default: Generated Files/communityPrReview/<PR>
.PARAMETER WorktreeDir
Path to the PR worktree. If provided, runs git commands there instead of the current repo.
.PARAMETER MinContextLines
Number of context lines around each change. Default: 3.
.EXAMPLE
./Format-SuggestedChanges.ps1 -PRNumber 45234 -OriginalSha abc123
.EXAMPLE
./Format-SuggestedChanges.ps1 -PRNumber 45234
#>
param(
[int]$PRNumber,
[string]$OriginalSha,
[string]$OutputDir,
[string]$WorktreeDir,
[int]$MinContextLines = 3,
[switch]$Help
)
$ErrorActionPreference = 'Stop'
if ($Help) {
Get-Help $MyInvocation.MyCommand.Path -Full
return
}
if (-not $PRNumber -or $PRNumber -eq 0) {
Write-Error 'Format-SuggestedChanges: -PRNumber is required.'
return
}
# Load helpers
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
. "$scriptDir/ReviewLib.ps1"
$repoRoot = Get-RepoRoot
# Use WorktreeDir for git operations if provided, otherwise the current repo
$gitDir = if (-not [string]::IsNullOrWhiteSpace($WorktreeDir)) { $WorktreeDir } else { $repoRoot }
if ([string]::IsNullOrWhiteSpace($OutputDir)) {
$OutputDir = Join-Path $repoRoot "Generated Files/communityPrReview/$PRNumber"
}
# Resolve original SHA from signal file if not provided
if ([string]::IsNullOrWhiteSpace($OriginalSha)) {
$signalPath = Join-Path $OutputDir '.signal'
if (Test-Path $signalPath) {
$signal = Get-Content $signalPath -Raw | ConvertFrom-Json
$OriginalSha = $signal.originalHeadSha
}
if ([string]::IsNullOrWhiteSpace($OriginalSha)) {
Write-Error 'Format-SuggestedChanges: -OriginalSha is required (or must be in .signal file).'
return
}
}
Info "Generating suggested changes for PR #$PRNumber"
Info "Original SHA: $OriginalSha"
Info "Current HEAD: $(git -C $gitDir rev-parse HEAD)"
Info "Git dir: $gitDir"
# Get the diff between original PR and current state
$diffOutput = git -C $gitDir diff $OriginalSha HEAD --unified=$MinContextLines --no-color 2>&1
if (-not $diffOutput) {
Info "No changes between original PR and current state."
$noChangesReport = @"
# Suggested Changes PR #$PRNumber
No code changes were needed. The review found no high/medium issues requiring fixes.
"@
$noChangesReport | Set-Content (Join-Path $OutputDir 'suggested-changes.md') -Force
return
}
# Parse the unified diff into per-file, per-hunk suggestions
$suggestions = [System.Collections.Generic.List[hashtable]]::new()
$currentFile = $null
$currentHunk = $null
$oldLines = @()
$newLines = @()
$hunkStartLine = 0
function Flush-Hunk {
if ($null -eq $script:currentFile -or $null -eq $script:currentHunk) { return }
if (($script:oldLines.Count -eq 0) -and ($script:newLines.Count -eq 0)) { return }
# Build the suggestion text (what the new code should be)
$suggestionText = ($script:newLines -join "`n")
$script:suggestions.Add(@{
File = $script:currentFile
StartLine = $script:hunkStartLine
EndLine = $script:hunkStartLine + $script:oldLines.Count - 1
OldCode = ($script:oldLines -join "`n")
NewCode = $suggestionText
LineCount = $script:oldLines.Count
})
$script:oldLines = @()
$script:newLines = @()
}
foreach ($line in ($diffOutput -split "`n")) {
if ($line -match '^diff --git a/(.+) b/(.+)$') {
Flush-Hunk
$currentFile = $Matches[2]
$currentHunk = $null
continue
}
if ($line -match '^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@') {
Flush-Hunk
$currentHunk = $true
$hunkStartLine = [int]$Matches[2]
$oldLines = @()
$newLines = @()
$lineCounter = [int]$Matches[2]
continue
}
if ($null -eq $currentHunk) { continue }
# Track context and changes within a hunk
if ($line.StartsWith('+')) {
# Added line (new code)
$newLines += $line.Substring(1)
}
elseif ($line.StartsWith('-')) {
# Removed line (old code)
if ($oldLines.Count -eq 0) {
$hunkStartLine = $lineCounter
}
$oldLines += $line.Substring(1)
}
else {
# Context line — if we have accumulated changes, flush them
if ($oldLines.Count -gt 0 -or $newLines.Count -gt 0) {
Flush-Hunk
}
$lineCounter++
# Context lines are part of both old and new
$newLines = @()
$oldLines = @()
continue
}
}
Flush-Hunk
# Generate the suggested-changes.md
$output = [System.Text.StringBuilder]::new()
[void]$output.AppendLine("# Suggested Changes — PR #$PRNumber")
[void]$output.AppendLine("")
[void]$output.AppendLine("These changes address review findings. Each suggestion can be applied directly on GitHub.")
[void]$output.AppendLine("")
[void]$output.AppendLine("## Summary")
[void]$output.AppendLine("- **Total suggestions**: $($suggestions.Count)")
$fileGroups = $suggestions | Group-Object { $_.File }
[void]$output.AppendLine("- **Files affected**: $($fileGroups.Count)")
[void]$output.AppendLine("")
$suggestionNum = 0
foreach ($group in $fileGroups) {
[void]$output.AppendLine("---")
[void]$output.AppendLine("")
[void]$output.AppendLine("## ``$($group.Name)``")
[void]$output.AppendLine("")
foreach ($suggestion in $group.Group) {
$suggestionNum++
$lineRange = if ($suggestion.StartLine -eq $suggestion.EndLine) {
"line $($suggestion.StartLine)"
} else {
"lines $($suggestion.StartLine)-$($suggestion.EndLine)"
}
[void]$output.AppendLine("### Suggestion $suggestionNum ($lineRange)")
[void]$output.AppendLine("")
[void]$output.AppendLine("``````suggestion")
[void]$output.AppendLine($suggestion.NewCode)
[void]$output.AppendLine("``````")
[void]$output.AppendLine("")
}
}
# Also generate a machine-readable JSON for posting via API
$jsonSuggestions = $suggestions | ForEach-Object {
@{
path = $_.File
start_line = $_.StartLine
end_line = $_.EndLine
body = "``````suggestion`n$($_.NewCode)`n``````"
}
}
$output.ToString() | Set-Content (Join-Path $OutputDir 'suggested-changes.md') -Force
$jsonSuggestions | ConvertTo-Json -Depth 5 | Set-Content (Join-Path $OutputDir 'suggested-changes.json') -Force
Success "Generated $($suggestions.Count) suggestions across $($fileGroups.Count) files."
Info "Output: $(Join-Path $OutputDir 'suggested-changes.md')"
Info "JSON: $(Join-Path $OutputDir 'suggested-changes.json')"
return @{
SuggestionCount = $suggestions.Count
FileCount = $fileGroups.Count
Suggestions = $suggestions
}

View File

@@ -0,0 +1,48 @@
# ReviewLib.ps1 - Shared helpers for community PR review workflow
#region Console Output Helpers
function Info { param([string]$Message) Write-Host $Message -ForegroundColor Cyan }
function Warn { param([string]$Message) Write-Host $Message -ForegroundColor Yellow }
function Err { param([string]$Message) Write-Host $Message -ForegroundColor Red }
function Success { param([string]$Message) Write-Host $Message -ForegroundColor Green }
#endregion
#region Repository Helpers
function Get-RepoRoot {
$root = git rev-parse --show-toplevel 2>$null
if (-not $root) { throw 'Not inside a git repository.' }
return (Resolve-Path $root).Path
}
function Get-SkillsRoot {
$repoRoot = Get-RepoRoot
if (Test-Path (Join-Path $repoRoot '.github/skills')) { return '.github/skills' }
if (Test-Path (Join-Path $repoRoot '.claude/skills')) { return '.claude/skills' }
throw 'No skills directory found (.github/skills or .claude/skills).'
}
#endregion
#region Signal File Helpers
function Write-Signal {
param(
[string]$OutputDir,
[hashtable]$Data
)
$signalPath = Join-Path $OutputDir '.signal'
$Data['timestamp'] = (Get-Date).ToString('o')
$Data | ConvertTo-Json -Depth 5 | Set-Content $signalPath -Force
Info "Signal written: $signalPath"
}
#endregion
#region Build Helpers
function Get-BuildErrorLog {
param([string]$BuildDir)
$errorLogs = Get-ChildItem -Path $BuildDir -Filter 'build.*.errors.log' -Recurse -ErrorAction SilentlyContinue |
Sort-Object LastWriteTime -Descending
if ($errorLogs) {
return $errorLogs[0].FullName
}
return $null
}
#endregion

View File

@@ -0,0 +1,257 @@
<#
.SYNOPSIS
Orchestrate a full community PR review: code review + build verification + verification guide.
.DESCRIPTION
Runs the ReviewCommunityPR agent workflow for a given PR number.
Spawns Copilot CLI or Claude CLI with the review prompt and monitors completion.
.PARAMETER PRNumber
The PR number to review (required).
.PARAMETER CLIType
AI CLI to use: copilot or claude. Default: copilot.
.PARAMETER Model
Model override for Copilot CLI.
.PARAMETER SkipBuild
Skip the build verification phase.
.PARAMETER OutputRoot
Root folder for review outputs. Default: Generated Files/communityPrReview
.PARAMETER Force
Re-review PRs that already have completed reviews.
.PARAMETER DryRun
Show what would be done without executing.
.EXAMPLE
./Start-CommunityPRReview.ps1 -PRNumber 45234
.EXAMPLE
./Start-CommunityPRReview.ps1 -PRNumber 45234 -CLIType claude -SkipBuild
.EXAMPLE
./Start-CommunityPRReview.ps1 -PRNumber 45234 -Force
#>
param(
[int]$PRNumber,
[string]$CLIType = 'copilot',
[string]$Model,
[int]$MaxIterations = 3,
[switch]$SkipBuild,
[string]$OutputRoot = 'Generated Files/communityPrReview',
[string]$LogPath,
[switch]$Force,
[switch]$DryRun,
[switch]$Help
)
$ErrorActionPreference = 'Stop'
if ($Help) {
Get-Help $MyInvocation.MyCommand.Path -Full
return
}
if (-not $PRNumber -or $PRNumber -eq 0) {
Write-Error 'Start-CommunityPRReview: -PRNumber is required.'
return
}
if ($CLIType -notin 'copilot', 'claude') {
Write-Error "Start-CommunityPRReview: Invalid -CLIType '$CLIType'. Must be 'copilot' or 'claude'."
return
}
# Load helpers
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
. "$scriptDir/ReviewLib.ps1"
$repoRoot = Get-RepoRoot
# Resolve config directory name (.github or .claude) from script location
$_cfgDir = if ($PSScriptRoot -match '[\\/](\.github|\.claude)[\\/]') { $Matches[1] } else { '.github' }
# ── Setup logging ────────────────────────────────────────────────────────
if ([string]::IsNullOrWhiteSpace($LogPath)) {
$LogPath = Join-Path (Get-Location) 'Start-CommunityPRReview.log'
}
$logDir = Split-Path -Parent $LogPath
if (-not [string]::IsNullOrWhiteSpace($logDir) -and -not (Test-Path $logDir)) {
New-Item -ItemType Directory -Path $logDir -Force | Out-Null
}
function Write-LogHost {
param(
[Parameter(Position = 0, ValueFromRemainingArguments = $true)]
[object[]]$Object,
[object]$ForegroundColor,
[object]$BackgroundColor,
[switch]$NoNewline,
[Object]$Separator
)
$message = [string]::Join(' ', ($Object | ForEach-Object { [string]$_ }))
"[$(Get-Date -Format o)] $message" | Out-File -FilePath $LogPath -Encoding utf8 -Append
$invokeParams = @{}
if ($PSBoundParameters.ContainsKey('ForegroundColor') -and -not [string]::IsNullOrWhiteSpace([string]$ForegroundColor)) { $invokeParams.ForegroundColor = $ForegroundColor }
if ($PSBoundParameters.ContainsKey('BackgroundColor') -and -not [string]::IsNullOrWhiteSpace([string]$BackgroundColor)) { $invokeParams.BackgroundColor = $BackgroundColor }
if ($NoNewline) { $invokeParams.NoNewline = $true }
if ($PSBoundParameters.ContainsKey('Separator')) { $invokeParams.Separator = $Separator }
Microsoft.PowerShell.Utility\Write-Host @invokeParams -Object $message
}
Set-Alias -Name Write-Host -Value Write-LogHost -Scope Script -Force
# ── Resolve output directory ─────────────────────────────────────────────
$reviewRoot = if ([System.IO.Path]::IsPathRooted($OutputRoot)) {
$OutputRoot
} else {
Join-Path $repoRoot $OutputRoot
}
$reviewDir = Join-Path $reviewRoot "$PRNumber"
# Check for existing review
if (-not $Force -and (Test-Path (Join-Path $reviewDir 'review-comments.md'))) {
Info "PR #$PRNumber already has a completed review at $reviewDir"
Info "Use -Force to re-review."
return
}
if (-not (Test-Path $reviewDir)) {
New-Item -ItemType Directory -Path $reviewDir -Force | Out-Null
}
# ── Prepare prompt ───────────────────────────────────────────────────────
Info "=" * 80
Info "Community PR Review — PR #$PRNumber"
Info "=" * 80
Info "Repository root: $repoRoot"
Info "Output directory: $reviewDir"
Info "CLI type: $CLIType"
Info "Max iterations: $MaxIterations"
Info "Skip build: $SkipBuild"
$reviewPromptPath = Join-Path $repoRoot "$_cfgDir/skills/community-pr-review/references/review-community-pr.prompt.md"
$reviewDirForPrompt = ($reviewDir -replace '\\', '/')
if (Test-Path $reviewPromptPath) {
$rawPrompt = Get-Content $reviewPromptPath -Raw
$rawPrompt = $rawPrompt -replace '\{\{pr_number\}\}', [string]$PRNumber
$rawPrompt = $rawPrompt -replace '\{\{iteration\}\}', '1'
$rawPrompt = $rawPrompt -replace 'Generated Files/communityPrReview/\{\{pr_number\}\}', $reviewDirForPrompt
}
else {
Warn "Review prompt not found at $reviewPromptPath, using inline prompt."
$rawPrompt = @"
Review community bug-fix PR #$PRNumber with a review-fix loop (max $MaxIterations iterations).
1. Fetch PR data and linked issue. Record original head SHA.
2. Checkout and build. If build fails, try merging main.
3. Review-fix loop: review 7 dimensions, fix high/medium issues, re-review until clean.
4. Generate suggested-changes.md with GitHub suggestion blocks from diff.
5. Write build-report.md and verification-guide.md.
6. Write .signal file.
Output to: $reviewDirForPrompt/
"@
}
$skipBuildNote = if ($SkipBuild) { "`n`nIMPORTANT: Skip the build verification phase. Set buildStatus to 'skipped' in the signal file." } else { '' }
$loopNote = "`n`nReview-fix loop: max $MaxIterations iterations. Exit when no high/medium findings remain or max iterations reached."
$prompt = @"
You are running a community PR review workflow for PR #$PRNumber.
Execute the workflow below exactly and write all outputs to $reviewDirForPrompt/.
$skipBuildNote$loopNote
$rawPrompt
"@
$flatPrompt = ($prompt -replace "[\r\n]+", ' ').Trim()
if ($DryRun) {
Info "`nDry run — would execute:"
Info " CLI: $CLIType"
Info " Agent: ReviewCommunityPR"
Info " Output: $reviewDir"
Info "`nPrompt (first 500 chars):"
Info $flatPrompt.Substring(0, [Math]::Min($flatPrompt.Length, 500))
return
}
# Write in-progress signal
Write-Signal -OutputDir $reviewDir -Data @{
status = 'in-progress'
prNumber = $PRNumber
}
# ── Execute CLI ──────────────────────────────────────────────────────────
$logFile = Join-Path $reviewDir "_review.log"
if ($CLIType -eq 'copilot') {
$copilotExe = (Get-Command copilot -ErrorAction SilentlyContinue).Source
if (-not $copilotExe) { $copilotExe = 'copilot' }
$cliArgs = @('-p', $flatPrompt, '--yolo', '--no-custom-instructions', '--agent', 'ReviewCommunityPR')
if ($Model) { $cliArgs += @('--model', $Model) }
Info "`nLaunching Copilot CLI..."
Info "Log file: $logFile"
& $copilotExe @cliArgs 2>&1 | Tee-Object -FilePath $logFile
$exitCode = $LASTEXITCODE
}
else {
$cliArgs = @('-p', $flatPrompt, '--dangerously-skip-permissions', '--agent', 'ReviewCommunityPR')
Info "`nLaunching Claude CLI..."
Info "Log file: $logFile"
& claude @cliArgs 2>&1 | Tee-Object -FilePath $logFile
$exitCode = $LASTEXITCODE
}
# ── Finalize ─────────────────────────────────────────────────────────────
$hasReview = Test-Path (Join-Path $reviewDir 'review-comments.md')
$hasBuild = Test-Path (Join-Path $reviewDir 'build-report.md')
$hasVerification = Test-Path (Join-Path $reviewDir 'verification-guide.md')
$hasSuggestions = Test-Path (Join-Path $reviewDir 'suggested-changes.md')
$hasFixSummary = Test-Path (Join-Path $reviewDir 'fix-summary.md')
$status = if ($hasReview -and ($hasBuild -or $SkipBuild) -and $hasVerification) {
'success'
} elseif ($hasReview) {
'partial'
} else {
'failure'
}
Write-Signal -OutputDir $reviewDir -Data @{
status = $status
prNumber = $PRNumber
exitCode = $exitCode
hasReview = $hasReview
hasBuild = $hasBuild
hasVerification = $hasVerification
hasSuggestions = $hasSuggestions
hasFixSummary = $hasFixSummary
}
Info "`n$("=" * 80)"
Info "COMMUNITY PR REVIEW COMPLETE"
Info "=" * 80
Info "PR: #$PRNumber"
Info "Status: $status"
Info "Review comments: $(if ($hasReview) { 'YES' } else { 'NO' })"
Info "Suggested changes: $(if ($hasSuggestions) { 'YES' } else { 'NO' })"
Info "Fix summary: $(if ($hasFixSummary) { 'YES' } else { 'NO' })"
Info "Build report: $(if ($hasBuild) { 'YES' } else { 'NO' })"
Info "Verification: $(if ($hasVerification) { 'YES' } else { 'NO' })"
Info "Output: $reviewDir"
Info "=" * 80