关于 pr-resolver
这项技能指导处理 GitHub PR:获取审查线程和检查结果、落实反馈并验证处理状态。它要求每个审查线程和每类 CI 检查分别提交,最后统一推送。
主要功能
- 01获取 PR 审查线程和 CI 状态
- 02处理并解决所有审查线程
- 03每个审查线程单独提交
- 04按检查类型修复并提交 CI 问题
- 05重新获取 GitHub 数据以验证完成情况
适用场景
- 01处理 GitHub PR 上的审查意见
- 02合并前解决失败的检查
- 03推送后确认审查线程均已解决
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zenobi-us/ dotfiles --skill pr-resolver安装后在 Claude Code 中输入/pr-resolver确认已加载。
附带文件references/github_api_reference.md
详细介绍
GitHub PR Resolver
通过处理所有评论、修复代码、解决每一个对话线程,并修复所有失败的 CI 检查,自动处理拉取请求审查反馈。
此技能不会跳过任何评论。每个未解决的线程都必须得到处理并解决。
前提条件
# Verify gh CLI is installed and authenticated
gh auth status
# If not authenticated, run:
gh auth login
令牌需要具备 repo 范围,才能完全访问仓库。
工作流概览
- 获取 PR 上下文 → 获取所有审查线程和检查状态(始终从 GitHub 获取最新数据)
- 处理所有未解决的线程 → 对于每个线程:修复 → 提交 → 解决 → 重复
- 修复所有失败的检查 → 处理每一项失败,每种检查类型分别提交
- 推送更改 → 完成所有提交后一次性推送
- 验证 → 从 GitHub 重新获取数据,并确认所有线程均已解决且所有检查均已通过
提交节奏(不可妥协):
- 每个审查线程 = 1 次提交(修复 →
git add→git commit→ 解决线程 → 下一个线程) - 每种 CI 检查类型 = 1 次提交(lint 修复、测试修复和构建修复分别提交)
- 绝不将所有更改合并到最后的一次提交中
关键要求:
- 始终从 GitHub 获取最新数据。 切勿使用缓存或之前获取的上下文。
- 处理每一条未解决的评论。 不要跳过任何线程。每条评论都必须得到处理并解决。
- 未解决线程数为零是唯一可接受的最终状态。
提交规范
每项更改都要使用约定式提交格式单独提交:
<type>(<scope>): <description>
根据评论推断类型:
| 评论模式 | 提交类型 |
|---|---|
| Bug 修复、空值检查、错误处理、验证 | fix |
| 添加、包含、缺少、实现 | feat |
| 重命名、重构、建议、将 X 更改为 Y | refactor |
| 文档、注释、README | docs |
| 性能、优化 | perf |
| 样式、缩进、空白字符 | style |
根据文件路径确定范围:
- 提取目录/模块名称:
src/services/UserService.ts→services - 跳过常见前缀:
src、lib、app - 根目录文件使用文件名:
index.ts→index
CI 检查提交:
- Lint 修复 →
fix(lint): resolve linting errors - 测试修复 →
fix(tests): update failing assertions - 构建/类型错误 →
fix(build): resolve build errors - 格式修复 →
style(format): apply formatting
步骤 1:获取 PR 上下文(始终获取最新数据)
关键:始终从 GitHub 获取最新数据。切勿重复使用之前获取的上下文数据。
1.1 获取 PR 详情
# Get PR metadata
gh pr view <PR_NUMBER> --json number,title,state,headRefName,baseRefName,author,url
1.2 获取审查线程(使用 GraphQL 分页)
使用 GraphQL 获取审查线程及其解决状态。API 每次请求最多返回 100 个项目,因此线程较多的 PR 需要分页。
# First page (no cursor)
gh api graphql -f query='
query($owner: String!, $repo: String!, $prNumber: Int!, $cursor: String) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
reviewThreads(first: 100, after: $cursor) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
isResolved
isOutdated
path
line
comments(first: 100) {
nodes {
id
databaseId
body
author { login }
createdAt
path
line
diffHunk
}
}
}
}
}
}
}' -f owner=OWNER -f repo=REPO -F prNumber=PR_NUMBER
分页处理:
# Check if more pages exist
HAS_NEXT=$(echo "$RESULT" | jq '.data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage')
END_CURSOR=$(echo "$RESULT" | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.endCursor')
# If hasNextPage is true, fetch next page with cursor
if [ "$HAS_NEXT" = "true" ]; then
gh api graphql -f query='...' -f owner=OWNER -f repo=REPO -F prNumber=PR_NUMBER -f cursor="$END_CURSOR"
fi
重要:持续获取后续页面,直到 hasNextPage 为 false。处理前先收集所有线程。
1.3 获取检查状态
# Get all checks for the PR
gh pr checks <PR_NUMBER>
# For detailed check information
gh pr checks <PR_NUMBER> --json name,state,conclusion,description
1.4 解析上下文
获取数据后,识别:
- 未解决的线程:其中
isResolved: false - 失败的检查:其中
conclusion为failure、cancelled、timed_out或action_required
# Count unresolved threads
echo "Unresolved threads: $(echo "$THREADS_JSON" | jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)] | length')"
# List failing checks
gh pr checks <PR_NUMBER> --json name,conclusion | jq '.[] | select(.conclusion == "failure")'
步骤 2:处理所有审查线程
必须处理每个未解决的线程。不要跳过任何评论。
遍历所有未解决的线程,逐一处理:
2.1 理解评论
提取第一条评论(线程发起评论),并理解其中的要求:
# For each unresolved thread, extract:
# - path: file path
# - line: line number
# - body: comment text
# - thread_id: GraphQL ID for resolution
2.2 确定修复类型
所有评论都需要处理。确定合适的修复方式:
| 评论模式 | 所需操作 |
|---|---|
包含 ```suggestion | 直接应用建议的代码 |
| “Typo”“rename”“change X to Y” | 按要求修改具体文本 |
| “Add...”“Include...”“Missing...” | 添加所需代码/内容 |
| “Remove...”“Delete...” | 删除指定代码 |
| “Consider...”“Maybe...”“Nit:” | 应用改进建议(这些仍然是请求) |
| 问题或讨论 | 通过代码修复或回复进行处理,然后解决 |
评论都不可忽略。每个讨论串都必须处理并解决。
2.3 应用代码建议
对于有明确建议的评论,提取并应用:
# Suggestion blocks look like:
# ```suggestion
# replacement code here
# ```
# Extract suggestion content and apply to the file at the specified line
2.4 进行修复
- 在
path处打开文件 - 跳转到
line - 应用所需更改
- 保存文件
2.5 提交修复(立即提交——不要批量提交)
重要:每次修复后立即提交。不要等到最后。不要批量提交。
每次修复后,使用符合约定式提交规范的消息立即提交:
# Stage only the affected file
git add <path>
# Commit with conventional message
git commit -m "<type>(<scope>): <description>"
提交示例:
refactor(services): rename getUser to fetchUserfix(auth): add null check for tokenfeat(api): add retry logic per reviewdocs(utils): add JSDoc for helper function
2.6 解决讨论串
提交后,通过 GraphQL 解决讨论串:
gh api graphql -f query='
mutation($threadId: ID!) {
resolveReviewThread(input: {threadId: $threadId}) {
thread {
id
isResolved
}
}
}' -f threadId="<THREAD_ID>"
对每个未解决的讨论串重复步骤 2.1-2.6。每个讨论串对应 1 次提交。只有在所有讨论串都已分别提交处理后,才能继续步骤 3。
步骤 3:修复未通过的检查
3.1 分析失败原因
# List all failing checks
gh pr checks <PR_NUMBER> --json name,state,conclusion,description | jq '.[] | select(.conclusion == "failure")'
# View workflow run logs for details
gh run view <RUN_ID> --log-failed
3.2 常见检查修复
Lint(eslint、pylint、ruff):
# JavaScript/TypeScript
npm run lint -- --fix
npx eslint . --fix
# Python
ruff check --fix .
black .
类型检查(tsc、mypy、pyright):
- 修复检查输出中显示的类型错误
- 添加缺失的类型注解
- 更新错误的类型
测试(jest、pytest、vitest):
- 阅读测试输出以确定失败原因
- 修复未通过的测试或实现
- 如有需要,更新快照:
npm test -- -u
格式检查(prettier、black):
npx prettier --write .
black .
构建(webpack、vite、tsc):
- 修复语法错误
- 解决导入问题
- 确保依赖项已安装
3.3 分别提交每项检查的修复
修复每种检查的问题后,分别提交:
# After fixing lint errors
git add .
git commit -m "fix(lint): resolve linting errors"
# After fixing test failures
git add .
git commit -m "fix(tests): update failing assertions"
# After fixing build/type errors
git add .
git commit -m "fix(build): resolve build errors"
# After fixing formatting
git add .
git commit -m "style(format): apply formatting"
3.4 在本地重新运行检查
推送前,在本地验证修复:
# Run the same commands CI runs
npm run lint
npm run test
npm run build
步骤 4:推送所有提交
在通过单独提交处理完所有评审讨论串和 CI 检查后,只推送一次:
# Get the branch name
BRANCH=$(gh pr view <PR_NUMBER> --json headRefName -q '.headRefName')
# Push all commits to PR branch
git push origin $BRANCH
这样会为所有更改触发一次 CI 运行,而不是每次提交都触发一次。
步骤 5:验证解决情况(务必重新获取)
推送后,务必从 GitHub 获取最新数据进行验证——切勿依赖之前获取的上下文:
- 所有讨论串均显示为已解决(未解决的讨论串数为 0)
- CI 检查正在重新运行
- 没有引入新的失败
# Re-fetch threads (fresh API call with pagination support)
gh api graphql -f query='
query($owner: String!, $repo: String!, $prNumber: Int!, $cursor: String) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
reviewThreads(first: 100, after: $cursor) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
isResolved
}
}
}
}
}' -f owner=OWNER -f repo=REPO -F prNumber=PR_NUMBER
# Count remaining unresolved (remember to paginate if hasNextPage is true)
UNRESOLVED=$(echo "$RESULT" | jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)] | length')
echo "Remaining unresolved: $UNRESOLVED"
# Check CI status
gh pr checks <PR_NUMBER>
重要:
- 验证必须使用新的 API 调用,以确保看到 GitHub 上的实际当前状态。
- **如果仍有任何未解决的讨论串,任务就尚未完成。**返回并处理这些讨论串。
- 唯一可接受的最终状态是未解决的讨论串数为 0。
完整示例
#!/bin/bash
# Complete workflow to resolve ALL PR feedback
PR_NUMBER=$1
REPO="owner/repo" # Or extract from current git remote
OWNER=${REPO%/*}
REPO_NAME=${REPO#*/}
# 1. Fetch fresh context from GitHub
echo "Fetching PR #$PR_NUMBER..."
PR_INFO=$(gh pr view $PR_NUMBER --json number,title,headRefName,baseRefName)
BRANCH=$(echo $PR_INFO | jq -r '.headRefName')
echo "PR: $(echo $PR_INFO | jq -r '.title')"
echo "Branch: $BRANCH"
# Fetch ALL review threads with pagination
ALL_THREADS="[]"
CURSOR=""
while true; do
if [ -z "$CURSOR" ]; then
CURSOR_ARG=""
else
CURSOR_ARG="-f cursor=$CURSOR"
fi
RESULT=$(gh api graphql -f query='
query($owner: String!, $repo: String!, $prNumber: Int!, $cursor: String) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $prNumber) {
reviewThreads(first: 100, after: $cursor) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
isResolved
path
line
comments(first: 100) {
nodes {
body
author { login }
}
}
}
}
}
}
}' -f owner=$OWNER -f repo=$REPO_NAME -F prNumber=$PR_NUMBER $CURSOR_ARG)
# Append threads from this page
PAGE_THREADS=$(echo $RESULT | jq '.data.repository.pullRequest.reviewThreads.nodes')
ALL_THREADS=$(echo "$ALL_THREADS $PAGE_THREADS" | jq -s 'add')
# Check for next page
HAS_NEXT=$(echo $RESULT | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage')
if [ "$HAS_NEXT" != "true" ]; then
break
fi
CURSOR=$(echo $RESULT | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.endCursor')
done
UNRESOLVED_COUNT=$(echo $ALL_THREADS | jq '[.[] | select(.isResolved == false)] | length')
TOTAL_COUNT=$(echo $ALL_THREADS | jq 'length')
echo "Total threads: $TOTAL_COUNT"
echo "Unresolved threads: $UNRESOLVED_COUNT"
# 2. Checkout PR branch
git fetch origin $BRANCH
git checkout $BRANCH
# 3. Process EACH unresolved thread (Claude does this interactively)
# For each thread:
# - Read the comment
# - Make the fix
# - git add <file>
# - git commit -m "<type>(<scope>): <description>"
# - gh api graphql ... resolve mutation
# - Move to next thread
# 4. Fix failing checks (one commit per check type)
gh pr checks $PR_NUMBER --json name,conclusion | jq '.[] | select(.conclusion == "failure")'
# Fix each failing check type and commit separately
# 5. Push all commits
git push origin $BRANCH
# 6. Verify - re-fetch ALL pages and confirm zero unresolved
# (Use same pagination loop as above)
if [ "$REMAINING" -gt 0 ]; then
echo "⚠️ WARNING: $REMAINING threads still unresolved!"
echo "Must go back and process remaining threads."
else
echo "✅ All threads resolved!"
fi
参考
有关以下内容,请参阅 references/github_api_reference.md:
- 评论意图模式详解
- CI 检查类型和常见修复
- API 速率限制和错误处理
- Git 工作流最佳实践