|
| 1 | +import { Env } from "@/types"; |
| 2 | +import { App } from "octokit"; |
| 3 | +import { withCompatOctokit } from "@/services/octokit/compat"; |
| 4 | +import { createSupervisorAgent, createCodeReviewAgent, createSummaryAgent } from "./agents"; |
| 5 | +import { chunkFiles } from "./utils/chunking"; |
| 6 | +import { filterFilesForReview } from "./utils/filter"; |
| 7 | +import { z } from "zod"; |
| 8 | +import { createRunner } from "@/ai/agents/base/agent-ai"; |
| 9 | + |
| 10 | +export async function orchestratePrReview(env: Env, payload: any, appId: string, privateKey: string) { |
| 11 | + const prNumber = payload.pull_request?.number; |
| 12 | + const owner = payload.repository?.owner?.login; |
| 13 | + const repo = payload.repository?.name; |
| 14 | + const installationId = payload.installation?.id; |
| 15 | + |
| 16 | + if (!prNumber || !owner || !repo || !installationId) { |
| 17 | + console.warn("[PR Orchestrator] Missing required PR context payload fields."); |
| 18 | + return; |
| 19 | + } |
| 20 | + |
| 21 | + const octokitCtx = { appId, privateKey, installationId }; |
| 22 | + const runner = await createRunner(env, "worker-ai"); |
| 23 | + |
| 24 | + // 1. Supervisor Phase: Fetch and Filter Diffs |
| 25 | + const supervisor = createSupervisorAgent(env, octokitCtx); |
| 26 | + |
| 27 | + const supervisorPlanStr = await runner.run(supervisor, `Analyze PR #${prNumber} in ${owner}/${repo}. Call fetch_pr_diff to get the files. Decide if this PR needs review. Output a JSON plan with { shouldReview: boolean, reasoning: string, filesToReview: string[] }`) as any; |
| 28 | + |
| 29 | + let supervisorPlan; |
| 30 | + try { |
| 31 | + supervisorPlan = JSON.parse(supervisorPlanStr.finalOutput); |
| 32 | + } catch (e) { |
| 33 | + supervisorPlan = { shouldReview: true, reasoning: "Fallback", filesToReview: [] }; |
| 34 | + } |
| 35 | + |
| 36 | + if (!supervisorPlan.shouldReview) { |
| 37 | + console.log(`[PR Orchestrator] Supervisor skipped review for PR #${prNumber}: ${supervisorPlan.reasoning}`); |
| 38 | + return; |
| 39 | + } |
| 40 | + |
| 41 | + // Fetch the full diff to get patches (Supervisor might not have passed it back directly) |
| 42 | + const app = new App({ appId, privateKey }); |
| 43 | + const octokit = withCompatOctokit(await app.getInstallationOctokit(installationId)); |
| 44 | + const { data: files } = await octokit.rest.pulls.listFiles({ owner, repo, pull_number: prNumber, per_page: 100 }); |
| 45 | + |
| 46 | + const filesToReview = filterFilesForReview(files).filter(f => supervisorPlan.filesToReview.length === 0 || supervisorPlan.filesToReview.includes(f.filename)); |
| 47 | + const chunks = chunkFiles(filesToReview); |
| 48 | + |
| 49 | + // 2. Code Review Phase: Process Chunks |
| 50 | + const reviewer = createCodeReviewAgent(env, octokitCtx); |
| 51 | + const reviewFindings: any[] = []; |
| 52 | + |
| 53 | + for (const chunk of chunks) { |
| 54 | + const chunkResultStr = await runner.run(reviewer, `Review the following diff chunk for ${chunk.filename}:\n\n${chunk.content}\n\nCall create_review_comment if you find bugs or critical issues. Do not comment on nitpicks. Return JSON { commentsMade: number, issuesFound: string[] }`) as any; |
| 55 | + let chunkResult; |
| 56 | + try { |
| 57 | + chunkResult = JSON.parse(chunkResultStr.finalOutput || '{}'); |
| 58 | + } catch(e) { |
| 59 | + chunkResult = { issuesFound: [] }; |
| 60 | + } |
| 61 | + |
| 62 | + reviewFindings.push({ filename: chunk.filename, chunkId: chunk.chunkId, issues: chunkResult.issuesFound }); |
| 63 | + } |
| 64 | + |
| 65 | + // 3. Summary Phase: Submit Final Review |
| 66 | + const summaryAgent = createSummaryAgent(env, octokitCtx); |
| 67 | + const totalIssues = reviewFindings.flatMap(f => f.issues).length; |
| 68 | + const finalEvent = totalIssues > 0 ? "REQUEST_CHANGES" : "APPROVE"; |
| 69 | + |
| 70 | + await runner.run(summaryAgent, `Summarize the findings for PR #${prNumber} in ${owner}/${repo}. Total issues found: ${totalIssues}. Review findings: ${JSON.stringify(reviewFindings)}. Call submit_pr_review with event ${finalEvent} to finalize.`); |
| 71 | + |
| 72 | + console.log(`[PR Orchestrator] Completed review for PR #${prNumber}`); |
| 73 | +} |
0 commit comments