Loading...
Loading...
Reviews PR comments from GitHub (Copilot, reviewers), evaluates against actual code, replies with reasoning, and resolves threads. Triggers on "review pr comments", "address pr feedback", "fix pr comments", or "review copilot suggestions".
npx skill4agent add richtabor/agent-skills review-prgh pr list --head $(git branch --show-current) --json number,title --jq '.[0]'gh api repos/{owner}/{repo}/pulls/{number}/comments --jq '.[] | {id: .id, path: .path, line: (.line // .original_line), body: .body, in_reply_to_id: .in_reply_to_id}'in_reply_to_id| Category | Criteria | Response |
|---|---|---|
| Already addressed | Issue was fixed in a subsequent commit, or reviewer misread the code | Explain what the code actually does |
| Fix | Valid bug, security issue, or typo that exists in current code | Implement the fix |
| Won't fix | Over-engineering, style preference, feature request, or would break consistency | Explain reasoning |
## PR #[number]: [title]
Found [X] review comments. Here's my assessment:
---
### 1. `path/file.ts:42` — **Already addressed**
> [comment body]
**Issue**: Reviewer concerned about X
**Actual code**: Lines 26-34 already handle this — [brief explanation]
---
### 2. `path/other.ts:15` — **Won't fix**
> [comment body]
**Issue**: Reviewer wants X
**Why skip**: [Over-engineering / Matches existing pattern / Feature request / etc]
---
### 3. `path/file.ts:88` — **Fix**
> [comment body]
**Issue**: Typo in user-facing string
**Proposed change**: Change "Jumpope" to "Jump Rope"
---
## Summary
- **Already addressed**: 6 comments
- **Won't fix**: 8 comments
- **Fix**: 2 commentsgh api repos/{owner}/{repo}/pulls/{pr_number}/comments/{comment_id}/replies \
-X POST \
-f body="This is already handled — lines 26-34 fetch the authenticated user and verify user.id === state before proceeding. If they don't match, it rejects with a CSRF error."gh api repos/{owner}/{repo}/pulls/{pr_number}/comments/{comment_id}/replies \
-X POST \
-f body="Won't fix — this matches the exact pattern used for the other integrations in the same file. Changing just this one would be inconsistent."gh api repos/{owner}/{repo}/pulls/{pr_number}/comments/{comment_id}/replies \
-X POST \
-f body="Fixed in abc1234"cat << 'QUERY' | gh api graphql --input - --jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | {threadId: .id, commentId: .comments.nodes[0].databaseId}'
{"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 { databaseId } } } } } } }", "variables": {"owner": "OWNER", "repo": "REPO", "pr": 123}}
QUERYcat << 'QUERY' | gh api graphql --input -
{"query": "mutation { resolveReviewThread(input: {threadId: \"THREAD_ID_HERE\"}) { thread { isResolved } } }"}
QUERY## Review Complete
All 16 conversations resolved.
### Already addressed (6)
| Comment | Explanation |
|---------|-------------|
| State validation | Already validates user.id === state at lines 26-34 |
| HR zones docs | Comments already say "milliseconds", not "percentage" |
### Won't fix (8)
| Comment | Reason |
|---------|--------|
| Form semantics | Matches existing pattern for other integrations |
| Rate limiting | Self-heals on next cron run, over-engineering |
### Fixed (2)
| Commit | Fix |
|--------|-----|
| `abc123` | Fixed typo "Jumpope" → "Jump Rope" |"This is already handled — [specific location] does [what it does]. [Brief explanation of why it's correct]."
| Reason | Response template |
|---|---|
| Matches existing pattern | "Won't fix — this matches the exact pattern of X, Y, and Z in the same file. Changing just this would be inconsistent." |
| Over-engineering | "Won't fix — [the failure mode] will fail obviously / self-heal on retry / is handled by [existing mechanism]." |
| Feature request | "Won't fix — this is a feature request rather than a bug. The current flow works: [what it does]. Out of scope for this PR." |
| Product decision | "Won't fix — this is a product decision, not a bug. [Current behavior] is intentional because [reason]." |
| Would break things | "Won't fix — this would break [existing behavior / consistency with X]." |
// "Refresh if expires in less than 5 minutes"
if (expiry.getTime() - Date.now() > 5 * 60 * 1000) {
return integration.access_token!; // Token is fresh, return early
}
// Proceed to refresh...