Skip to content

Commit 2f3b3b0

Browse files
keithahfwang
authored andcommitted
Fix multi-provider review script and workflow
- Remove duplicate function definitions in multi-review-script.ts - Fix prompt variable passing to runReview function - Update workflow files to use local script instead of external URL - Add proper PR body extraction and AGENTS.md handling
1 parent 7034c27 commit 2f3b3b0

File tree

2 files changed

+67
-84
lines changed

2 files changed

+67
-84
lines changed

github/multi-provider-review-template.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ jobs:
7878
PR_BODY: ${{ steps.pr-body.outputs.body }}
7979
HAS_AGENTS: ${{ steps.agents-md.outputs.exists || 'false' }}
8080
run: |
81-
curl -f -o multi-review.ts https://raw.githubusercontent.com/keithah/multi-provider-code-review/main/multi-review-script.ts
81+
cp github/multi-review-script.ts multi-review.ts
8282
8383
if [ "$HAS_AGENTS" = "true" ]; then
8484
export INCLUDE_AGENTS=true

github/multi-review-script.ts

Lines changed: 66 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ type ReviewResult = {
55
output: string
66
}
77

8-
const REVIEW_PROVIDERS = (process.env.REVIEW_PROVIDERS || "").split(",").map((p) => p.trim()).filter(Boolean)
8+
const REVIEW_PROVIDERS = (process.env.REVIEW_PROVIDERS || "")
9+
.split(",")
10+
.map((p) => p.trim())
11+
.filter(Boolean)
912
const PR_NUMBER = process.env.PR_NUMBER || ""
1013
const PR_TITLE = process.env.PR_TITLE || ""
1114
const PR_BODY = process.env.PR_BODY || ""
@@ -25,7 +28,7 @@ async function buildPrompt(): Promise<string> {
2528
} catch (e) {
2629
console.log("Warning: Could not read AGENTS.md")
2730
}
28-
31+
2932
return `REPO: ${REPO}
3033
PR NUMBER: ${PR_NUMBER}
3134
PR TITLE: ${PR_TITLE}
@@ -71,7 +74,7 @@ IMPORTANT: Only create comments for actual issues. If the code follows all guide
7174

7275
async function runReview(provider: string, prompt: string): Promise<ReviewResult> {
7376
console.log(`Starting review with provider: ${provider}`)
74-
77+
7578
try {
7679
const result = execSync(`opencode run -m ${provider} -- "${prompt.replace(/"/g, '\\"')}"`, {
7780
encoding: "utf8",
@@ -86,7 +89,7 @@ async function runReview(provider: string, prompt: string): Promise<ReviewResult
8689

8790
async function synthesize(reviews: ReviewResult[]): Promise<string> {
8891
const combined = reviews.map((r) => `## Review from ${r.provider}\n\n${r.output}`).join("\n\n---\n\n")
89-
const providerList = reviews.map(r => r.provider).join(", ")
92+
const providerList = reviews.map((r) => r.provider).join(", ")
9093
const synthesisPrompt = `You are an expert code reviewer. Synthesize these reviews into one comprehensive review following Claude Code's professional format.
9194
9295
Rules:
@@ -149,7 +152,7 @@ Status: ⚠️ [Approve with Recommendations / Changes Requested / Approved]
149152
**Positive Aspects** ✨
150153
✅ [Positive aspect 1]
151154
✅ [Positive aspect 2]
152-
✅ [Positive aspect 3]
155+
✅ [Positive aspect 3]`
153156

154157
try {
155158
return execSync(`opencode run -m opencode/big-pickle -- "${synthesisPrompt.replace(/"/g, '\\"')}"`, {
@@ -161,7 +164,6 @@ Status: ⚠️ [Approve with Recommendations / Changes Requested / Approved]
161164
return combined
162165
}
163166
}
164-
}
165167

166168
let checklistCommentId: string | null = null
167169

@@ -171,28 +173,34 @@ async function postOrUpdateChecklist(status: string, completedTasks: string[] =
171173
"Read modified files",
172174
"Analyze security implications",
173175
"Review code quality and conventions",
174-
"Provide comprehensive feedback"
176+
"Provide comprehensive feedback",
175177
]
176178

177-
const checklist = tasks.map(task => {
178-
const isCompleted = completedTasks.includes(task)
179-
return `${isCompleted ? '✅' : '⏳'} ${task}`
180-
}).join('\n')
179+
const checklist = tasks
180+
.map((task) => {
181+
const isCompleted = completedTasks.includes(task)
182+
return `${isCompleted ? "✅" : "⏳"} ${task}`
183+
})
184+
.join("\n")
181185

182186
const body = `🤖 **Multi-Provider Code Review** ${status}\n\n**Tasks:**\n${checklist}`
183187

184-
const escaped = body.replace(/"/g, '\\"').replace(/\n/g, '\\n')
188+
const escaped = body.replace(/"/g, '\\"').replace(/\n/g, "\\n")
185189

186190
if (checklistCommentId) {
187-
// Update existing comment
188-
execSync(`gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/${REPO}/issues/comments/${checklistCommentId} -f "body=${escaped}"`, {
189-
encoding: "utf8",
190-
})
191+
execSync(
192+
`gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/${REPO}/issues/comments/${checklistCommentId} -f "body=${escaped}"`,
193+
{
194+
encoding: "utf8",
195+
},
196+
)
191197
} else {
192-
// Create new comment with specific author name
193-
const result = execSync(`gh api --method POST -H "Accept: application/vnd.github+json" -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" /repos/${REPO}/issues/${PR_NUMBER}/comments -f "body=${escaped}"`, {
194-
encoding: "utf8",
195-
})
198+
const result = execSync(
199+
`gh api --method POST -H "Accept: application/vnd.github+json" /repos/${REPO}/issues/${PR_NUMBER}/comments -f "body=${escaped}"`,
200+
{
201+
encoding: "utf8",
202+
},
203+
)
196204
const comment = JSON.parse(result.toString())
197205
checklistCommentId = comment.id
198206
}
@@ -203,7 +211,7 @@ async function postFinalReview(synthesis: string, providerList: string, confiden
203211
204212
**Tasks:**
205213
✅ Read repository conventions
206-
✅ Read modified files
214+
✅ Read modified files
207215
✅ Analyze security implications
208216
✅ Review code quality and conventions
209217
✅ Provide comprehensive feedback
@@ -213,33 +221,32 @@ ${synthesis}
213221
*Review generated by: ${providerList}*
214222
*Provider confidence scores: ${confidenceString}*`
215223

216-
const escaped = finalBody.replace(/"/g, '\\"').replace(/\n/g, '\\n')
217-
execSync(`gh api --method POST -H "Accept: application/vnd.github+json" /repos/${REPO}/issues/${PR_NUMBER}/comments -f "body=${escaped}"`, {
218-
encoding: "utf8",
219-
})
224+
const escaped = finalBody.replace(/"/g, '\\"').replace(/\n/g, "\\n")
225+
execSync(
226+
`gh api --method POST -H "Accept: application/vnd.github+json" /repos/${REPO}/issues/${PR_NUMBER}/comments -f "body=${escaped}"`,
227+
{
228+
encoding: "utf8",
229+
},
230+
)
220231
}
221232

222233
async function calculateConfidenceScores(reviews: ReviewResult[]): Promise<Record<string, number>> {
223234
const scores: Record<string, number> = {}
224235

225-
// Simple confidence scoring based on review length and content quality
226236
for (const review of reviews) {
227-
let score = 0.5 // Base score
237+
let score = 0.5
228238

229-
// Length factor (longer reviews tend to be more thorough)
230239
if (review.output.length > 2000) score += 0.2
231240
else if (review.output.length > 1000) score += 0.1
232241

233-
// Content quality indicators
234-
if (review.output.includes('security') || review.output.includes('performance')) score += 0.1
235-
if (review.output.includes('suggestion') || review.output.includes('recommend')) score += 0.1
236-
if (review.output.includes('line') || review.output.includes('file')) score += 0.1
242+
if (review.output.includes("security") || review.output.includes("performance")) score += 0.1
243+
if (review.output.includes("suggestion") || review.output.includes("recommend")) score += 0.1
244+
if (review.output.includes("line") || review.output.includes("file")) score += 0.1
237245

238-
// Provider-specific adjustments
239-
if (review.provider.includes('big-pickle')) score += 0.1 // Reasoning model
240-
if (review.provider.includes('grok-code')) score += 0.1 // Code-specialized
241-
if (review.provider.includes('minimax')) score += 0.05 // General purpose
242-
if (review.provider.includes('glm-4.7')) score += 0.05 // Balanced
246+
if (review.provider.includes("big-pickle")) score += 0.1
247+
if (review.provider.includes("grok-code")) score += 0.1
248+
if (review.provider.includes("minimax")) score += 0.05
249+
if (review.provider.includes("glm-4.7")) score += 0.05
243250

244251
scores[review.provider] = Math.min(Math.max(score, 0.1), 1.0)
245252
}
@@ -253,21 +260,35 @@ async function main() {
253260
const prompt = await buildPrompt()
254261
console.log(`Prompt built (${prompt.length} chars), includes AGENTS.md: ${INCLUDE_AGENTS}`)
255262

256-
const results = await Promise.all(REVIEW_PROVIDERS.map((provider) => runReview(provider, prompt)))
263+
await postOrUpdateChecklist("🔍 Reading modified files...", ["Read repository conventions"])
257264

258-
// Update checklist - analysis complete
259-
await postOrUpdateChecklist("🔒 Analyzing security implications...", ["Read repository conventions", "Read modified files", "Analyze security implications"])
265+
await postOrUpdateChecklist("🔍 Analyzing security implications...", [
266+
"Read repository conventions",
267+
"Read modified files",
268+
"Analyze security implications",
269+
])
260270

261-
// Update checklist - code review
262-
await postOrUpdateChecklist("🔍 Reviewing code quality and conventions...", ["Read repository conventions", "Read modified files", "Analyze security implications", "Review code quality and conventions"])
271+
const results = await Promise.all(REVIEW_PROVIDERS.map((provider) => runReview(provider, prompt)))
272+
273+
await postOrUpdateChecklist("🔍 Reviewing code quality and conventions...", [
274+
"Read repository conventions",
275+
"Read modified files",
276+
"Analyze security implications",
277+
"Review code quality and conventions",
278+
])
263279

264280
console.log("\nAll reviews completed. Synthesizing...")
265281

266-
// Update checklist - synthesizing
267-
await postOrUpdateChecklist("🤔 Providing comprehensive feedback...", ["Read repository conventions", "Read modified files", "Analyze security implications", "Review code quality and conventions", "Provide comprehensive feedback"])
282+
await postOrUpdateChecklist("🤔 Providing comprehensive feedback...", [
283+
"Read repository conventions",
284+
"Read modified files",
285+
"Analyze security implications",
286+
"Review code quality and conventions",
287+
"Provide comprehensive feedback",
288+
])
268289

269290
const synthesis = await synthesize(results)
270-
const providerList = results.map(r => r.provider).join(", ")
291+
const providerList = results.map((r) => r.provider).join(", ")
271292
const confidenceScores = await calculateConfidenceScores(results)
272293
const confidenceString = Object.entries(confidenceScores)
273294
.map(([provider, score]) => `${provider}: ${(score * 100).toFixed(0)}%`)
@@ -276,49 +297,11 @@ async function main() {
276297
console.log("\n=== SYNTHESIS COMPLETE ===\n")
277298
console.log(synthesis)
278299

279-
// Post final comprehensive review
280300
await postFinalReview(synthesis, providerList, confidenceString)
281301
console.log("\n✅ Final review posted to PR!")
282302
}
283303

284304
main().catch((error) => {
285305
console.error("Error:", error)
286306
process.exit(1)
287-
})
288-
} catch (e) {
289-
return `Error with ${provider}`
290-
}
291-
}
292-
293-
async function synthesize(reviews: string[]) {
294-
const combined = reviews.join("\n\n---\n\n")
295-
const synthesis = `Synthesize these code reviews into one comprehensive review. Combine overlapping feedback, highlight unique insights, remove duplicates:\n\n${combined}`
296-
try {
297-
return execSync(`opencode run -m opencode/big-pickle -- "${synthesis}"`, {
298-
encoding: "utf8",
299-
timeout: 180000,
300-
})
301-
} catch (e) {
302-
return combined
303-
}
304-
}
305-
306-
async function main() {
307-
console.log(`Running ${providers.length} parallel reviews...`)
308-
const results = await Promise.all(providers.map(runReview))
309-
console.log("Synthesizing results...")
310-
const final = await synthesize(results)
311-
console.log("\n=== SYNTHESIS ===\n" + final)
312-
313-
// Post as PR comment
314-
const escaped = final.replace(/"/g, '\\"').replace(/\n/g, "\\n")
315-
execSync(
316-
`gh api --method POST -H "Accept: application/vnd.github+json" /repos/${repo}/issues/${prNumber}/comments -f "body=${escaped}"`,
317-
{ encoding: "utf8" },
318-
)
319-
}
320-
321-
main().catch((e) => {
322-
console.error(e)
323-
process.exit(1)
324307
})

0 commit comments

Comments
 (0)