mcpField
P
pr-resolver

pr-resolver

github.com/zenobi-us测试与质量6835 次安装今天更新
代码仓库

关于 pr-resolver

这项技能指导处理 GitHub PR:获取审查线程和检查结果、落实反馈并验证处理状态。它要求每个审查线程和每类 CI 检查分别提交,最后统一推送。

主要功能

  1. 01获取 PR 审查线程和 CI 状态
  2. 02处理并解决所有审查线程
  3. 03每个审查线程单独提交
  4. 04按检查类型修复并提交 CI 问题
  5. 05重新获取 GitHub 数据以验证完成情况

适用场景

  1. 01处理 GitHub PR 上的审查意见
  2. 02合并前解决失败的检查
  3. 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 范围,才能完全访问仓库。

工作流概览

  1. 获取 PR 上下文 → 获取所有审查线程和检查状态(始终从 GitHub 获取最新数据)
  2. 处理所有未解决的线程 → 对于每个线程:修复 → 提交 → 解决 → 重复
  3. 修复所有失败的检查 → 处理每一项失败,每种检查类型分别提交
  4. 推送更改 → 完成所有提交后一次性推送
  5. 验证 → 从 GitHub 重新获取数据,并确认所有线程均已解决且所有检查均已通过

提交节奏(不可妥协):

  • 每个审查线程 = 1 次提交(修复 → git add → git commit → 解决线程 → 下一个线程)
  • 每种 CI 检查类型 = 1 次提交(lint 修复、测试修复和构建修复分别提交)
  • 绝不将所有更改合并到最后的一次提交中

关键要求:

  • 始终从 GitHub 获取最新数据。 切勿使用缓存或之前获取的上下文。
  • 处理每一条未解决的评论。 不要跳过任何线程。每条评论都必须得到处理并解决。
  • 未解决线程数为零是唯一可接受的最终状态。

提交规范

每项更改都要使用约定式提交格式单独提交:

<type>(<scope>): <description>

根据评论推断类型:

评论模式提交类型
Bug 修复、空值检查、错误处理、验证fix
添加、包含、缺少、实现feat
重命名、重构、建议、将 X 更改为 Yrefactor
文档、注释、READMEdocs
性能、优化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 进行修复
  1. 在 path 处打开文件
  2. 跳转到 line
  3. 应用所需更改
  4. 保存文件
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 fetchUser
  • fix(auth): add null check for token
  • feat(api): add retry logic per review
  • docs(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 获取最新数据进行验证——切勿依赖之前获取的上下文:

  1. 所有讨论串均显示为已解决(未解决的讨论串数为 0)
  2. CI 检查正在重新运行
  3. 没有引入新的失败
# 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 工作流最佳实践

评论

支持 Markdown