Loading...
Loading...
This skill processes unresolved GitHub PR review discussions. Activated when the user provides a GitHub PR link (github.com/.../pull/...) or mentions "PR review", "review PR", "process review comments", etc.
npx skill4agent add pitzcarraldo/skills pr-review| Action Type | When Used | Outcome |
|---|---|---|
| Create Todo | Valid feedback needing fixes | Adds task to todo list |
| Reply & Resolve | Invalid or misunderstood feedback | Posts clarifying comment, resolves thread |
| Confirm & Resolve | Already fixed feedback | Posts confirmation, resolves thread |
| Escalate | Ambiguous or complex feedback | Asks user for guidance |
gh --versiongh version X.Y.Z (YYYY-MM-DD)GitHub CLI (gh) is not installed.
Installation:
macOS: brew install gh
Ubuntu/Debian: sudo apt install gh
Windows: winget install GitHub.cli
Linux (other): See https://github.com/cli/cli#installation
After installation, authenticate:
gh auth logingh auth statusPlease authenticate with GitHub:
gh auth login
Follow the prompts to complete authentication.https://github.com/{owner}/{repo}/pull/{number}url="$ARGUMENTS"
owner=$(echo "$url" | sed -n 's#.*/github.com/\([^/]*\)/.*#\1#p')
repo=$(echo "$url" | sed -n 's#.*/github.com/[^/]*/\([^/]*\)/.*#\1#p')
pr_number=$(echo "$url" | sed -n 's#.*/pull/\([0-9]*\).*#\1#p')Input: https://github.com/anthropics/claude-code/pull/123
Output: owner=anthropics, repo=claude-code, pr_number=123Error: Invalid GitHub PR URL
Expected format: https://github.com/OWNER/REPO/pull/NUMBER
Example: https://github.com/anthropics/claude-code/pull/123gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
title
author { login }
reviewThreads(first: 100) {
nodes {
id
isResolved
isOutdated
path
line
comments(first: 10) {
nodes {
id
body
author { login }
createdAt
}
}
}
}
}
}
}
' -f owner="$owner" -f repo="$repo" -F pr="$pr_number"{
"data": {
"repository": {
"pullRequest": {
"reviewThreads": {
"nodes": [
{
"id": "PRRT_...",
"isResolved": false,
"isOutdated": false,
"path": "src/auth.ts",
"line": 45,
"comments": {
"nodes": [
{
"body": "This needs null checking",
"author": {"login": "reviewer"},
"createdAt": "2024-01-01T12:00:00Z"
}
]
}
}
]
}
}
}
}
}isResolved: falseisOutdated: true✓ All review discussions have been resolved
PR: [PR Title]
Status: No pending feedback# Read the file with context around the line
Read file_path="$path"gh pr diff "$pr_number" -R "$owner/$repo"# Add to todo list using TodoWrite
TodoWrite:
- content: "Fix [issue] in [file]:[line]"
- status: "pending"Discussion: "Missing null check for user.profile"
Current code: No null check present
Category: Valid Feedback
Action: Added todo "Add null check for user.profile in auth.ts:45"# Post clarifying comment
gh api graphql -f query='
mutation($threadId: ID!, $body: String!) {
addPullRequestReviewThreadReply(input: {
pullRequestReviewThreadId: $threadId
body: $body
}) {
comment { id }
}
}
' -f threadId="$thread_id" -f body="[polite clarification]"
# Resolve the thread
gh api graphql -f query='
mutation($threadId: ID!) {
resolveReviewThread(input: {threadId: $threadId}) {
thread { isResolved }
}
}
' -f threadId="$thread_id"Thank you for the feedback! This is actually already handled:
[Explanation with code reference]
The [specific mechanism] ensures [desired behavior].
Reference: [file]:[line]Discussion: "Variable 'token' is unused"
Current code: Variable used on line 67
Category: Invalid Feedback
Reply: "Thank you for checking! The 'token' variable is actually used
on line 67 in the authentication middleware. Here's the usage:
```typescript
if (token) {
return validateToken(token);
}
```"
Action: Resolved thread# Post confirmation comment
gh api graphql -f query='[same mutation as Type B]' \
-f threadId="$thread_id" \
-f body="This has been addressed in commit [hash]. [Brief explanation]"
# Resolve thread
gh api graphql -f query='[same resolve mutation]' -f threadId="$thread_id"This has been addressed in commit [short-hash].
Changes made:
[Brief summary of the fix]
Current implementation:
[Code snippet if helpful]Discussion: "Add TypeScript types for User interface"
Current code: User interface fully typed
Category: Already Addressed
Reply: "This has been addressed in commit abc123f. Added comprehensive
TypeScript types for the User interface including optional fields."
Action: Resolved thread# Ask user for guidance using AskUserQuestion
AskUserQuestion:
question: "[Reviewer] suggests [approach]. How would you like to proceed?"
options:
- Implement suggested approach
- Explain current approach to reviewer
- Discuss alternative solution━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PR Review Processing Complete
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PR: [PR Title] (#[number])
Repository: [owner]/[repo]
Summary:
Total discussions: [X]
Valid feedback: [Y] → Added to todo list
Invalid feedback: [Z] → Clarified and resolved
Already addressed: [W] → Confirmed and resolved
Escalated: [N] → Requires user input
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Action Items Created:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1. [file]:[line] - [description]
2. [file]:[line] - [description]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Resolved Discussions:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ [file]:[line] - [reason for resolution]
✓ [file]:[line] - [reason for resolution]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━# 변경사항 커밋
git add .
git commit -m "fix: address PR review feedback"
# Push
git push# Check 상태 모니터링 (완료될 때까지 polling)
gh pr checks "$pr_number" -R "$owner/$repo" --watchgh pr checks "$pr_number" -R "$owner/$repo"Some checks are still pending
0 failing, 1 pending, 0 passing, and 0 skipped checks
build In progress https://github.com/...--watch# 3단계의 GraphQL 쿼리 재실행
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
isOutdated
path
line
comments(first: 10) {
nodes {
id
body
author { login }
createdAt
}
}
}
}
}
}
}
' -f owner="$owner" -f repo="$repo" -F pr="$pr_number"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ PR Review 처리 완료
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PR: [PR Title] (#[number])
Repository: [owner]/[repo]
처리 사이클: [N]회
총 처리 discussion: [X]개
CI Check: ✓ 모두 통과
모든 review feedback이 처리되었습니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┌─────────────────────────────────────────────────────────┐
│ 시작 │
└────────────────────────┬────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ Unresolved discussion 조회 │
└────────────────────────┬────────────────────────────────┘
▼
┌─────────────────────┐
│ discussion 있음? │
└──────────┬──────────┘
│ │
Yes No
│ │
▼ ▼
┌──────────────────────┐ ┌─────────────────────────┐
│ 분석 및 분류 (4단계) │ │ ✓ 처리 완료 │
└──────────┬───────────┘ │ 루프 종료 │
▼ └─────────────────────────┘
┌──────────────────────┐
│ 처리 및 응답 (5단계) │
│ - Valid: 수정 │
│ - Invalid: resolve │
│ - Addressed: resolve │
└──────────┬───────────┘
▼
┌───────────┐
│ 수정 있음? │
└─────┬─────┘
Yes │
▼
┌──────────────────────┐
│ git commit && push │
└──────────┬───────────┘
▼
┌──────────────────────┐
│ CI Check 대기 │
│ gh pr checks --watch │
└──────────┬───────────┘
▼
│ (처음으로 돌아감)
└────────────────────────────────────────┐
│
┌─────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ Unresolved discussion 재조회 │
└─────────────────────────────────────────────────────────┘src/auth.ts:45
"This function should handle null user profiles"# Read file
Read src/auth.ts
# Check lines 35-55
function getProfile(user) {
return user.profile.name; // Line 45 - no null check
}Category: Valid Feedback
Action: Added todo "Add null check for user.profile in auth.ts:45"src/api.ts:120
"Variable 'cache' is declared but never used"# Read file
Read src/api.ts
# Check lines 110-140
const cache = new Map(); // Line 120
function getData(key) {
if (cache.has(key)) { // Line 130 - using cache
return cache.get(key);
}
// ...
}Category: Invalid Feedback
Reply posted:
"Thank you for reviewing! The 'cache' variable is actually used in the
getData function on lines 130-132 for memoization. Here's the usage:
```typescript
if (cache.has(key)) {
return cache.get(key);
}
### Example 3: Already Fixed Type Issue
**Review comment:**
**Analysis:**
```bash
# Check current code
Read src/types.ts
# Line 15 now has type annotation
function calculate(): number { // Line 15 - type added
return 42;
}
# Check git log
git log --oneline --since="[review date]" src/types.ts
# Shows: "a1b2c3d feat(types): add return type annotations"Category: Already Addressed
Reply posted:
"This has been addressed in commit a1b2c3d. Added return type annotations
to all exported functions including this one.
Current implementation:
```typescript
function calculate(): number {
return 42;
}
```"
Action: Resolved thread| Tool | Purpose | Installation | Check Command |
|---|---|---|---|
| GitHub CLI | API access | | |
| Git | Local repository | Built-in or package manager | |
| jq | JSON parsing | | |
brew install ghgh auth logingh auth refresh -s repogh api rate_limit# Process in batches of 10
for i in {0..9}; do
# Process discussion $i
# Add delay to avoid rate limiting
sleep 1
doneDraft response for [file]:[line]:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Response text]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Post this response? [y/n]# Filter threads by comment author
jq '.data.repository.pullRequest.reviewThreads.nodes[] |
select(.comments.nodes[0].author.login == "specific-reviewer")'