Skip to content

Commit d8c3808

Browse files
authored
Merge pull request #80 from mvanhorn/osc/75-multi-perspective-review
feat: add multi-perspective code review example
2 parents dc8cbe0 + 40f13a0 commit d8c3808

1 file changed

Lines changed: 193 additions & 0 deletions

File tree

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
/**
2+
* Multi-Perspective Code Review
3+
*
4+
* Demonstrates:
5+
* - Dependency chain: generator produces code, three reviewers depend on it
6+
* - Parallel execution: security, performance, and style reviewers run concurrently
7+
* - Shared memory: generator writes code, reviewers read it and write feedback,
8+
* synthesizer reads all feedback and produces a unified report
9+
*
10+
* Flow:
11+
* generator → [security-reviewer, performance-reviewer, style-reviewer] (parallel) → synthesizer
12+
*
13+
* Run:
14+
* npx tsx examples/multi-perspective-code-review.ts
15+
*
16+
* Prerequisites:
17+
* ANTHROPIC_API_KEY env var must be set.
18+
*/
19+
20+
import { OpenMultiAgent } from '../src/index.js'
21+
import type { AgentConfig, OrchestratorEvent } from '../src/types.js'
22+
23+
// ---------------------------------------------------------------------------
24+
// API spec to implement
25+
// ---------------------------------------------------------------------------
26+
27+
const API_SPEC = `POST /users endpoint that:
28+
- Accepts JSON body with name (string, required), email (string, required), age (number, optional)
29+
- Validates all fields
30+
- Inserts into a PostgreSQL database
31+
- Returns 201 with the created user or 400/500 on error`
32+
33+
// ---------------------------------------------------------------------------
34+
// Agents
35+
// ---------------------------------------------------------------------------
36+
37+
const generator: AgentConfig = {
38+
name: 'generator',
39+
model: 'claude-sonnet-4-6',
40+
systemPrompt: `You are a Node.js backend developer. Given an API spec, write a complete
41+
Express route handler. Include imports, validation, database query, and error handling.
42+
Store the generated code in shared memory under the key "generated_code".
43+
Write only the code, no explanation. Keep it under 80 lines.`,
44+
maxTurns: 2,
45+
}
46+
47+
const securityReviewer: AgentConfig = {
48+
name: 'security-reviewer',
49+
model: 'claude-sonnet-4-6',
50+
systemPrompt: `You are a security reviewer. Read the code from shared memory key
51+
"generated_code" and check for OWASP top 10 vulnerabilities: SQL injection, XSS,
52+
broken authentication, sensitive data exposure, etc. Write your findings as a
53+
markdown checklist. Store your review in shared memory under "security_review".
54+
Keep it to 150-200 words.`,
55+
maxTurns: 2,
56+
}
57+
58+
const performanceReviewer: AgentConfig = {
59+
name: 'performance-reviewer',
60+
model: 'claude-sonnet-4-6',
61+
systemPrompt: `You are a performance reviewer. Read the code from shared memory key
62+
"generated_code" and check for N+1 queries, memory leaks, blocking calls, missing
63+
connection pooling, and inefficient patterns. Write your findings as a markdown
64+
checklist. Store your review in shared memory under "performance_review".
65+
Keep it to 150-200 words.`,
66+
maxTurns: 2,
67+
}
68+
69+
const styleReviewer: AgentConfig = {
70+
name: 'style-reviewer',
71+
model: 'claude-sonnet-4-6',
72+
systemPrompt: `You are a code style reviewer. Read the code from shared memory key
73+
"generated_code" and check naming conventions, function structure, readability,
74+
error message clarity, and consistency. Write your findings as a markdown checklist.
75+
Store your review in shared memory under "style_review".
76+
Keep it to 150-200 words.`,
77+
maxTurns: 2,
78+
}
79+
80+
const synthesizer: AgentConfig = {
81+
name: 'synthesizer',
82+
model: 'claude-sonnet-4-6',
83+
systemPrompt: `You are a lead engineer synthesizing code review feedback. Read all
84+
reviews from shared memory (security_review, performance_review, style_review) and
85+
the original code (generated_code). Produce a unified report with:
86+
87+
1. Critical issues (must fix before merge)
88+
2. Recommended improvements (should fix)
89+
3. Minor suggestions (nice to have)
90+
91+
Deduplicate overlapping feedback. Keep the report to 200-300 words.`,
92+
maxTurns: 2,
93+
}
94+
95+
// ---------------------------------------------------------------------------
96+
// Orchestrator + team
97+
// ---------------------------------------------------------------------------
98+
99+
function handleProgress(event: OrchestratorEvent): void {
100+
if (event.type === 'task_start') {
101+
console.log(` [START] ${event.task ?? '?'}${event.agent ?? '?'}`)
102+
}
103+
if (event.type === 'task_complete') {
104+
const success = (event.data as { success?: boolean })?.success ?? true
105+
console.log(` [DONE] ${event.task ?? '?'} (${success ? 'OK' : 'FAIL'})`)
106+
}
107+
}
108+
109+
const orchestrator = new OpenMultiAgent({
110+
defaultModel: 'claude-sonnet-4-6',
111+
onProgress: handleProgress,
112+
})
113+
114+
const team = orchestrator.createTeam('code-review-team', {
115+
name: 'code-review-team',
116+
agents: [generator, securityReviewer, performanceReviewer, styleReviewer, synthesizer],
117+
sharedMemory: true,
118+
})
119+
120+
// ---------------------------------------------------------------------------
121+
// Tasks
122+
// ---------------------------------------------------------------------------
123+
124+
const tasks = [
125+
{
126+
title: 'Generate code',
127+
description: `Write a Node.js Express route handler for this API spec:\n\n${API_SPEC}\n\nStore the complete code in shared memory as "generated_code".`,
128+
assignee: 'generator',
129+
},
130+
{
131+
title: 'Security review',
132+
description: 'Read "generated_code" from shared memory and perform a security review. Store findings in shared memory as "security_review".',
133+
assignee: 'security-reviewer',
134+
dependsOn: ['Generate code'],
135+
},
136+
{
137+
title: 'Performance review',
138+
description: 'Read "generated_code" from shared memory and perform a performance review. Store findings in shared memory as "performance_review".',
139+
assignee: 'performance-reviewer',
140+
dependsOn: ['Generate code'],
141+
},
142+
{
143+
title: 'Style review',
144+
description: 'Read "generated_code" from shared memory and perform a style review. Store findings in shared memory as "style_review".',
145+
assignee: 'style-reviewer',
146+
dependsOn: ['Generate code'],
147+
},
148+
{
149+
title: 'Synthesize feedback',
150+
description: 'Read all reviews (security_review, performance_review, style_review) and the original generated_code from shared memory. Produce a unified, prioritized action item report.',
151+
assignee: 'synthesizer',
152+
dependsOn: ['Security review', 'Performance review', 'Style review'],
153+
},
154+
]
155+
156+
// ---------------------------------------------------------------------------
157+
// Run
158+
// ---------------------------------------------------------------------------
159+
160+
console.log('Multi-Perspective Code Review')
161+
console.log('='.repeat(60))
162+
console.log(`Spec: ${API_SPEC.split('\n')[0]}`)
163+
console.log('Pipeline: generator → 3 reviewers (parallel) → synthesizer')
164+
console.log('='.repeat(60))
165+
console.log()
166+
167+
const result = await orchestrator.runTasks(team, tasks)
168+
169+
// ---------------------------------------------------------------------------
170+
// Output
171+
// ---------------------------------------------------------------------------
172+
173+
console.log('\n' + '='.repeat(60))
174+
console.log(`Overall success: ${result.success}`)
175+
console.log(`Tokens — input: ${result.totalTokenUsage.input_tokens}, output: ${result.totalTokenUsage.output_tokens}`)
176+
console.log()
177+
178+
for (const [name, r] of result.agentResults) {
179+
const icon = r.success ? 'OK ' : 'FAIL'
180+
const tokens = `in:${r.tokenUsage.input_tokens} out:${r.tokenUsage.output_tokens}`
181+
console.log(` [${icon}] ${name.padEnd(22)} ${tokens}`)
182+
}
183+
184+
const synthResult = result.agentResults.get('synthesizer')
185+
if (synthResult?.success) {
186+
console.log('\n' + '='.repeat(60))
187+
console.log('UNIFIED REVIEW REPORT')
188+
console.log('='.repeat(60))
189+
console.log()
190+
console.log(synthResult.output)
191+
}
192+
193+
console.log('\nDone.')

0 commit comments

Comments
 (0)