Loading...
Loading...
Compare original and translation side by side
| Aspect | Rule |
|---|---|
| Task Granularity | 1 PR = Exactly 1 |
| Execution Mode | |
| Result Handling | |
| Reporting | IMMEDIATE streaming when each task finishes |
| 维度 | 规则 |
|---|---|
| 任务粒度 | 1个PR = 恰好1次 |
| 执行模式 | |
| 结果处理 | 使用 |
| 报告方式 | 每个任务完成后立即流式上报 |
| WRONG | CORRECT |
|---|---|
| Fetch all → Wait for all agents → Report all at once | Fetch all → Launch 1 task per PR (background) → Stream results as each completes → Next |
| "Processing 50 PRs... (wait 5 min) ...here are all results" | "PR #123 analysis complete... [RESULT] PR #124 analysis complete... [RESULT] ..." |
| User sees nothing during processing | User sees live progress as each background task finishes |
| |
| 错误做法 | 正确做法 |
|---|---|
| 全量获取 → 等待所有代理完成 → 一次性报告所有结果 | 全量获取 → 为每个PR启动1个后台任务 → 每个任务完成后立即流式返回结果 → 继续处理下一个 |
| "正在处理50个PR...(等待5分钟)...以下是所有结果" | "PR #123分析完成... [结果] PR #124分析完成... [结果] ..." |
| 处理过程中用户看不到任何内容 | 用户可实时查看每个后台任务完成的进度 |
| |
// CORRECT: Launch all as background tasks, stream results
const taskIds = []
// Category ratio: unspecified-low : writing : quick = 1:2:1
// Every 4 PRs: 1 unspecified-low, 2 writing, 1 quick
function getCategory(index) {
const position = index % 4
if (position === 0) return "unspecified-low" // 25%
if (position === 1 || position === 2) return "writing" // 50%
return "quick" // 25%
}
// PHASE 1: Launch 1 background task per PR
for (let i = 0; i < allPRs.length; i++) {
const pr = allPRs[i]
const category = getCategory(i)
const taskId = await task(
category=category,
load_skills=[],
run_in_background=true, // ← CRITICAL: Each PR is independent background task
prompt=`Analyze PR #${pr.number}...`
)
taskIds.push({ pr: pr.number, taskId, category })
console.log(`🚀 Launched background task for PR #${pr.number} (${category})`)
}
// PHASE 2: Stream results as they complete
console.log(`\n📊 Streaming results for ${taskIds.length} PRs...`)
const completed = new Set()
while (completed.size < taskIds.length) {
for (const { pr, taskId } of taskIds) {
if (completed.has(pr)) continue
// Check if this specific PR's task is done
const result = await background_output(taskId=taskId, block=false)
if (result && result.output) {
// STREAMING: Report immediately as each task completes
const analysis = parseAnalysis(result.output)
reportRealtime(analysis)
completed.add(pr)
console.log(`\n✅ PR #${pr} analysis complete (${completed.size}/${taskIds.length})`)
}
}
// Small delay to prevent hammering
if (completed.size < taskIds.length) {
await new Promise(r => setTimeout(r, 1000))
}
}// 正确做法:将所有任务作为后台任务启动,流式返回结果
const taskIds = []
// 任务类别比例:unspecified-low : writing : quick = 1:2:1
// 每4个PR:1个unspecified-low,2个writing,1个quick
function getCategory(index) {
const position = index % 4
if (position === 0) return "unspecified-low" // 25%
if (position === 1 || position === 2) return "writing" // 50%
return "quick" // 25%
}
// 阶段1:为每个PR启动1个后台任务
for (let i = 0; i < allPRs.length; i++) {
const pr = allPRs[i]
const category = getCategory(i)
const taskId = await task(
category=category,
load_skills=[],
run_in_background=true, // ← 核心:每个PR是独立的后台任务
prompt=`Analyze PR #${pr.number}...`
)
taskIds.push({ pr: pr.number, taskId, category })
console.log(`🚀 为PR #${pr.number}启动后台任务(${category})`)
}
// 阶段2:每个任务完成后立即流式返回结果
console.log(`\n📊 正在为${taskIds.length}个PR流式返回结果...`)
const completed = new Set()
while (completed.size < taskIds.length) {
for (const { pr, taskId } of taskIds) {
if (completed.has(pr)) continue
// 检查当前PR的任务是否完成
const result = await background_output(taskId=taskId, block=false)
if (result && result.output) {
// 流式处理:每个任务完成后立即上报
const analysis = parseAnalysis(result.output)
reportRealtime(analysis)
completed.add(pr)
console.log(`\n✅ PR #${pr}分析完成(${completed.size}/${taskIds.length})`)
}
}
// 短暂延迟避免频繁查询
if (completed.size < taskIds.length) {
await new Promise(r => setTimeout(r, 1000))
}
}// Create todos immediately
todowrite([
{ id: "1", content: "Fetch all open PRs with exhaustive pagination", status: "in_progress", priority: "high" },
{ id: "2", content: "Launch 1 background task per PR (1 PR = 1 task)", status: "pending", priority: "high" },
{ id: "3", content: "Stream-process results as each task completes", status: "pending", priority: "high" },
{ id: "4", content: "Execute conservative auto-close for eligible PRs", status: "pending", priority: "high" },
{ id: "5", content: "Generate final comprehensive report", status: "pending", priority: "high" }
])// 立即创建待办事项
todowrite([
{ id: "1", content: "通过全量分页获取所有开放状态的PR", status: "in_progress", priority: "high" },
{ id: "2", content: "为每个PR启动1个后台任务(1个PR = 1个任务)", status: "pending", priority: "high" },
{ id: "3", content: "每个任务完成后流式返回结果", status: "pending", priority: "high" },
{ id: "4", content: "对符合条件的PR执行保守式自动关闭", status: "pending", priority: "high" },
{ id: "5", content: "生成完整的最终报告", status: "pending", priority: "high" }
])./scripts/gh_fetch.py prs --output json./scripts/gh_fetch.py prs --output jsonREPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
gh pr list --repo $REPO --state open --limit 500 --json number,title,state,createdAt,updatedAt,labels,author,headRefName,baseRefName,isDraft,mergeable,bodyREPO=$(gh repo view --json nameWithOwner -q .nameWithOwner)
gh pr list --repo $REPO --state open --limit 500 --json number,title,state,createdAt,updatedAt,labels,author,headRefName,baseRefName,isDraft,mergeable,body
// 如果返回500个PR,继续分页...
**AFTER Phase 1:** Update todo status to completed, mark Phase 2 as in_progress.
---// 用于跟踪的集合
const taskMap = new Map() // prNumber -> taskId
// 任务类别比例:unspecified-low : writing : quick = 1:2:1
// 每4个PR:1个unspecified-low,2个writing,1个quick
function getCategory(index) {
const position = index % 4
if (position === 0) return "unspecified-low" // 25%
if (position === 1 || position === 2) return "writing" // 50%
return "quick" // 25%
}
// 为每个PR启动1个后台任务
for (let i = 0; i < allPRs.length; i++) {
const pr = allPRs[i]
const category = getCategory(i)
console.log(`🚀 为PR #${pr.number}启动后台任务(${category})...`)
const taskId = await task(
category=category,
load_skills=[],
run_in_background=true, // ← 后台任务:每个PR独立运行
prompt=`// Collection for tracking
const taskMap = new Map() // prNumber -> taskId
// Category ratio: unspecified-low : writing : quick = 1:2:1
// Every 4 PRs: 1 unspecified-low, 2 writing, 1 quick
function getCategory(index) {
const position = index % 4
if (position === 0) return "unspecified-low" // 25%
if (position === 1 || position === 2) return "writing" // 50%
return "quick" // 25%
}
// Launch 1 background task per PR
for (let i = 0; i < allPRs.length; i++) {
const pr = allPRs[i]
const category = getCategory(i)
console.log(`🚀 Launching background task for PR #${pr.number} (${category})...`)
const taskId = await task(
category=category,
load_skills=[],
run_in_background=true, // ← BACKGROUND TASK: Each PR runs independently
prompt=`\n✅ 已启动${taskMap.size}个后台任务(1个PR对应1个任务)
**阶段2完成后**:更新待办事项,标记阶段3为进行中。
---\n✅ Launched ${taskMap.size} background tasks (1 per PR)
**AFTER Phase 2:** Update todo, mark Phase 3 as in_progress.
---const results = []
const autoCloseable = []
const readyToMerge = []
const needsReview = []
const needsWork = []
const stale = []
const drafts = []
const completedPRs = new Set()
const totalPRs = taskMap.size
console.log(`\n📊 正在为${totalPRs}个PR流式返回结果...`)
// 每个后台任务完成后流式返回结果
while (completedPRs.size < totalPRs) {
let newCompletions = 0
for (const [prNumber, taskId] of taskMap) {
if (completedPRs.has(prNumber)) continue
// 非阻塞式检查当前任务
const output = await background_output(task_id=taskId, block=false)
if (output && output.length > 0) {
// 解析已完成的分析结果
const analysis = parseAnalysis(output)
results.push(analysis)
completedPRs.add(prNumber)
newCompletions++
// 实时流式报告
console.log(`\n🔄 PR #${prNumber}: ${analysis.TITLE.substring(0, 60)}...`)
// 实时分类与报告
if (analysis.CLOSE_ELIGIBLE === 'YES') {
autoCloseable.push(analysis)
console.log(` ⚠️ 可自动关闭:${analysis.CLOSE_REASON}`)
} else if (analysis.MERGE_READY === 'YES') {
readyToMerge.push(analysis)
console.log(` ✅ 可立即合并`)
} else if (analysis.RECOMMENDATION === 'REVIEW') {
needsReview.push(analysis)
console.log(` 👀 需要评审`)
} else if (analysis.RECOMMENDATION === 'WAIT') {
needsWork.push(analysis)
console.log(` ⏳ 等待作者处理`)
} else if (analysis.STALENESS === 'STALE' || analysis.STALENESS === 'ABANDONED') {
stale.push(analysis)
console.log(` 💤 ${analysis.STALENESS}`)
} else {
drafts.push(analysis)
console.log(` 📝 草稿`)
}
console.log(` 📊 操作建议:${analysis.ACTION_NEEDED}`)
// 每完成5个PR更新一次进度
if (completedPRs.size % 5 === 0) {
console.log(`\n📈 进度:已分析${completedPRs.size}/${totalPRs}个PR`)
console.log(` 可合并:${readyToMerge.length} | 需要评审:${needsReview.length} | 等待处理:${needsWork.length} | 已废弃:${stale.length} | 草稿:${drafts.length} | 可自动关闭:${autoCloseable.length}`)
}
}
}
// 如果没有新完成的任务,短暂延迟后再查询
if (newCompletions === 0 && completedPRs.size < totalPRs) {
await new Promise(r => setTimeout(r, 2000))
}
}
console.log(`\n✅ 所有${totalPRs}个PR分析完成`)const results = []
const autoCloseable = []
const readyToMerge = []
const needsReview = []
const needsWork = []
const stale = []
const drafts = []
const completedPRs = new Set()
const totalPRs = taskMap.size
console.log(`\n📊 Streaming results for ${totalPRs} PRs...`)
// Stream results as each background task completes
while (completedPRs.size < totalPRs) {
let newCompletions = 0
for (const [prNumber, taskId] of taskMap) {
if (completedPRs.has(prNumber)) continue
// Non-blocking check for this specific task
const output = await background_output(task_id=taskId, block=false)
if (output && output.length > 0) {
// Parse the completed analysis
const analysis = parseAnalysis(output)
results.push(analysis)
completedPRs.add(prNumber)
newCompletions++
// REAL-TIME STREAMING REPORT
console.log(`\n🔄 PR #${prNumber}: ${analysis.TITLE.substring(0, 60)}...`)
// Immediate categorization & reporting
if (analysis.CLOSE_ELIGIBLE === 'YES') {
autoCloseable.push(analysis)
console.log(` ⚠️ AUTO-CLOSE CANDIDATE: ${analysis.CLOSE_REASON}`)
} else if (analysis.MERGE_READY === 'YES') {
readyToMerge.push(analysis)
console.log(` ✅ READY TO MERGE`)
} else if (analysis.RECOMMENDATION === 'REVIEW') {
needsReview.push(analysis)
console.log(` 👀 NEEDS REVIEW`)
} else if (analysis.RECOMMENDATION === 'WAIT') {
needsWork.push(analysis)
console.log(` ⏳ WAITING FOR AUTHOR`)
} else if (analysis.STALENESS === 'STALE' || analysis.STALENESS === 'ABANDONED') {
stale.push(analysis)
console.log(` 💤 ${analysis.STALENESS}`)
} else {
drafts.push(analysis)
console.log(` 📝 DRAFT`)
}
console.log(` 📊 Action: ${analysis.ACTION_NEEDED}`)
// Progress update every 5 completions
if (completedPRs.size % 5 === 0) {
console.log(`\n📈 PROGRESS: ${completedPRs.size}/${totalPRs} PRs analyzed`)
console.log(` Ready: ${readyToMerge.length} | Review: ${needsReview.length} | Wait: ${needsWork.length} | Stale: ${stale.length} | Draft: ${drafts.length} | Close-Candidate: ${autoCloseable.length}`)
}
}
}
// If no new completions, wait briefly before checking again
if (newCompletions === 0 && completedPRs.size < totalPRs) {
await new Promise(r => setTimeout(r, 2000))
}
}
console.log(`\n✅ All ${totalPRs} PRs analyzed`)if (autoCloseable.length > 0) {
console.log(`\n🚨 发现${autoCloseable.length}个符合自动关闭条件的PR:`)
for (const pr of autoCloseable) {
console.log(` #${pr.PR}: ${pr.TITLE} (${pr.CLOSE_REASON})`)
}
// 逐个关闭并显示进度
for (const pr of autoCloseable) {
console.log(`\n 正在关闭#${pr.PR}...`)
await bash({
command: `gh pr close ${pr.PR} --repo ${REPO} --comment "${pr.CLOSE_MESSAGE}"`,
description: `为PR #${pr.PR}添加友好提示后关闭`
})
console.log(` ✅ 已关闭#${pr.PR}`)
}
}if (autoCloseable.length > 0) {
console.log(`\n🚨 FOUND ${autoCloseable.length} PR(s) ELIGIBLE FOR AUTO-CLOSE:`)
for (const pr of autoCloseable) {
console.log(` #${pr.PR}: ${pr.TITLE} (${pr.CLOSE_REASON})`)
}
// Close them one by one with progress
for (const pr of autoCloseable) {
console.log(`\n Closing #${pr.PR}...`)
await bash({
command: `gh pr close ${pr.PR} --repo ${REPO} --comment "${pr.CLOSE_MESSAGE}"`,
description: `Close PR #${pr.PR} with friendly message`
})
console.log(` ✅ Closed #${pr.PR}`)
}
}undefinedundefined| 类别 | 数量 | 状态 |
|---|---|---|
| ✅ 可立即合并 | ${readyToMerge.length} | 操作:立即合并 |
| ⚠️ 已自动关闭 | ${autoCloseable.length} | 已处理完成 |
| 👀 需要评审 | ${needsReview.length} | 操作:分配评审人员 |
| ⏳ 需要处理 | ${needsWork.length} | 操作:添加指导评论 |
| 💤 已废弃 | ${stale.length} | 操作:跟进作者 |
| 📝 草稿 | ${drafts.length} | 无需操作 |
| Category | Count | Status |
|---|---|---|
| ✅ Ready to Merge | ${readyToMerge.length} | Action: Merge immediately |
| ⚠️ Auto-Closed | ${autoCloseable.length} | Already processed |
| 👀 Needs Review | ${needsReview.length} | Action: Assign reviewers |
| ⏳ Needs Work | ${needsWork.length} | Action: Comment guidance |
| 💤 Stale | ${stale.length} | Action: Follow up |
| 📝 Draft | ${drafts.length} | No action needed |
| #${pr.PR} | ${pr.TITLE.substring(0, 50)}... || #${pr.PR} | ${pr.TITLE.substring(0, 50)}... || #${pr.PR} | ${pr.TITLE.substring(0, 40)}... | ${pr.CLOSE_REASON} || #${pr.PR} | ${pr.TITLE.substring(0, 40)}... | ${pr.CLOSE_REASON} || #${pr.PR} | ${pr.TITLE.substring(0, 50)}... || #${pr.PR} | ${pr.TITLE.substring(0, 50)}... || #${pr.PR} | ${pr.TITLE.substring(0, 50)}... | ${pr.ACTION_NEEDED} || #${pr.PR} | ${pr.TITLE.substring(0, 50)}... | ${pr.ACTION_NEEDED} || #${pr.PR} | ${pr.TITLE.substring(0, 40)}... | ${pr.STALENESS} || #${pr.PR} | ${pr.TITLE.substring(0, 40)}... | ${pr.STALENESS} || #${pr.PR} | ${pr.TITLE.substring(0, 50)}... || #${pr.PR} | ${pr.TITLE.substring(0, 50)}... |${i+1}. #${r.PR}: ${r.RECOMMENDATION} (${r.MERGE_READY === 'YES' ? '可合并' : r.CLOSE_ELIGIBLE === 'YES' ? '可关闭' : '需要关注'})
---${i+1}. #${r.PR}: ${r.RECOMMENDATION} (${r.MERGE_READY === 'YES' ? 'ready' : r.CLOSE_ELIGIBLE === 'YES' ? 'close' : 'needs attention'})
---| 违规行为 | 错误原因 | 严重程度 |
|---|---|---|
| 将多个PR批量放入同一个任务 | 违反1个PR对应1个任务的规则 | 严重 |
使用 | 无并行性,执行速度慢 | 严重 |
| 收集所有任务结果后一次性报告 | 失去流式处理的优势 | 严重 |
未使用 | 无法流式返回结果 | 严重 |
| 无进度更新 | 用户不知道系统是卡住还是在运行 | 高 |
| Violation | Why It's Wrong | Severity |
|---|---|---|
| Batch multiple PRs in one task | Violates 1 PR = 1 task rule | CRITICAL |
Use | No parallelism, slower execution | CRITICAL |
| Collect all tasks, report at end | Loses streaming benefit | CRITICAL |
No | Can't stream results | CRITICAL |
| No progress updates | User doesn't know if stuck or working | HIGH |
run_in_background=truebackground_output()run_in_background=truebackground_output()gh repo view --json nameWithOwner -q .nameWithOwnertask(run_in_background=true)background_output()gh repo view --json nameWithOwner -q .nameWithOwnertask(run_in_background=true)background_output()