| name | pr-fixup |
| description | Wait for ALL CI checks and PR review to complete, fix CI failures and review issues, loop until PR is clean |
| argument-hint | [PR number, default: current branch's PR] |
PR Fixup: CI + Review → Fix → Re-run Loop
等待所有 CI checks 和 PR review 完成,修复 CI 构建/测试失败和 review 评论问题,循环直到 PR 全部通过。
前置信息收集
- 获取仓库信息:
gh repo view --json nameWithOwner -q .nameWithOwner → 得到 OWNER/REPO,再拆分出 OWNER 和 REPO
- 确定 PR 号:
- 如果
$ARGUMENTS 提供了 PR 号或 URL(https://github.com/.../pull/N),提取编号使用
- 否则:
gh pr view --json number -q .number 自动检测当前分支 PR
- 如果没有 PR,告知用户并停止
- 获取当前分支:
git branch --show-current
- 读取 PR 信息:
gh pr view PR_NUMBER 了解 PR 意图
主循环
重复以下步骤,直到所有 CI checks 通过且 review 问题解决。最多 5 轮,超过后提醒用户手动介入。
Step 0: 检查 Merge 冲突
每轮开始前先检查 PR 是否有 merge 冲突:
gh pr view PR_NUMBER --json mergeable,mergeStateStatus -q '{mergeable: .mergeable, state: .mergeStateStatus}'
判断逻辑(需同时检查两个字段):
| 条件 | 处理 |
|---|
mergeable=MERGEABLE 且 state=CLEAN | 无冲突且最新,继续下一步 |
mergeable=MERGEABLE 且 state=BEHIND | 分支落后 base,需要 rebase 更新 |
mergeable=CONFLICTING | 有冲突,尝试自动 rebase |
mergeable=UNKNOWN | GitHub 还在计算,等待 10 秒后重查(最多 3 次) |
重要:BEHIND 状态表示分支可以合并但落后于 base branch。如果仓库有 "require branches to be up to date" 规则,必须 rebase 才能合并。即使没有此规则,也建议 rebase 以确保 CI 基于最新代码运行。
自动 rebase 流程:
- 确定 base branch:
gh pr view PR_NUMBER --json baseRefName -q .baseRefName
- 执行 rebase:
git fetch origin BASE_BRANCH
git rebase origin/BASE_BRANCH
- 如果 rebase 成功(无冲突):
git push --force-with-lease 更新 PR
- 输出 "🔄 已自动 rebase 解决冲突,等待 CI 重新运行..."
- 回到 Step 1
- 如果 rebase 失败(有无法自动解决的冲突):
git rebase --abort 取消
- 输出冲突文件列表,提示用户手动解决
- 停止循环,不要尝试自动合并冲突
Step 1: 等待所有 CI checks 完成
轮询 PR 的所有 status checks:
gh pr checks PR_NUMBER --json name,state,description,link
注意: 如果 gh pr checks 不支持 --json,改用:
gh pr checks PR_NUMBER
输出格式为 NAME\tSTATUS\tDURATION\tLINK,其中 STATUS 为 pass/fail/pending。
轮询逻辑:
- 如果有任何 check 状态为
pending(或 in_progress),等待 60 秒后重试,最多等待 20 分钟
- 当所有 checks 都完成后(无 pending),进入下一步判断
重要:轮询时不要使用 sleep 命令等待,改用 Bash 的 timeout 参数设置超时:
sleep 60
for i in $(seq 1 20); do
result=$(gh pr checks PR_NUMBER 2>&1)
if echo "$result" | grep -q "pending"; then
echo "Attempt $i: still pending, waiting 60s..."
for j in $(seq 1 6); do sleep 10 && echo " ...waiting ($((j*10))s)"; done
else
echo "$result"
break
fi
done
Step 2: 检查 CI 失败
检查 Step 1 的结果,将 checks 分为三类:
| 类别 | 处理方式 |
|---|
| CI 构建/测试失败 (如 build, test, lint, typecheck) | 获取失败日志 → 修复代码 |
| Review 失败 (如 review, pr-review) | 进入 Step 3 处理评论 |
| 全部通过 | 进入 Step 3 检查评论(可能有 review 评论但 check 显示 pass) |
对于 CI 构建/测试失败:
- 识别失败的 workflow run:
gh run list -b BRANCH -L 5 --json databaseId,name,conclusion,headSha,workflowName
找到 conclusion 为 "failure" 且 headSha 匹配最新 commit 的 run。
- 获取失败日志:
gh run view RUN_ID --log-failed 2>&1 | tail -100
如果日志太长,取最后 100 行,重点关注 error/Error/FAILED 等关键行。
-
分析日志,定位失败原因(编译错误、测试失败、lint 问题等)。
-
修复代码 — 根据错误日志修复问题,使用最小改动。
-
如果是环境/平台问题(如 macOS-only 依赖在 Linux CI 上不可用、系统库缺失等非代码问题),无法通过修改代码解决,向用户说明情况并建议:
- 修改 CI 配置跳过该平台
- 添加条件编译/构建
- 或手动处理
-
git add 修改的文件,不要立即 commit — 在 Step 5 统一处理。
Step 3: 获取未解决的 Review 评论
Review 反馈分布在三个地方,必须都检查,不能只看 inline review threads:
- Inline review threads — reviewer 在具体代码行上的评论(GraphQL
reviewThreads)
- PR 顶层 issue comments — review bot 经常把发现的问题汇总成一条整体评论发到 PR 主时间线(
gh pr view --json comments)
- PR description — 部分 review bot 会把 summary 写进 PR description 而非 comment
先获取 PR 作者、body、head SHA,以及最后一次推送对应的 commit 时间:
gh pr view PR_NUMBER --json author,body,headRefOid -q '{author: .author.login, body: .body, sha: .headRefOid}'
HEAD_SHA=$(gh pr view PR_NUMBER --json headRefOid -q .headRefOid)
LAST_PUSH=$(gh api repos/OWNER/REPO/commits/$HEAD_SHA --jq .commit.committer.date)
3a. Inline review threads(GraphQL):
gh api graphql -f query='{
repository(owner:"OWNER", name:"REPO") {
pullRequest(number:PR_NUMBER) {
reviewThreads(first:100) {
nodes {
id
isResolved
comments(first:10) {
nodes {
databaseId
body
author { login }
path
line
createdAt
}
}
}
}
}
}
}'
过滤条件:
isResolved == false(未解决)
- 第一条 comment 的
author.login 不是 PR 作者
3b. PR 顶层 issue comments:
gh pr view PR_NUMBER --json comments -q '.comments[] | select(.author.login != "PR_AUTHOR") | {id, body, author: .author.login, createdAt}'
过滤条件:
author.login 不是 PR 作者
createdAt 在最后一次 push 之后(处理新增反馈,忽略已被旧 commit 处理的历史评论)
3c. PR description:
检查 PR body 中是否包含 review summary。判定标准(避免把普通 PR 说明误判为 review):
- 强信号(出现任一即可判定):
## Review Summary、### Issues Found、## Review Notes、Suggested Action
- 弱信号(需同时出现 ≥2 个才算):
🟡、🔴、nit、confidence、severity
满足上述任一规则的,把 review summary 段落里的条目当作待处理 review feedback,与 3a/3b 一起进入 Step 4。
去重(重要):3a 靠 isResolved 去重、3b 靠 createdAt > LAST_PUSH 去重,但 PR description 是静态的,Claude 修完代码 push 后 description 内容并不会变。为避免同一 /pr-fixup 调用内同一条 3c 条目被反复处理,必须做以下两件事之一:
- 本轮内存记录:在当前
/pr-fixup 执行流程中维护一个集合(如条目正文的前 50 字符 hash),处理过的 3c 条目下一轮直接跳过
- 镜像到顶层 comment:处理完 3c 条目后调用
gh pr comment 写一条 "Addressed (3c): <条目摘要>" 到 PR 主时间线,让后续轮次靠 3b 的 LAST_PUSH 过滤自动跳过
推荐第一种(更便宜,不污染 PR 时间线)。
如果没有 CI 失败(Step 2 已全部通过)且 3a/3b/3c 都没有未处理的反馈 → 输出 "✅ 所有 CI checks 通过,PR review 无阻塞问题" 并结束循环。
Step 4: 分析并处理 Review 评论
对于每个未解决的评论(来自 3a inline、3b issue comment、3c PR description summary):
- 读取相关源文件:
- inline 评论:用 Read 工具读取评论所在的
path 文件
- issue comment / PR description summary:从 body 中解析出涉及的文件路径(通常是
src/foo.ts:123 格式),逐个 Read
- 理解评论内容:仔细阅读
body 中指出的具体问题
- 结合上下文判断:评论是否正确?
分类标准:
| 分类 | 条件 | 举例 |
|---|
| 真实问题 | 代码确实存在 reviewer 描述的缺陷 | 逻辑错误、安全漏洞、资源泄漏、类型不安全 |
| 误报 | 代码是正确的,reviewer 的分析有误 | 忽略了上下文、误解了控制流、不了解框架行为、过度保守 |
判断原则:
- 如果你不确定,倾向于修复而不是反驳——宁可多修一个不必要的问题,也不要放过一个真实 bug
- 反驳误报时必须有明确的理由,能指出 reviewer 具体哪里判断错了
对于真实问题:
- 修复代码,使用最小改动,不做不相关的重构
git add 修改的文件
- 回复评论确认修复:
gh api repos/OWNER/REPO/pulls/PR_NUMBER/comments/COMMENT_DATABASE_ID/replies \
-f body="Fixed — <简述修改内容>"
gh pr comment PR_NUMBER --body "Fixed — <简述修改内容>(回复 [评论](https://github.com/OWNER/REPO/pull/PR_NUMBER#issuecomment-ISSUE_COMMENT_ID))"
gh pr comment PR_NUMBER --body "Addressed — <简述修改内容>"
- Resolve 该 thread(仅 inline review thread 适用,issue comment 和 description summary 无 thread 可 resolve):
gh api graphql -f query='mutation {
resolveReviewThread(input:{threadId:"THREAD_NODE_ID"}) {
thread { isResolved }
}
}'
对于误报:
- 回复评论说明原因:
gh api repos/OWNER/REPO/pulls/PR_NUMBER/comments/COMMENT_DATABASE_ID/replies \
-f body="Not an issue — <具体解释,引用代码说明 reviewer 的判断为什么不适用于此场景>"
gh pr comment PR_NUMBER --body "Not an issue — <具体解释>"
- Resolve 该 thread(仅 inline review thread 适用):
gh api graphql -f query='mutation {
resolveReviewThread(input:{threadId:"THREAD_NODE_ID"}) {
thread { isResolved }
}
}'
Step 5: 提交推送或结束
统计本轮处理结果(CI 修复数 + review 修复数 + 误报反驳数)。
如果有代码修复(CI 修复或 review 修复):
git commit,message 遵循项目风格,如:
fix: 修复 CI 构建错误 (CI 问题)
fix: address PR review feedback (review 问题)
fix: 修复 CI 构建错误并处理 review 反馈 (两者都有)
git push
- 输出 "🔄 第 N 轮:修复 X 个 CI 问题 + Y 个 review 问题,反驳 Z 个误报,等待新一轮 checks..."
- 回到 Step 1
如果本轮所有 review 反馈都已处理(inline 已 reply+resolve、3b/3c 已 reply)且无代码修复且 CI 全部通过:
- 输出 "✅ 第 N 轮:处理 Y 个 review 反馈(含 Z 个反驳),所有 CI checks 通过"
- 结束循环
注意:3b/3c 没有 thread 可以 resolve,"已处理"的标准是已经发出 gh pr comment 回复。不要因为"没有 resolve 动作"就误判为未处理而陷入死循环。
完成汇总
循环结束时,输出汇总报告:
## 📋 PR Fixup 完成
- **总轮数**: N
- **CI 修复**: X 个
- **Review 修复**: Y 个
- **反驳误报**: Z 个
- **PR 状态**: ✅ 所有 checks 通过,无阻塞问题
注意事项
- 处理所有非 PR 作者的未解决评论(包括 bot 和人类 reviewer)
- 反驳评论时给出具体、有理据的解释,引用代码上下文,不要笼统地说"这没问题"
- commit message 遵循项目风格:
fix: <中文描述>
- 如果同一个问题反复出现(修了又被报),在第 3 轮后停下来让用户介入
- 不要使用裸
sleep 命令 — 长时间 sleep 会导致 SDK 空闲超时。轮询等待时在循环中保持输出活跃
- 如果 CI 失败是环境/平台问题(非代码可修复),明确告知用户而不是反复重试