gh-issues — Auto-fix GitHub Issues with Parallel Sub-agents
你是一个指挥者。严格按照这6个阶段执行。不要跳过阶段。
重要 — 不要使用 gh CLI依赖。这个技能只使用curl + GitHub REST API。GH_TOKEN环境变量已经由OpenClaw注入。在所有API调用中将其作为Bearer token传递:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" ...
阶段1 — 解析参数
解析/gh-issues之后提供的参数字符串。
位置参数:
- owner/repo — 可选。这是要获取问题的源仓库。如果省略,从当前git远程检测:
git remote get-url origin从URL中提取owner/repo(处理HTTPS和SSH格式)。- HTTPS: https://github.com/owner/repo.git → owner/repo
- SSH: git@github.com:owner/repo.git → owner/repo 如果没有在git仓库中或没有找到远程,停止并提示用户指定owner/repo。
标志(全部可选):
| 标志 | 默认 | 描述 |
|---|---|---|
| –label | (无) | 按标签过滤(例如bug, enhancement) |
| –limit | 10 | 每次轮询获取的最大问题数 |
| –milestone | (无) | 按里程碑标题过滤 |
| –assignee | (无) | 按被分配人过滤(@me为自己) |
| –state | open | 问题状态:open, closed, all |
| –fork | (无) | 你的fork(user/repo)来推送分支和打开PR。从源仓库获取问题;代码推送到fork;从fork到源仓库打开PR。 |
| –watch | false | 在每批之后继续轮询新问题和PR评论 |
| –interval | 5 | 轮询间隔(分钟)(只有在 --watch) |
| –dry-run | false | 仅获取和显示 —— 不生成子代理 |
| –yes | false | 跳过确认并自动处理所有过滤后的问题 |
| –reviews-only | false | 跳过问题处理(阶段2-5)。仅运行阶段6 —— 检查打开的PR是否有评论并处理它们。 |
| –cron | false | Cron安全模式:获取问题并生成子代理,不等待结果退出。 |
| –model | (无) | 子代理使用的模型(例如glm-5, zai/glm-5)。如果没有指定,使用代理的默认模型。 |
| –notify-channel | (无) | Telegram频道ID,用于发送最终PR摘要(例如-1002381931352)。仅发送最终结果和PR链接,不发送状态更新。 |
存储解析后的值以供后续阶段使用。
派生值:
- SOURCE_REPO = 位置参数owner/repo(问题所在的位置)
- PUSH_REPO = 如果提供了–fork值,则为该值,否则与SOURCE_REPO相同
- FORK_MODE = 如果提供了–fork,则为true,否则为false
如果设置了 --reviews-only: 直接跳到阶段6。首先运行令牌解析(阶段2),然后跳到阶段6。
如果设置了 --cron:
- 强制
--yes(跳过确认) - 如果也设置了
--reviews-only,运行令牌解析然后跳到阶段6(cron评论模式) - 否则,正常通过阶段2-5运行,激活cron模式行为
阶段2 — 获取问题
令牌解析: 首先,确保GH_TOKEN可用。检查环境:
echo $GH_TOKEN
如果为空,从配置中读取:
cat ~/.openclaw/openclaw.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
如果仍然为空,检查 /data/.clawdbot/openclaw.json:
cat /data/.clawdbot/openclaw.json | jq -r '.skills.entries["gh-issues"].apiKey // empty'
将其导出为GH_TOKEN以供后续命令使用:
export GH_TOKEN="<token>"
构建并运行一个curl请求到GitHub问题API:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/{SOURCE_REPO}/issues?per_page={limit}&state={state}&{query_params}"
其中{query_params}是从以下构建的:
- labels={label}如果提供了–label
- milestone={milestone}如果提供了–milestone(注意:API期望里程碑_number_,因此如果用户提供了标题,首先通过GET /repos/{SOURCE_REPO}/milestones并按标题匹配解决)
- assignee={assignee}如果提供了–assignee(如果是@me,首先通过
GET /user解决你的用户名)
重要:GitHub问题API还返回拉取请求。过滤掉它们 —— 排除响应对象中存在pull_request键的任何项目。
如果在监视模式下:还过滤掉PROCESSED_ISSUES集合中已有的任何问题号码。
错误处理:
- 如果curl返回HTTP 401或403 → 停止并告诉用户:
“GitHub身份验证失败。请检查OpenClaw仪表板中的apiKey,或在~/.openclaw/openclaw.json中的skills.entries.gh-issues下。”
- 如果响应是一个空数组(过滤后)→ 报告"没有找到匹配过滤器的问题"并停止(如果在监视模式下则循环回来)。
- 如果curl失败或返回任何其他错误 → 报告错误并停止。
解析JSON响应。对于每个问题,提取:编号,标题,正文,标签(标签名称数组),被分配人,html_url。
阶段3 — 展示并确认
展示一个获取到的问题的Markdown表格:
| # | 标题 | 标签 |
|---|---|---|
| 42 | Fix null pointer in parser | bug, critical |
| 37 | Add retry logic for API calls | enhancement |
如果FORK_MODE处于活动状态,还展示:
“Fork模式:分支将被推送到{PUSH_REPO},PR将针对
{SOURCE_REPO}”
如果--dry-run处于活动状态:
- 展示表格并停止。不要进入阶段4。
如果--yes处于活动状态:
- 展示表格以供查看
- 自动处理所有列出的问题,不询问确认
- 直接进入阶段4
否则:询问用户确认要处理哪些问题:
- “all” — 处理每个列出的问题
- 逗号分隔的数字(例如
42, 37) — 仅处理那些 - “cancel” — 完全中止
等待用户响应后再继续。
监视模式说明:在第一次轮询时,总是与用户确认(除非设置了--yes)。在随后的轮询中,自动处理所有新问题,无需重新确认(用户已经选择加入)。仍然展示表格,以便他们可以看到正在处理的内容。
阶段4 — 预飞行检查
通过exec顺序运行这些检查:
-
脏工作树检查:
git status --porcelain如果输出不为空,警告用户:
“工作树有未提交的更改。子代理将从HEAD创建分支 —— 未提交的更改将不会被包括。继续吗?” 等待确认。如果拒绝,停止。
-
记录基础分支:
git rev-parse --abbrev-ref HEAD存储为BASE_BRANCH。
-
验证远程访问: 如果FORK_MODE:
- 验证fork远程是否存在。检查是否存在名为fork的git远程:
如果不存在,添加它:git remote get-url forkgit remote add fork https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git - 还要验证origin(源仓库)是否可达:
git ls-remote --exit-code origin HEAD
如果不FORK_MODE:
git ls-remote --exit-code origin HEAD如果失败,停止并说:“无法到达远程origin。检查你的网络和git配置。”
- 验证fork远程是否存在。检查是否存在名为fork的git远程:
-
验证GH_TOKEN有效性:
curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/user如果HTTP状态不是200,停止并说:
“GitHub身份验证失败。请检查OpenClaw仪表板中的apiKey,或在~/.openclaw/openclaw.json中的skills.entries.gh-issues下。”
-
检查现有PR: 对于每个确认的问题编号N,运行:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \ "https://api.github.com/repos/{SOURCE_REPO}/pulls?head={PUSH_REPO_OWNER}:fix/issue-{N}&state=open&per_page=1"(其中PUSH_REPO_OWNER是
PUSH_REPO的所有者部分) 如果响应数组不为空,则从处理列表中移除该问题,并报告:“Skipping #{N} — PR already exists: {html_url}”
如果所有问题都被跳过,报告并停止(如果在监视模式下则循环回来)。
-
检查进行中的分支(尚未PR = 子代理仍在工作): 对于每个剩余的问题编号N(上面第5步中没有被跳过的),检查
fix/issue-{N}分支是否存在于推送仓库上(可能是fork,不是origin):curl -s -o /dev/null -w "%{http_code}" \ -H "Authorization: Bearer $GH_TOKEN" \ "https://api.github.com/repos/{PUSH_REPO}/branches/fix/issue-{N}"如果HTTP 200 → 分支存在于推送仓库上但没有找到与之相关的开放PR。跳过该问题:
“Skipping #{N} — branch fix/issue-{N} exists on {PUSH_REPO}, fix likely in progress”
这个检查使用GitHub API而不是git ls-remote,因此在fork模式下也能正确工作(分支被推送到fork,而不是origin)。
如果在所有问题都被跳过此检查后,报告并停止(如果在监视模式下则循环回来)。
-
检查基于声明的进行中跟踪: 这防止了当之前的cron运行的子代理仍在工作但尚未推送分支或打开PR时重复处理。
读取声明文件(如果缺失则创建空的{}):
CLAIMS_FILE="/data/.clawdbot/gh-issues-claims.json" if [ ! -f "$CLAIMS_FILE" ]; then mkdir -p /data/.clawdbot echo '{}' > "$CLAIMS_FILE" fi解析声明文件。对于每个条目,如果声明时间戳超过2小时。如果是,将其移除(已过期 —— 子代理可能已完成或默默失败)。写回清理后的文件:
CLAIMS=$(cat "$CLAIMS_FILE") CUTOFF=$(date -u -d '2 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-2H +%Y-%m-%dT%H:%M:%SZ) CLAIMS=$(echo "$CLAIMS" | jq --arg cutoff "$CUTOFF" 'to_entries | map(select(.value > $cutoff)) | from_entries') echo "$CLAIMS" > "$CLAIMS_FILE"对于每个剩余的问题编号N(上面第5步或第6步中没有被跳过的),检查{SOURCE_REPO}#{N}是否存在作为键在声明文件中。
如果声明并且没有过期 → 跳过:
“Skipping #{N} — sub-agent claimed this issue {minutes}m ago, still within timeout window”
其中{minutes}是从声明时间戳到现在计算的。
如果在所有问题都被跳过此检查后,报告并停止(如果在监视模式下则循环回来)。
阶段5 — 生成子代理(并行)
Cron模式(--cron处于活动状态):
-
顺序游标跟踪: 使用游标文件跟踪下一个要处理的问题:
CURSOR_FILE="/data/.clawdbot/gh-issues-cursor-{SOURCE_REPO_SLUG}.json" # SOURCE_REPO_SLUG = owner-repo,用连字符替换斜杠(例如,openclaw-openclaw)读取游标文件(如果缺失则创建):
if [ ! -f "$CURSOR_FILE" ]; then echo '{"last_processed": null, "in_progress": null}' > "$CURSOR_FILE" filast_processed:最后完成的问题的编号(如果没有则为null)in_progress:当前正在处理的问题编号(如果没有则为null)
-
选择下一个问题: 过滤获取的问题列表,找到第一个问题,其中:
- 问题编号 > last_processed(如果设置了last_processed)
- 并且问题不在声明文件中(尚未进行中)
- 并且没有为该问题存在PR(在阶段4第5步中检查)
- 并且在推送仓库上没有分支(在阶段4第6步中检查)
-
如果在last_processed游标之后没有找到合格的问题,回绕到开始(从最老的合格问题开始)。
-
如果找到了合格的问题:
- 在游标文件中将其标记为进行中
- 为该问题生成一个子代理,
cleanup: "keep"和runTimeoutSeconds: 3600 - 如果提供了
--model,则在生成配置中包括model: "{MODEL}" - 如果提供了
--notify-channel,则在任务中包括频道,以便子代理可以通知 - 不要等待子代理结果 —— 发射并忘记
- 写声明: 生成后,读取声明文件,添加{SOURCE_REPO}#{N}与当前ISO时间戳,并写回
- 立即报告:“Spawned fix agent for #{N} — will create PR when complete”
- 退出技能。不要进入结果收集或阶段6。
-
如果没有找到合格的问题(所有问题要么有PRs,要么有分支,要么正在进行中),报告"No eligible issues to process — all issues have PRs/branches or are in progress"并退出。
普通模式(--cron不处于活动状态):
对于每个确认的问题,使用sessions_spawn生成子代理。同时启动多达8个(匹配subagents.maxConcurrent: 8)。如果问题超过8个,分批处理 —— 每个完成后启动下一个代理。
写声明: 生成每个子代理后,读取声明文件,添加{SOURCE_REPO}#{N}与当前ISO时间戳,并写回(与cron模式上述程序相同)。这涵盖了交互式使用,其中监视模式可能与cron运行重叠。
子代理任务提示
对于每个问题,构建以下提示并传递给sessions_spawn。注入到模板中的变量:
- {SOURCE_REPO} — 问题所在的上游仓库
- {PUSH_REPO} — 推送分支的仓库(与SOURCE_REPO相同,除非fork模式)
- {FORK_MODE} — true/false
- {PUSH_REMOTE} — 如果FORK_MODE,则是fork,否则是origin
- {number}, {title}, {url}, {labels}, {body} — 来自问题
- {BASE_BRANCH} — 来自阶段4
- {notify_channel} — Telegram频道ID用于通知(如果没有设置则为空)。在模板中将{notify_channel}替换为
--notify-channel标志的值(如果没有提供则保留为空字符串)。
构建任务时,用实际值替换所有模板变量,包括{notify_channel}。
你是一个专注的代码修复代理。你的任务是修复一个GitHub问题并打开一个PR。
重要:不要使用gh CLI —— 它没有安装。使用curl与GitHub REST API进行所有GitHub操作。
首先,确保GH_TOKEN已设置。检查:`echo $GH_TOKEN`。如果为空,从配置中读取:
GH_TOKEN=$(cat ~/.openclaw/openclaw.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/openclaw.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty')
在所有GitHub API调用中使用该令牌:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" ...
<config>
源仓库(问题):{SOURCE_REPO}
推送仓库(分支 + PRs):{PUSH_REPO}
Fork模式:{FORK_MODE}
推送远程名称:{PUSH_REMOTE}
基础分支:{BASE_BRANCH}
通知频道:{notify_channel}
</config>
<issue>
仓库:{SOURCE_REPO}
问题:#{number}
标题:{title}
URL:{url}
标签:{labels}
正文:{body}
</issue>
<instructions>
按顺序遵循以下步骤。如果任何步骤失败,报告失败并停止。
0. SETUP — 确保GH_TOKEN可用:
export GH_TOKEN=$(node -e “const fs=require(‘fs’); const c=JSON.parse(fs.readFileSync(‘/data/.clawdbot/openclaw.json’,‘utf8’)); console.log(c.skills?.entries?.[‘gh-issues’]?.apiKey || ‘’)”)
如果失败,也尝试:
export GH_TOKEN=$(cat ~/.openclaw/openclaw.json 2>/dev/null | node -e “const fs=require(‘fs’);const d=JSON.parse(fs.readFileSync(0,‘utf8’));console.log(d.skills?.entries?.[‘gh-issues’]?.apiKey||‘’)”)
验证:echo "Token: ${GH_TOKEN:0:10}..."
1. CONFIDENCE CHECK — 实施前,评估这个问题是否可以操作:
- 仔细阅读问题正文。问题是否清楚描述?
- 搜索代码库(grep/find)以找到相关代码。你能定位它吗?
- 范围合理吗?(单个文件/函数 = 好,整个子系统 = 坏)
- 是否建议了具体的修复,还是只是一个模糊的投诉?
对你的信心进行评分(1-10)。如果信心<7,停止并报告:
> "Skipping #{number}: Low confidence (score: N/10) — [reason: vague requirements | cannot locate code | scope too large | no clear fix suggested]"
只有在信心≥7时才继续。
1. UNDERSTAND — 仔细阅读问题。确定需要更改的内容和位置。
2. BRANCH — 从基础分支创建一个特性分支:
git checkout -b fix/issue-{number} {BASE_BRANCH}
3. ANALYZE — 搜索代码库以找到相关文件:
- 使用grep/find通过exec定位与问题相关的代码
- 阅读相关文件以了解当前行为
- 确定根本原因
4. IMPLEMENT — 进行最小、专注的修复:
- 遵循现有的代码风格和约定
- 只更改修复问题所必需的内容
- 不要在没有理由的情况下添加无关的更改或新依赖
5. TEST — 如果存在,发现并运行现有的测试套件:
- 查找package.json脚本,Makefile目标,pytest,cargo test等。
- 运行相关测试
- 如果测试在修复后失败,尝试一次带有修正方法的重试
- 如果测试仍然失败,报告失败
6. COMMIT — 阶段和提交更改:
git add {changed_files}
git commit -m "fix: {short_description}
Fixes {SOURCE_REPO}#{number}"
7. PUSH — 推送分支:
首先,确保推送远程使用令牌认证并禁用凭据助手:
git config --global credential.helper ""
git remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git
然后推送:
GIT_ASKPASS=true git push -u {PUSH_REMOTE} fix/issue-{number}
8. PR — 使用GitHub API创建拉取请求:
如果FORK_MODE为真,则PR从你的fork到源仓库:
- head = "{PUSH_REPO_OWNER}:fix/issue-{number}"
- base = "{BASE_BRANCH}"
- PR在{SOURCE_REPO}上创建
如果FORK_MODE为假:
- head = "fix/issue-{number}"
- base = "{BASE_BRANCH}"
- PR在{SOURCE_REPO}上创建
curl -s -X POST \
-H "Authorization: Bearer $GH_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/{SOURCE_REPO}/pulls \
-d '{
"title": "fix: {title}",
"head": "{head_value}",
"base": "{BASE_BRANCH}",
"body": "## Summary
{one_paragraph_description_of_fix}
## Changes
{bullet_list_of_changes}
## Testing
{what_was_tested_and_results}
Fixes {SOURCE_REPO}#{number}"
}'
从响应中提取`html_url` —— 这是PR链接。
9. REPORT — 发送摘要:
- PR URL(第8步中的html_url)
- 更改的文件(列表)
- 修复摘要(1-2句话)
- 任何注意事项
10. NOTIFY (如果notify_channel设置) — 如果{notify_channel}不为空,则向Telegram频道发送通知:
使用消息工具,使用:
- action: “send”
- channel: “telegram”
- target: “{notify_channel}”
- message: "✅ PR Created: {SOURCE_REPO}#{number}
{title}
{pr_url}
Files changed: {files_changed_list}"
</instructions>
<constraints>
- 不要强制推送,不要修改基础分支
- 不要进行无关的更改或无谓的重构
- 没有新依赖的理由不要添加新依赖
- 如果问题不清晰或太复杂而无法自信地修复,报告你的分析而不是猜测
- 不要使用gh CLI —— 它不可用。使用curl + GitHub REST API进行所有GitHub操作。
- GH_TOKEN已经在环境中 —— 不要提示认证
- 时间限制:你最多有60分钟。彻底 —— 正确分析,测试你的修复,不要匆忙。
</constraints>
每个子代理的生成配置:
- runTimeoutSeconds: 3600(60分钟)
- cleanup: “keep”(保留记录以供审查)
- 如果提供了
--model,则在生成配置中包括model: "{MODEL}"
超时处理
如果子代理超过60分钟,记录为:
“#{N} — 超时(问题可能太复杂,无法自动修复)”
结果收集
如果--cron处于活动状态: 完全跳过这一节 —— 指挥者已经在阶段5生成后退出。
在所有子代理完成(或超时)后,收集他们的结果。将成功打开的PR列表存储在OPEN_PRS中(PR编号,分支名称,问题编号,PR URL)以供阶段6使用。
展示一个摘要表:
| 问题 | 状态 | PR | 注释 |
|---|---|---|---|
| #42 Fix null pointer | PR opened | https://github.com/.../pull/99 | 3 files changed |
| #37 Add retry logic | Failed | – | Could not identify target code |
| #15 Update docs | Timed out | – | Too complex for auto-fix |
| #8 Fix race condition | Skipped | – | PR already exists |
状态值:
- PR opened — 成功,链接到PR
- Failed — 子代理无法完成(在注释中包括原因)
- Timed out — 超过60分钟限制
- Skipped — 检测到现有PR在预飞行中
最后用一句话总结:
“Processed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped.”
发送通知到频道(如果设置了--notify-channel):
如果提供了--notify-channel,使用message工具将最终摘要发送到该Telegram频道:
使用消息工具,使用:
- action: "send"
- channel: "telegram"
- target: "{notify-channel}"
- message: "✅ GitHub Issues Processed
Processed {N} issues: {success} PRs opened, {failed} failed, {skipped} skipped.
{PR_LIST}"
其中PR_LIST只包括成功打开的PR,格式为:
• #{issue_number}: {PR_url} ({notes})
然后进入阶段6。
阶段6 — PR评论处理程序
这个阶段监控打开的PR(由这个技能或预先存在的fix/issue-* PR创建)以获取审核评论,并生成子代理以解决它们。
当这个阶段运行时:
- 在结果收集(阶段2-5完成后) —— 检查刚刚打开的PR
- 当设置了
--reviews-only标志时 —— 完全跳过阶段2-5,只运行这个阶段 - 在监视模式下 —— 在检查新问题后每个轮询周期运行
Cron评论模式(--cron --reviews-only):
当同时设置了--cron和--reviews-only:
- 运行令牌解析(阶段2令牌部分)
- 发现打开的
fix/issue-*PR(步骤6.1) - 获取审核评论(步骤6.2)
- 分析评论内容以确定是否可操作(步骤6.3)
- 如果发现可操作的评论,为第一个有未解决评论的PR生成一个评论修复子代理 —— 发射并忘记(不要等待结果)
- 使用
cleanup: "keep"和runTimeoutSeconds: 3600 - 如果提供了
--model,则在生成配置中包括model: "{MODEL}"
- 使用
- 报告:“Spawned review handler for PR #{N} — will push fixes when complete”
- 立即退出技能。不要进入步骤6.5(评论结果)。
如果没有发现可操作的评论,报告"No actionable review comments found"并退出。
普通模式(非cron)继续以下:
步骤6.1 — 发现要监控的PR
收集要检查审核评论的PR:
如果来自阶段5: 使用结果收集中的OPEN_PRS列表。
如果--reviews-only或随后的监视周期: 获取所有开放的fix/issue-分支模式的PR:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/{SOURCE_REPO}/pulls?state=open&per_page=100"
过滤为head.ref以fix/issue-开头的PR。
对于每个PR,提取:number(PR编号),head.ref(分支名称),html_url,title,body。
如果没有找到PR,报告"No open fix/ PRs to monitor"并停止(如果在监视模式下则循环回来)。
步骤6.2 — 获取所有审核来源
对于每个PR,从多个来源获取审核:
获取PR审核:
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/reviews"
获取PR审核评论(内联/文件级):
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments"
获取PR问题评论(一般对话):
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments"
获取PR正文以嵌入审核: 一些审核工具(如Greptile)直接在PR正文中嵌入反馈。检查:
<!-- greptile_comment -->标记- PR正文中的其他结构化审核部分
curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}"
提取body字段并解析嵌入的审核内容。
步骤6.3 — 分析评论以确定可操作性
确定机器人自己的用户名以过滤:
curl -s -H "Authorization: Bearer $GH_TOKEN" https://api.github.com/user | jq -r '.login'
存储为BOT_USERNAME。排除任何user.login等于BOT_USERNAME的评论。
对于每个评论/审核,分析内容以确定是否需要行动:
不可操作(跳过):
- 纯粹的批准或"LGTM"没有建议
- 仅提供信息的机器人评论(CI状态,没有具体请求的自动生成摘要)
- 已经解决的评论(检查机器人是否用"Addressed in commit…"回复)
- 审核状态为
APPROVED且没有内联评论请求更改
是可操作的(需要关注):
- 审核状态为
CHANGES_REQUESTED - 审核状态为
COMMENTED包含具体请求:- “this test needs to be updated”
- “please fix”, “change this”, “update”, “can you”, “should be”, “needs to”
- “will fail”, “will break”, “causes an error”
- 提及特定代码问题(错误,缺少错误处理,边缘情况)
- 指出代码问题的内联审核评论
- PR正文中嵌入的审核,识别:
- 临界问题或破坏性更改
- 预期的测试失败
- 需要关注的特定代码
- 带有担忧的信心分数
解析嵌入的审核内容(例如,Greptile):
寻找用<!-- greptile_comment -->或类似标记的节。提取:
- 摘要文本
- 任何提及"Critical issue", “needs attention”, “will fail”, “test needs to be updated”
- 低于4/5的信心分数(表示担忧)
构建可操作_comments列表,包括:
- 来源(审核,内联评论,PR正文等)
- 作者
- 正文文本
- 对于内联:文件路径和行号
- 识别的具体行动项目
如果没有在任何PR中发现可操作的评论,报告"No actionable review comments found"并停止(如果在监视模式下则循环回来)。
步骤6.4 — 展示审核评论
展示带有待处理可操作评论的PR的表格:
| PR | Branch | Actionable Comments | Sources |
|----|--------|---------------------|---------|
| #99 | fix/issue-42 | 2 comments | @reviewer1, greptile |
| #101 | fix/issue-37 | 1 comment | @reviewer2 |
如果--yes未设置且这不是随后的监视轮询:询问用户确认要处理哪些PR(“all”,逗号分隔的PR编号,或"skip")。
步骤6.5 — 生成审核修复子代理(并行)
对于每个有可操作评论的PR,生成一个子代理。同时启动多达8个。
审核修复子代理提示:
你是一个PR审核处理代理。你的任务是通过进行请求的更改,推送更新,并回复每个评论来处理拉取请求上的审核评论。
重要:不要使用gh CLI —— 它没有安装。使用curl与GitHub REST API进行所有GitHub操作。
首先,确保GH_TOKEN已设置。检查:echo $GH_TOKEN。如果为空,从配置中读取:
GH_TOKEN=$(cat ~/.openclaw/openclaw.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty') || GH_TOKEN=$(cat /data/.clawdbot/openclaw.json 2>/dev/null | jq -r '.skills.entries["gh-issues"].apiKey // empty')
<config>
仓库:{SOURCE_REPO}
推送仓库:{PUSH_REPO}
Fork模式:{FORK_MODE}
推送远程:{PUSH_REMOTE}
PR编号:{pr_number}
PR URL:{pr_url}
分支:{branch_name}
</config>
<review_comments>
{json_array_of_actionable_comments}
每个评论都有:
- id:评论ID(用于回复)
- user:谁留下的
- body:评论文本
- path:文件路径(对于内联评论)
- line:行号(对于内联评论)
- diff_hunk:周围的diff上下文(对于内联评论)
- source:评论来源(审核,内联,pr_body,greptile等)
</review_comments>
<instructions>
按顺序遵循以下步骤:
0. SETUP — 确保GH_TOKEN可用:
export GH_TOKEN=$(node -e “const fs=require(‘fs’); const c=JSON.parse(fs.readFileSync(‘/data/.clawdbot/openclaw.json’,‘utf8’)); console.log(c.skills?.entries?.[‘gh-issues’]?.apiKey || ‘’)”)
验证:echo "Token: ${GH_TOKEN:0:10}..."
1. CHECKOUT — 切换到PR分支:
git fetch {PUSH_REMOTE} {branch_name}
git checkout {branch_name}
git pull {PUSH_REMOTE} {branch_name}
2. UNDERSTAND — 仔细阅读所有审核评论。按文件分组。了解每个审阅者要求的内容。
3. IMPLEMENT — 对于每个评论,进行请求的更改:
- 阅读文件并定位相关代码
- 进行审阅者请求的更改
- 如果评论模糊或你不同意,仍然尝试合理的修复,但注明你的担忧
- 如果评论要求不可能或矛盾的事情,跳过并在回复中解释原因
4. TEST — 运行现有测试以确保你的更改不会破坏任何内容:
- 如果测试失败,修复问题或撤销有问题的更改
- 在你的回复中注明任何测试失败
5. COMMIT — 阶段并提交所有更改为一个提交:
git add {changed_files}
git commit -m "fix: address review comments on PR #{pr_number}
Addresses review feedback from {reviewer_names}"
6. PUSH — 推送更新的分支:
git config --global credential.helper ""
git remote set-url {PUSH_REMOTE} https://x-access-token:$GH_TOKEN@github.com/{PUSH_REPO}.git
GIT_ASKPASS=true git push {PUSH_REMOTE} {branch_name}
7. REPLY — 对于每个解决的评论,发布回复:
对于内联审核评论(有路径/行),在评论线程中回复:
curl -s -X POST \
-H "Authorization: Bearer $GH_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/{SOURCE_REPO}/pulls/{pr_number}/comments/{comment_id}/replies \
-d '{"body": "Addressed in commit {short_sha} — {brief_description_of_change}"}'
对于一般PR评论(问题评论),在PR上回复:
curl -s -X POST \
-H "Authorization: Bearer $GH_TOKEN" \
-H "Accept: application/vnd.github+json" \
https://api.github.com/repos/{SOURCE_REPO}/issues/{pr_number}/comments \
-d '{"body": "Addressed feedback from @{reviewer}:
{summary_of_changes_made}
Updated in commit {short_sha}"}'
对于你不能解决的评论,回复解释原因:
"Unable to address this comment: {reason}. This may need manual review."
8. REPORT — 发送摘要:
- PR URL
- 解决的评论数量与跳过的评论数量
- 提交SHA
- 更改的文件
- 需要手动关注的任何评论
</instructions>
<constraints>
- 只修改与审核评论相关的文件
- 不要进行无关的更改
- 不要强制推送 —— 总是正常推送
- 如果一个评论与另一个评论相矛盾,处理最新的一个并标记冲突
- 不要使用gh CLI —— 使用curl + GitHub REST API
- GH_TOKEN已经在环境中 —— 不要提示认证
- 时间限制:最多60分钟
</constraints>
每个审核修复子代理的生成配置:
- runTimeoutSeconds: 3600(60分钟)
- cleanup: “keep”(保留记录以供审查)
- 如果提供了
--model,则在生成配置中包括model: "{MODEL}"
步骤6.6 — 审核结果
在所有审核子代理完成后,展示一个摘要:
| PR | Comments Addressed | Comments Skipped | Commit | Status |
|----|-------------------|-----------------|--------|--------|
| #99 fix/issue-42 | 3 | 0 | abc123f | All addressed |
| #101 fix/issue-37 | 1 | 1 | def456a | 1 needs manual review |
将此批次的评论ID添加到ADDRESSED_COMMENTS集合中,以防止重新处理。
监视模式(如果–watch处于活动状态)
在展示当前批次的结果后:
- 将此批次的所有问题编号添加到运行中的PROCESSED_ISSUES集合中。
- 将所有解决的评论ID添加到ADDRESSED_COMMENTS。
- 告诉用户:
“Next poll in {interval} minutes… (say ‘stop’ to end watch mode)”
- 睡眠{interval}分钟。
- 返回阶段2 — 获取问题。获取将自动过滤:
- 已经在PROCESSED_ISSUES中的问题
- 已经有fix/issue-{N} PR的问题(在阶段4预飞行中捕获)
- 在阶段2-5之后(如果没有新问题),运行阶段6以检查所有跟踪的PR上的新审核评论(包括新创建的和之前打开的)。
- 如果没有新问题并且没有新的可操作审核评论 → 报告"No new activity. Polling again in {interval} minutes…"并循环回到步骤4。
- 用户可以随时说"stop"以退出监视模式。停止时,展示所有批次的最终累积摘要 —— 处理问题和审核评论。
轮询之间的上下文卫生 —— 重要: 只在轮询周期之间保留:
- PROCESSED_ISSUES(问题编号集合)
- ADDRESSED_COMMENTS(评论ID集合)
- OPEN_PRS(跟踪的PR列表:编号,分支,URL)
- 累积结果(每个问题一行 + 每个审核批次一行)
- 解析的参数来自阶段1
- BASE_BRANCH, SOURCE_REPO, PUSH_REPO, FORK_MODE, BOT_USERNAME 不要在轮询之间保留问题正文,评论正文,子代理记录,或代码库分析。