arcadiasuite/server/blackboard/agents/GeneratorAgent.ts

188 lines
5.9 KiB
TypeScript

/**
* Arcadia Suite - Generator Agent
*
* Agente responsável por gerar código a partir de especificações.
* Lê arquivos existentes para manter consistência.
*
* @author Arcadia Development Team
* @version 2.0.0
*/
import { BaseBlackboardAgent, type AgentConfig } from "../BaseBlackboardAgent";
import { blackboardService } from "../service";
import { type BlackboardTask } from "@shared/schema";
import { toolManager } from "../../autonomous/tools";
const SYSTEM_PROMPT = `Você é o Agente Gerador de Código do Arcadia Suite.
## Seu Papel
Você gera código TypeScript/React de alta qualidade baseado em especificações técnicas.
Você pode ler arquivos existentes para manter consistência de estilo.
## Stack do Arcadia Suite
- Backend: Node.js, Express, TypeScript
- Frontend: React 18, TypeScript, Tailwind CSS, shadcn/ui
- Banco: PostgreSQL com Drizzle ORM
- Autenticação: Passport.js com sessões
- Estado: TanStack Query (React Query)
## Padrões de Código
1. Sempre use TypeScript com tipos estritos
2. Use componentes funcionais com hooks
3. Siga o padrão existente de rotas Express
4. Use Drizzle ORM para queries
5. Use shadcn/ui para componentes de UI
6. Adicione data-testid em elementos interativos
## Formato de Saída
Para cada arquivo, retorne JSON:
{
"files": [
{
"path": "caminho/completo/arquivo.ts",
"content": "código completo do arquivo",
"type": "schema|route|component|hook|util",
"action": "create|modify"
}
]
}`;
export class GeneratorAgent extends BaseBlackboardAgent {
constructor() {
const config: AgentConfig = {
name: "generator",
displayName: "Agente Gerador de Código",
description: "Gera código TypeScript/React a partir de especificações",
systemPrompt: SYSTEM_PROMPT,
capabilities: [
"Geração de schemas Drizzle",
"Criação de rotas Express",
"Componentes React/TypeScript",
"Leitura de código existente",
"Manutenção de consistência"
],
pollInterval: 2000
};
super(config);
}
canHandle(task: BlackboardTask): boolean {
const context = task.context as any;
return context?.phase === "codegen" || task.assignedAgent === "generator";
}
async process(task: BlackboardTask): Promise<void> {
await this.log(task.id, "thinking", "Buscando especificação e contexto...");
const specArtifact = await blackboardService.getLatestArtifact(task.id, "spec");
const contextArtifact = await blackboardService.getLatestArtifact(task.id, "doc");
if (!specArtifact) {
await blackboardService.failTask(task.id, "generator", "Especificação não encontrada");
return;
}
let spec: any;
try {
spec = JSON.parse(specArtifact.content || "{}");
} catch {
spec = { raw: specArtifact.content };
}
await this.log(task.id, "reading", "Lendo arquivos de referência...");
let schemaRef = "";
const schemaResult = await toolManager.execute("read_file", { path: "shared/schema.ts", startLine: 1, endLine: 100 });
if (schemaResult.success) {
schemaRef = `\nREFERÊNCIA - shared/schema.ts (primeiras 100 linhas):\n\`\`\`typescript\n${schemaResult.data?.content?.slice(0, 2000)}\n\`\`\``;
}
let routesRef = "";
const routesResult = await toolManager.execute("read_file", { path: "server/routes.ts", startLine: 1, endLine: 50 });
if (routesResult.success) {
routesRef = `\nREFERÊNCIA - server/routes.ts (primeiras 50 linhas):\n\`\`\`typescript\n${routesResult.data?.content?.slice(0, 1500)}\n\`\`\``;
}
await this.log(task.id, "generating", "Gerando código baseado na especificação...");
const prompt = `Com base nesta especificação, gere o código completo:
ESPECIFICAÇÃO:
${JSON.stringify(spec, null, 2)}
CONTEXTO DO PROJETO:
${contextArtifact?.content?.slice(0, 2000) || "Não disponível"}
${schemaRef}
${routesRef}
Gere arquivos completos e funcionais para:
1. Schema do banco de dados (se necessário) - adicionar ao shared/schema.ts
2. Rotas da API - novo arquivo em server/ ou adicionar a routes.ts
3. Componente principal React - em client/src/pages/
Retorne em formato JSON com a estrutura:
{
"files": [
{ "path": "caminho/completo.ts", "content": "código completo", "type": "tipo", "action": "create|modify" }
]
}
IMPORTANTE: Gere código completo e funcional, não use placeholders ou "// TODO".`;
try {
const response = await this.generateWithAI(prompt);
let files: any[] = [];
try {
const jsonMatch = response.match(/\{[\s\S]*\}/);
if (jsonMatch) {
const parsed = JSON.parse(jsonMatch[0]);
files = parsed.files || [];
}
} catch {
files = [{
path: `server/modules/${spec.moduleName || "generated"}/index.ts`,
content: response,
type: "module",
action: "create"
}];
}
for (const file of files) {
await blackboardService.addArtifact(
task.id,
"code",
file.path,
file.content,
"generator",
{ type: file.type, action: file.action }
);
}
await this.log(task.id, "completed", `Gerados ${files.length} arquivos`);
const mainTask = await blackboardService.getMainTask(task.id);
if (mainTask) {
await blackboardService.createSubtask(
mainTask.id,
"Validar código",
"Validar e testar o código gerado",
"validator",
[task.id],
{ phase: "validation" }
);
}
await blackboardService.completeTask(task.id, "generator", {
filesGenerated: files.length,
files: files.map(f => ({ path: f.path, type: f.type, action: f.action })),
});
} catch (error: any) {
await blackboardService.failTask(task.id, "generator", error.message);
}
}
}
export const generatorAgent = new GeneratorAgent();