Skip to content

Commit 310bc03

Browse files
Merge branch 'feature/agent-orchestration-system'
2 parents 7f88491 + f9ef14b commit 310bc03

22 files changed

Lines changed: 5771 additions & 2284 deletions

File tree

docs/plans/2025-11-17-agent-orchestration-mvp.md

Lines changed: 1787 additions & 0 deletions
Large diffs are not rendered by default.

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module.exports = {
1818
coverageReporters: ['text', 'lcov', 'html'],
1919
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
2020
testTimeout: 10000,
21-
moduleNameMapping: {
21+
moduleNameMapper: {
2222
'^@/(.*)$': '<rootDir>/src/$1'
2323
}
2424
};

package-lock.json

Lines changed: 2461 additions & 2282 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
"test:e2e:debug": "playwright test --debug",
2020
"test:e2e:report": "playwright show-report",
2121
"test:e2e:comprehensive": "tsx scripts/run-comprehensive-e2e.ts",
22-
"test:e2e:validate": "playwright test tests/e2e/validation/comprehensive-validation.spec.ts"
22+
"test:e2e:validate": "playwright test tests/e2e/validation/comprehensive-validation.spec.ts",
23+
"orchestrator:use": "tsx scripts/set-orchestrator.ts"
2324
},
2425
"dependencies": {
2526
"@anthropic-ai/sdk": "^0.68.0",

scripts/set-orchestrator.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#!/usr/bin/env node
2+
import fs from 'fs'
3+
import path from 'path'
4+
5+
const validModels = [
6+
'claude-sonnet-4.5',
7+
'claude-opus-4',
8+
'deepseek-r1',
9+
'kimi-k1.5',
10+
'llama-3.3-70b',
11+
'qwen-2.5-coder-32b'
12+
]
13+
14+
const model = process.argv[2]
15+
16+
if (!model) {
17+
console.error('Usage: npm run orchestrator:use <model>')
18+
console.log('Available models:')
19+
validModels.forEach(m => console.log(` - ${m}`))
20+
process.exit(1)
21+
}
22+
23+
if (!validModels.includes(model)) {
24+
console.error(`Invalid model: ${model}`)
25+
console.log('Available models:')
26+
validModels.forEach(m => console.log(` - ${m}`))
27+
process.exit(1)
28+
}
29+
30+
// Update .env file
31+
const envPath = path.join(process.cwd(), '.env')
32+
const envExamplePath = path.join(process.cwd(), '.env.example')
33+
34+
let envContent = ''
35+
36+
if (fs.existsSync(envPath)) {
37+
envContent = fs.readFileSync(envPath, 'utf-8')
38+
} else if (fs.existsSync(envExamplePath)) {
39+
envContent = fs.readFileSync(envExamplePath, 'utf-8')
40+
}
41+
42+
// Update or add ORCHESTRATOR_MODEL
43+
const modelRegex = /^ORCHESTRATOR_MODEL=.*/m
44+
if (modelRegex.test(envContent)) {
45+
envContent = envContent.replace(modelRegex, `ORCHESTRATOR_MODEL=${model}`)
46+
} else {
47+
envContent += `\nORCHESTRATOR_MODEL=${model}\n`
48+
}
49+
50+
fs.writeFileSync(envPath, envContent)
51+
52+
console.log(`✅ Orchestrator model set to: ${model}`)
53+
console.log(`📝 Updated .env file`)
54+
console.log(`🔄 Restart your dev server to apply changes`)

src/agents/BackendDeveloper.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { BaseAgent } from './BaseAgent'
2+
import { ProjectWorkspace } from '@/services/workspace/ProjectWorkspace'
3+
import { AgentOutput, ProjectContext } from '@/types/orchestrator'
4+
5+
export interface BackendDeveloperContext {
6+
projectId: string
7+
userRequest: string
8+
workspace: ProjectWorkspace
9+
architecture?: any
10+
}
11+
12+
export interface BackendDeveloperOutput {
13+
filesCreated: string[]
14+
filesModified: string[]
15+
cost: number
16+
duration: number
17+
}
18+
19+
export class BackendDeveloper extends BaseAgent {
20+
agentType = 'BackendDeveloper' as const
21+
22+
private workspace: ProjectWorkspace
23+
private userRequest: string
24+
private architecture?: any
25+
26+
constructor(context: BackendDeveloperContext) {
27+
// Create a minimal ProjectContext for BaseAgent
28+
const projectContext: ProjectContext = {
29+
state: {
30+
userRequest: context.userRequest,
31+
userId: 'test-user',
32+
organizationId: 'test-org',
33+
projectId: context.projectId,
34+
projectName: context.projectId,
35+
createdAt: new Date().toISOString(),
36+
agentsSpawned: [],
37+
agentOutputs: {},
38+
errors: [],
39+
retryCount: 0,
40+
},
41+
organizationId: 'test-org',
42+
userId: 'test-user',
43+
costOptimizerUrl: process.env.COST_OPTIMIZER_URL || 'http://localhost:3001',
44+
costOptimizerApiKey: process.env.COST_OPTIMIZER_API_KEY || 'test-key',
45+
}
46+
47+
super('BackendDeveloper', projectContext)
48+
this.workspace = context.workspace
49+
this.userRequest = context.userRequest
50+
this.architecture = context.architecture
51+
}
52+
53+
async execute(): Promise<AgentOutput> {
54+
const startTime = Date.now()
55+
56+
try {
57+
// TODO: Load skills (test-driven-development, api-design-patterns, security-best-practices)
58+
// TODO: Use MCP tools (context7, sequential-thinking, supabase)
59+
60+
// For MVP: Generate simple API file structure
61+
const prompt = this.buildPrompt()
62+
const response = await this.think({ prompt, complexity: 'simple' })
63+
64+
// Parse response and generate files
65+
const files = await this.generateFiles(response)
66+
67+
this.output.duration = Date.now() - startTime
68+
69+
return this.getOutput()
70+
} catch (error) {
71+
console.error('[BackendDeveloper] Error:', error)
72+
this.addError(`BackendDeveloper execution failed: ${error}`)
73+
this.output.duration = Date.now() - startTime
74+
return this.getOutput()
75+
}
76+
}
77+
78+
private buildPrompt(): string {
79+
return `You are a backend developer agent. Generate TypeScript files for the following request:
80+
81+
User Request: ${this.userRequest}
82+
83+
${this.architecture ? `Architecture Context:\n${JSON.stringify(this.architecture, null, 2)}\n` : ''}
84+
85+
Generate a simple API structure with:
86+
1. API route file (app/api/[resource]/route.ts)
87+
2. Service layer file (src/services/[resource]Service.ts)
88+
3. Type definitions (src/types/[resource].ts)
89+
90+
Return a JSON array of files:
91+
[
92+
{
93+
"path": "app/api/todos/route.ts",
94+
"content": "import { NextRequest, NextResponse } from 'next/server'..."
95+
},
96+
{
97+
"path": "src/services/todoService.ts",
98+
"content": "export class TodoService { ... }"
99+
}
100+
]
101+
102+
Keep it simple and focused. Use Next.js 15 patterns.`
103+
}
104+
105+
private async generateFiles(response: string): Promise<string[]> {
106+
try {
107+
// Try to parse JSON response
108+
const files = JSON.parse(response)
109+
const createdFiles: string[] = []
110+
111+
for (const file of files) {
112+
await this.workspace.writeFile(file.path, file.content)
113+
createdFiles.push(file.path)
114+
115+
// Track file creation using BaseAgent method
116+
this.addFileCreated(file.path)
117+
}
118+
119+
return createdFiles
120+
} catch (error) {
121+
// If JSON parsing fails, create a single example file
122+
console.warn('[BackendDeveloper] Failed to parse response, creating example file')
123+
124+
const exampleFile = 'src/example-generated.ts'
125+
await this.workspace.writeFile(exampleFile, `// Generated by BackendDeveloper\n// Request: ${this.userRequest}\n\nexport const placeholder = true;`)
126+
127+
this.addFileCreated(exampleFile)
128+
return [exampleFile]
129+
}
130+
}
131+
}

src/agents/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { BaseAgent } from './BaseAgent'
2+
export { CodeArchitect } from './CodeArchitect'
3+
export { BackendDeveloper } from './BackendDeveloper'
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { NextRequest, NextResponse } from 'next/server'
2+
import { AgentOrchestrator } from '@/orchestrator/AgentOrchestrator'
3+
import { CodebaseReview } from '@/types/events'
4+
5+
export async function POST(request: NextRequest) {
6+
try {
7+
// Parse request body
8+
const body = await request.json()
9+
const { projectPath } = body
10+
11+
// Validation
12+
if (!projectPath || typeof projectPath !== 'string') {
13+
return NextResponse.json(
14+
{ error: 'projectPath is required and must be a string' },
15+
{ status: 400 }
16+
)
17+
}
18+
19+
// Get orchestrator instance
20+
const orchestrator = AgentOrchestrator.getInstance()
21+
22+
// Trigger review
23+
const review: CodebaseReview = await orchestrator.reviewCodebase(projectPath)
24+
25+
// Return result
26+
return NextResponse.json(
27+
{
28+
success: true,
29+
data: review
30+
},
31+
{ status: 200 }
32+
)
33+
} catch (error) {
34+
console.error('❌ Review API error:', error)
35+
36+
return NextResponse.json(
37+
{
38+
success: false,
39+
error: error instanceof Error ? error.message : 'Unknown error occurred'
40+
},
41+
{ status: 500 }
42+
)
43+
}
44+
}
45+
46+
// Optional: GET endpoint to check API health
47+
export async function GET(request: NextRequest) {
48+
return NextResponse.json({
49+
status: 'ok',
50+
endpoint: '/api/orchestrator/review',
51+
methods: ['POST'],
52+
description: 'Trigger codebase review and analysis'
53+
})
54+
}

0 commit comments

Comments
 (0)