wegho-agentes 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/.agents/AGENT_WORKFLOW.md +528 -0
  2. package/.agents/AI_COMPATIBILITY.md +332 -0
  3. package/.agents/CLI.md +222 -0
  4. package/.agents/README.md +130 -0
  5. package/.agents/cli.js +389 -0
  6. package/.agents/code-auditor-agent.ts +333 -0
  7. package/.agents/config.ts +145 -0
  8. package/.agents/context-loader.ts +300 -0
  9. package/.agents/core/agent-parallelizer.test.ts +94 -0
  10. package/.agents/core/agent-parallelizer.ts +108 -0
  11. package/.agents/core/architecture-agent.ts +237 -0
  12. package/.agents/core/base-agent.ts +311 -0
  13. package/.agents/core/cache-manager.test.ts +147 -0
  14. package/.agents/core/cache-manager.ts +184 -0
  15. package/.agents/core/documentation-agent.ts +183 -0
  16. package/.agents/core/feedback-collector.ts +207 -0
  17. package/.agents/core/frontend-agent.ts +210 -0
  18. package/.agents/core/inventory-agent.ts +582 -0
  19. package/.agents/core/memory-system.ts +397 -0
  20. package/.agents/core/quality-agent.ts +268 -0
  21. package/.agents/core/retry-utility.test.ts +165 -0
  22. package/.agents/core/retry-utility.ts +140 -0
  23. package/.agents/core/security-agent.ts +217 -0
  24. package/.agents/core/workflow-validator.test.ts +171 -0
  25. package/.agents/core/workflow-validator.ts +158 -0
  26. package/.agents/domains/README.md +53 -0
  27. package/.agents/domains/logistics/route-agent.ts +177 -0
  28. package/.agents/domains/news/cms-agent.ts +158 -0
  29. package/.agents/domains/news/seo-agent.ts +170 -0
  30. package/.agents/domains/production/production-control-agent.ts +169 -0
  31. package/.agents/example-learning-system.js +118 -0
  32. package/.agents/init.ts +164 -0
  33. package/.agents/memory/architecture-agent/failures.json +1 -0
  34. package/.agents/memory/architecture-agent/learnings.json +1 -0
  35. package/.agents/memory/architecture-agent/specialty.md +31 -0
  36. package/.agents/memory/architecture-agent/successes.json +16 -0
  37. package/.agents/memory/cms-agent/failures.json +1 -0
  38. package/.agents/memory/cms-agent/learnings.json +1 -0
  39. package/.agents/memory/cms-agent/specialty.md +30 -0
  40. package/.agents/memory/cms-agent/successes.json +16 -0
  41. package/.agents/memory/documentation-agent/failures.json +1 -0
  42. package/.agents/memory/documentation-agent/learnings.json +1 -0
  43. package/.agents/memory/documentation-agent/specialty.md +33 -0
  44. package/.agents/memory/documentation-agent/successes.json +16 -0
  45. package/.agents/memory/frontend-agent/failures.json +1 -0
  46. package/.agents/memory/frontend-agent/learnings.json +1 -0
  47. package/.agents/memory/frontend-agent/specialty.md +30 -0
  48. package/.agents/memory/frontend-agent/successes.json +16 -0
  49. package/.agents/memory/inventory-agent/failures.json +1 -0
  50. package/.agents/memory/inventory-agent/inventory/index.json +8 -0
  51. package/.agents/memory/inventory-agent/inventory/types.json +77716 -0
  52. package/.agents/memory/inventory-agent/inventory/variables.json +405 -0
  53. package/.agents/memory/inventory-agent/learnings.json +1 -0
  54. package/.agents/memory/inventory-agent/specialty.md +129 -0
  55. package/.agents/memory/inventory-agent/successes.json +30 -0
  56. package/.agents/memory/production-control-agent/failures.json +1 -0
  57. package/.agents/memory/production-control-agent/learnings.json +1 -0
  58. package/.agents/memory/production-control-agent/specialty.md +29 -0
  59. package/.agents/memory/production-control-agent/successes.json +16 -0
  60. package/.agents/memory/quality-agent/failures.json +16 -0
  61. package/.agents/memory/quality-agent/learnings.json +1 -0
  62. package/.agents/memory/quality-agent/specialty.md +31 -0
  63. package/.agents/memory/quality-agent/successes.json +1 -0
  64. package/.agents/memory/reference-repositories.json +271 -0
  65. package/.agents/memory/route-agent/failures.json +1 -0
  66. package/.agents/memory/route-agent/learnings.json +1 -0
  67. package/.agents/memory/route-agent/specialty.md +29 -0
  68. package/.agents/memory/route-agent/successes.json +16 -0
  69. package/.agents/memory/security-agent/failures.json +1 -0
  70. package/.agents/memory/security-agent/learnings.json +1 -0
  71. package/.agents/memory/security-agent/specialty.md +31 -0
  72. package/.agents/memory/security-agent/successes.json +16 -0
  73. package/.agents/memory/seo-agent/failures.json +1 -0
  74. package/.agents/memory/seo-agent/learnings.json +1 -0
  75. package/.agents/memory/seo-agent/specialty.md +31 -0
  76. package/.agents/memory/seo-agent/successes.json +16 -0
  77. package/.agents/orchestrator.ts +438 -0
  78. package/.agents/project-discovery-agent.ts +342 -0
  79. package/.agents/security/pentesting-agent.py +387 -0
  80. package/.agents/security/python-bridge.ts +193 -0
  81. package/.agents/security/vulnerability-db.json +201 -0
  82. package/.agents/task-analyzer-agent.ts +346 -0
  83. package/.agents/test-init-context.js +67 -0
  84. package/INSTALL.md +300 -0
  85. package/LICENSE +21 -0
  86. package/README.md +315 -0
  87. package/docs/AGENT_RULES.md +292 -0
  88. package/docs/BUILD_HISTORY.md +65 -0
  89. package/docs/DESIGN_SYSTEM.md +256 -0
  90. package/docs/LEARNING_SYSTEM.md +326 -0
  91. package/docs/SYMBOLS_TREE.md +182 -0
  92. package/docs/VERSION.md +6 -0
  93. package/docs/architecture.md +111 -0
  94. package/package.json +60 -0
@@ -0,0 +1,397 @@
1
+ /**
2
+ * Memory System - Sistema de Memória para Agentes
3
+ *
4
+ * Responsabilidades:
5
+ * - Gerenciar memória persistente de cada agente
6
+ * - Registrar sucessos e falhas
7
+ * - Armazenar aprendizados
8
+ * - Buscar casos similares
9
+ * - Fornecer recomendações baseadas em histórico
10
+ */
11
+
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+ import { CacheManager } from './cache-manager';
15
+
16
+ /**
17
+ * Entrada individual de memória
18
+ */
19
+ export interface MemoryEntry {
20
+ id: string;
21
+ timestamp: Date;
22
+ taskDescription: string;
23
+ context: {
24
+ files: string[];
25
+ areas: string[];
26
+ complexity: 'low' | 'medium' | 'high';
27
+ };
28
+ result: 'success' | 'failure';
29
+ details: string;
30
+ userFeedback?: {
31
+ satisfied: boolean;
32
+ likes: string[];
33
+ dislikes: string[];
34
+ suggestions: string[];
35
+ };
36
+ metadata?: Record<string, any>;
37
+ }
38
+
39
+ /**
40
+ * Padrão aprendido
41
+ */
42
+ export interface Learning {
43
+ id: string;
44
+ pattern: string;
45
+ description: string;
46
+ recommendation: string;
47
+ confidence: number; // 0-1
48
+ occurrences: number;
49
+ lastSeen: Date;
50
+ examples: string[]; // IDs de MemoryEntry
51
+ }
52
+
53
+ /**
54
+ * Estrutura de memória de um agente
55
+ */
56
+ export interface AgentMemory {
57
+ agentName: string;
58
+ successes: MemoryEntry[];
59
+ failures: MemoryEntry[];
60
+ learnings: Learning[];
61
+ stats: {
62
+ totalTasks: number;
63
+ successRate: number;
64
+ lastUpdated: Date;
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Sistema de Memória
70
+ */
71
+ export class MemorySystem {
72
+ private memoryBasePath: string;
73
+ private maxEntries: number;
74
+ private cache: CacheManager;
75
+
76
+ constructor(memoryBasePath: string = '.agents/memory', maxEntries: number = 1000) {
77
+ this.memoryBasePath = memoryBasePath;
78
+ this.maxEntries = maxEntries;
79
+ this.cache = new CacheManager(300000, 500); // 5 min TTL, max 500 entries
80
+ this.ensureMemoryStructure();
81
+ }
82
+
83
+ /**
84
+ * Garante que a estrutura de pastas existe
85
+ */
86
+ private ensureMemoryStructure(): void {
87
+ if (!fs.existsSync(this.memoryBasePath)) {
88
+ fs.mkdirSync(this.memoryBasePath, { recursive: true });
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Garante que a pasta do agente existe
94
+ */
95
+ private ensureAgentFolder(agentName: string): string {
96
+ const agentPath = path.join(this.memoryBasePath, agentName);
97
+ if (!fs.existsSync(agentPath)) {
98
+ fs.mkdirSync(agentPath, { recursive: true });
99
+ }
100
+ return agentPath;
101
+ }
102
+
103
+ /**
104
+ * Carrega memória de um agente
105
+ */
106
+ loadMemory(agentName: string): AgentMemory {
107
+ const agentPath = this.ensureAgentFolder(agentName);
108
+
109
+ const successesPath = path.join(agentPath, 'successes.json');
110
+ const failuresPath = path.join(agentPath, 'failures.json');
111
+ const learningsPath = path.join(agentPath, 'learnings.json');
112
+
113
+ const successes = this.loadJsonFile<MemoryEntry[]>(successesPath, []);
114
+ const failures = this.loadJsonFile<MemoryEntry[]>(failuresPath, []);
115
+ const learnings = this.loadJsonFile<Learning[]>(learningsPath, []);
116
+
117
+ const totalTasks = successes.length + failures.length;
118
+ const successRate = totalTasks > 0 ? successes.length / totalTasks : 0;
119
+
120
+ return {
121
+ agentName,
122
+ successes,
123
+ failures,
124
+ learnings,
125
+ stats: {
126
+ totalTasks,
127
+ successRate,
128
+ lastUpdated: new Date(),
129
+ },
130
+ };
131
+ }
132
+
133
+ /**
134
+ * Salva memória de um agente
135
+ */
136
+ saveMemory(memory: AgentMemory): void {
137
+ const agentPath = this.ensureAgentFolder(memory.agentName);
138
+
139
+ // Limitar número de entradas
140
+ const successes = this.limitEntries(memory.successes);
141
+ const failures = this.limitEntries(memory.failures);
142
+
143
+ this.saveJsonFile(path.join(agentPath, 'successes.json'), successes);
144
+ this.saveJsonFile(path.join(agentPath, 'failures.json'), failures);
145
+ this.saveJsonFile(path.join(agentPath, 'learnings.json'), memory.learnings);
146
+ }
147
+
148
+ /**
149
+ * Registra um sucesso
150
+ */
151
+ recordSuccess(
152
+ agentName: string,
153
+ taskDescription: string,
154
+ context: MemoryEntry['context'],
155
+ details: string,
156
+ userFeedback?: MemoryEntry['userFeedback']
157
+ ): void {
158
+ const memory = this.loadMemory(agentName);
159
+
160
+ const entry: MemoryEntry = {
161
+ id: this.generateId(),
162
+ timestamp: new Date(),
163
+ taskDescription,
164
+ context,
165
+ result: 'success',
166
+ details,
167
+ userFeedback,
168
+ };
169
+
170
+ memory.successes.push(entry);
171
+ this.saveMemory(memory);
172
+
173
+ console.log(`✅ [${agentName}] Sucesso registrado: ${taskDescription}`);
174
+ }
175
+
176
+ /**
177
+ * Registra uma falha
178
+ */
179
+ recordFailure(
180
+ agentName: string,
181
+ taskDescription: string,
182
+ context: MemoryEntry['context'],
183
+ details: string,
184
+ userFeedback?: MemoryEntry['userFeedback']
185
+ ): void {
186
+ const memory = this.loadMemory(agentName);
187
+
188
+ const entry: MemoryEntry = {
189
+ id: this.generateId(),
190
+ timestamp: new Date(),
191
+ taskDescription,
192
+ context,
193
+ result: 'failure',
194
+ details,
195
+ userFeedback,
196
+ };
197
+
198
+ memory.failures.push(entry);
199
+ this.saveMemory(memory);
200
+
201
+ console.log(`❌ [${agentName}] Falha registrada: ${taskDescription}`);
202
+ }
203
+
204
+ /**
205
+ * Adiciona um aprendizado
206
+ */
207
+ addLearning(
208
+ agentName: string,
209
+ pattern: string,
210
+ description: string,
211
+ recommendation: string,
212
+ exampleIds: string[] = []
213
+ ): void {
214
+ const memory = this.loadMemory(agentName);
215
+
216
+ // Verificar se já existe
217
+ const existing = memory.learnings.find(l => l.pattern === pattern);
218
+
219
+ if (existing) {
220
+ existing.occurrences++;
221
+ existing.lastSeen = new Date();
222
+ existing.confidence = Math.min(1, existing.confidence + 0.1);
223
+ existing.examples.push(...exampleIds);
224
+ } else {
225
+ const learning: Learning = {
226
+ id: this.generateId(),
227
+ pattern,
228
+ description,
229
+ recommendation,
230
+ confidence: 0.5,
231
+ occurrences: 1,
232
+ lastSeen: new Date(),
233
+ examples: exampleIds,
234
+ };
235
+ memory.learnings.push(learning);
236
+ }
237
+
238
+ this.saveMemory(memory);
239
+ console.log(`🧠 [${agentName}] Aprendizado adicionado: ${pattern}`);
240
+ }
241
+
242
+ /**
243
+ * Busca casos similares
244
+ */
245
+ getSimilarCases(
246
+ agentName: string,
247
+ taskDescription: string,
248
+ limit: number = 5
249
+ ): MemoryEntry[] {
250
+ // Verificar cache primeiro
251
+ const cacheKey = `similar:${agentName}:${taskDescription}:${limit}`;
252
+ const cached = this.cache.get<MemoryEntry[]>(cacheKey);
253
+
254
+ if (cached) {
255
+ return cached;
256
+ }
257
+
258
+ // Cache miss - buscar do disco
259
+ const memory = this.loadMemory(agentName);
260
+ const allEntries = [...memory.successes, ...memory.failures];
261
+
262
+ // Busca simples por palavras-chave
263
+ const keywords = this.extractKeywords(taskDescription);
264
+
265
+ const scored = allEntries.map(entry => {
266
+ const entryKeywords = this.extractKeywords(entry.taskDescription);
267
+ const score = this.calculateSimilarity(keywords, entryKeywords);
268
+ return { entry, score };
269
+ });
270
+
271
+ const results = scored
272
+ .sort((a, b) => b.score - a.score)
273
+ .slice(0, limit)
274
+ .filter(s => s.score > 0.3)
275
+ .map(s => s.entry);
276
+
277
+ // Cachear resultado
278
+ this.cache.set(cacheKey, results, 60000); // 1 minuto TTL
279
+
280
+ return results;
281
+ }
282
+
283
+ /**
284
+ * Obtém recomendações baseadas em aprendizados
285
+ */
286
+ getRecommendations(agentName: string, taskDescription: string): string[] {
287
+ // Verificar cache primeiro
288
+ const cacheKey = `recommendations:${agentName}:${taskDescription}`;
289
+ const cached = this.cache.get<string[]>(cacheKey);
290
+
291
+ if (cached) {
292
+ return cached;
293
+ }
294
+
295
+ // Cache miss - buscar do disco
296
+ const memory = this.loadMemory(agentName);
297
+ const keywords = this.extractKeywords(taskDescription);
298
+
299
+ const results = memory.learnings
300
+ .filter(learning => {
301
+ const patternKeywords = this.extractKeywords(learning.pattern);
302
+ const similarity = this.calculateSimilarity(keywords, patternKeywords);
303
+ return similarity > 0.3 && learning.confidence > 0.5;
304
+ })
305
+ .sort((a, b) => b.confidence - a.confidence)
306
+ .map(l => l.recommendation);
307
+
308
+ // Cachear resultado
309
+ this.cache.set(cacheKey, results, 120000); // 2 minutos TTL
310
+
311
+ return results;
312
+ }
313
+
314
+ /**
315
+ * Obtém estatísticas do cache
316
+ */
317
+ getCacheStats() {
318
+ return this.cache.getStats();
319
+ }
320
+
321
+ /**
322
+ * Limpa o cache
323
+ */
324
+ clearCache(): void {
325
+ this.cache.clear();
326
+ }
327
+
328
+ /**
329
+ * Obtém estatísticas de um agente
330
+ */
331
+ getStats(agentName: string): AgentMemory['stats'] {
332
+ const memory = this.loadMemory(agentName);
333
+ return memory.stats;
334
+ }
335
+
336
+ // ==================== Métodos Auxiliares ====================
337
+
338
+ private loadJsonFile<T>(filePath: string, defaultValue: T): T {
339
+ try {
340
+ if (fs.existsSync(filePath)) {
341
+ const content = fs.readFileSync(filePath, 'utf-8');
342
+ const data = JSON.parse(content);
343
+
344
+ // Robustez: se o arquivo JSON estiver envolto em um objeto (ex: { "successes": [] })
345
+ // extraímos o array diretamente
346
+ const fileName = path.basename(filePath, '.json');
347
+ if (data && typeof data === 'object' && !Array.isArray(data) && data[fileName]) {
348
+ return data[fileName] as T;
349
+ }
350
+
351
+ return data;
352
+ }
353
+ } catch (error) {
354
+ console.warn(`Erro ao carregar ${filePath}:`, error);
355
+ }
356
+ return defaultValue;
357
+ }
358
+
359
+ private saveJsonFile(filePath: string, data: any): void {
360
+ try {
361
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
362
+ } catch (error) {
363
+ console.error(`Erro ao salvar ${filePath}:`, error);
364
+ }
365
+ }
366
+
367
+ private limitEntries<T extends MemoryEntry>(entries: T[]): T[] {
368
+ if (entries.length <= this.maxEntries) {
369
+ return entries;
370
+ }
371
+ // Manter as mais recentes
372
+ return entries
373
+ .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
374
+ .slice(0, this.maxEntries);
375
+ }
376
+
377
+ private generateId(): string {
378
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
379
+ }
380
+
381
+ private extractKeywords(text: string): string[] {
382
+ return text
383
+ .toLowerCase()
384
+ .replace(/[^\w\s]/g, ' ')
385
+ .split(/\s+/)
386
+ .filter(word => word.length > 3);
387
+ }
388
+
389
+ private calculateSimilarity(keywords1: string[], keywords2: string[]): number {
390
+ const set1 = new Set(keywords1);
391
+ const set2 = new Set(keywords2);
392
+ const intersection = new Set([...set1].filter(k => set2.has(k)));
393
+ const union = new Set([...set1, ...set2]);
394
+
395
+ return union.size > 0 ? intersection.size / union.size : 0;
396
+ }
397
+ }
@@ -0,0 +1,268 @@
1
+ /**
2
+ * Quality Agent (com Memória)
3
+ *
4
+ * Responsabilidades:
5
+ * - Executar lint (ESLint)
6
+ * - Executar type check (TypeScript)
7
+ * - Executar build
8
+ * - Executar testes
9
+ * - Dar decisão final GO/NO-GO
10
+ * - Aprender com falhas de build recorrentes
11
+ */
12
+
13
+ import { spawn } from 'child_process';
14
+ import * as fs from 'fs';
15
+ import { BaseAgent, TaskContext, TaskResult } from './base-agent';
16
+
17
+ export interface QualityCheckResult extends TaskResult {
18
+ checks: {
19
+ lint: { passed: boolean; errors: number; warnings: number };
20
+ typecheck: { passed: boolean; errors: number };
21
+ build: { passed: boolean; error?: string };
22
+ tests: { passed: boolean; total: number; failed: number };
23
+ };
24
+ decision: 'GO' | 'NO-GO';
25
+ }
26
+
27
+ export class QualityAgent extends BaseAgent {
28
+ constructor(memoryBasePath: string = '.agents/memory') {
29
+ super('quality-agent', memoryBasePath);
30
+ }
31
+
32
+ /**
33
+ * Implementação da validação de qualidade
34
+ */
35
+ async executeTask(taskDescription: string, context: TaskContext): Promise<TaskResult> {
36
+ console.log('\n🔍 Executando validações de qualidade...\n');
37
+
38
+ const checks = {
39
+ lint: await this.runLint(),
40
+ typecheck: await this.runTypeCheck(),
41
+ build: await this.runBuild(),
42
+ tests: await this.runTests()
43
+ };
44
+
45
+ // Decisão GO/NO-GO
46
+ const decision = this.makeDecision(checks);
47
+
48
+ const success = decision === 'GO';
49
+ const details = this.formatChecks(checks, decision);
50
+
51
+ const issues: string[] = [];
52
+ if (!checks.lint.passed) issues.push(`Lint: ${checks.lint.errors} erro(s)`);
53
+ if (!checks.typecheck.passed) issues.push(`TypeCheck: ${checks.typecheck.errors} erro(s)`);
54
+ if (!checks.build.passed) issues.push('Build falhou');
55
+ if (!checks.tests.passed) issues.push(`Tests: ${checks.tests.failed} falhou(ram)`);
56
+
57
+ return {
58
+ success,
59
+ details,
60
+ issues: issues.length > 0 ? issues : undefined,
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Executa lint
66
+ */
67
+ private async runLint(): Promise<{ passed: boolean; errors: number; warnings: number }> {
68
+ console.log(' 📝 Executando lint...');
69
+
70
+ return new Promise((resolve) => {
71
+ // Verificar se package.json tem script de lint
72
+ if (!this.hasScript('lint')) {
73
+ console.log(' ⏭️ Lint não configurado');
74
+ resolve({ passed: true, errors: 0, warnings: 0 });
75
+ return;
76
+ }
77
+
78
+ const lint = spawn('npm', ['run', 'lint'], { shell: true });
79
+
80
+ let output = '';
81
+ lint.stdout.on('data', (data) => { output += data.toString(); });
82
+ lint.stderr.on('data', (data) => { output += data.toString(); });
83
+
84
+ lint.on('close', (code) => {
85
+ const errors = (output.match(/error/gi) || []).length;
86
+ const warnings = (output.match(/warning/gi) || []).length;
87
+ const passed = code === 0;
88
+
89
+ console.log(` ${passed ? '✅' : '❌'} Lint: ${errors} erro(s), ${warnings} aviso(s)`);
90
+ resolve({ passed, errors, warnings });
91
+ });
92
+
93
+ lint.on('error', () => {
94
+ resolve({ passed: false, errors: 1, warnings: 0 });
95
+ });
96
+ });
97
+ }
98
+
99
+ /**
100
+ * Executa type check
101
+ */
102
+ private async runTypeCheck(): Promise<{ passed: boolean; errors: number }> {
103
+ console.log(' 🔤 Executando type check...');
104
+
105
+ return new Promise((resolve) => {
106
+ const tsc = spawn('npx', ['tsc', '--noEmit'], { shell: true });
107
+
108
+ let output = '';
109
+ tsc.stdout.on('data', (data) => { output += data.toString(); });
110
+ tsc.stderr.on('data', (data) => { output += data.toString(); });
111
+
112
+ tsc.on('close', (code) => {
113
+ const errors = (output.match(/error TS/g) || []).length;
114
+ const passed = code === 0;
115
+
116
+ console.log(` ${passed ? '✅' : '❌'} TypeCheck: ${errors} erro(s)`);
117
+ resolve({ passed, errors });
118
+ });
119
+
120
+ tsc.on('error', () => {
121
+ console.log(' ⏭️ TypeScript não disponível');
122
+ resolve({ passed: true, errors: 0 });
123
+ });
124
+ });
125
+ }
126
+
127
+ /**
128
+ * Executa build
129
+ */
130
+ private async runBuild(): Promise<{ passed: boolean; error?: string }> {
131
+ console.log(' 🏗️ Executando build...');
132
+
133
+ return new Promise((resolve) => {
134
+ if (!this.hasScript('build')) {
135
+ console.log(' ⏭️ Build não configurado');
136
+ resolve({ passed: true });
137
+ return;
138
+ }
139
+
140
+ const build = spawn('npm', ['run', 'build'], { shell: true });
141
+
142
+ let output = '';
143
+ build.stdout.on('data', (data) => { output += data.toString(); });
144
+ build.stderr.on('data', (data) => { output += data.toString(); });
145
+
146
+ build.on('close', (code) => {
147
+ const passed = code === 0;
148
+ const error = passed ? undefined : output.substring(0, 500);
149
+
150
+ console.log(` ${passed ? '✅' : '❌'} Build ${passed ? 'passou' : 'falhou'}`);
151
+ resolve({ passed, error });
152
+ });
153
+
154
+ build.on('error', () => {
155
+ resolve({ passed: false, error: 'Erro ao executar build' });
156
+ });
157
+ });
158
+ }
159
+
160
+ /**
161
+ * Executa testes
162
+ */
163
+ private async runTests(): Promise<{ passed: boolean; total: number; failed: number }> {
164
+ console.log(' 🧪 Executando testes...');
165
+
166
+ return new Promise((resolve) => {
167
+ if (!this.hasScript('test')) {
168
+ console.log(' ⏭️ Testes não configurados');
169
+ resolve({ passed: true, total: 0, failed: 0 });
170
+ return;
171
+ }
172
+
173
+ const test = spawn('npm', ['run', 'test', '--', '--passWithNoTests'], { shell: true });
174
+
175
+ let output = '';
176
+ test.stdout.on('data', (data) => { output += data.toString(); });
177
+ test.stderr.on('data', (data) => { output += data.toString(); });
178
+
179
+ test.on('close', (code) => {
180
+ const passed = code === 0;
181
+ const total = (output.match(/Tests:.*?(\d+) total/)?.[1] || '0');
182
+ const failed = (output.match(/Tests:.*?(\d+) failed/)?.[1] || '0');
183
+
184
+ console.log(` ${passed ? '✅' : '❌'} Tests: ${failed} falhou(ram) de ${total}`);
185
+ resolve({ passed, total: parseInt(total), failed: parseInt(failed) });
186
+ });
187
+
188
+ test.on('error', () => {
189
+ resolve({ passed: true, total: 0, failed: 0 });
190
+ });
191
+ });
192
+ }
193
+
194
+ /**
195
+ * Verifica se script existe no package.json
196
+ */
197
+ private hasScript(scriptName: string): boolean {
198
+ try {
199
+ const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf-8'));
200
+ return !!packageJson.scripts?.[scriptName];
201
+ } catch {
202
+ return false;
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Toma decisão GO/NO-GO
208
+ */
209
+ private makeDecision(checks: QualityCheckResult['checks']): 'GO' | 'NO-GO' {
210
+ // Bloqueadores: lint errors, type errors, build failed
211
+ if (checks.lint.errors > 0) return 'NO-GO';
212
+ if (checks.typecheck.errors > 0) return 'NO-GO';
213
+ if (!checks.build.passed) return 'NO-GO';
214
+ if (!checks.tests.passed) return 'NO-GO';
215
+
216
+ return 'GO';
217
+ }
218
+
219
+ /**
220
+ * Formata resultado dos checks
221
+ */
222
+ private formatChecks(checks: QualityCheckResult['checks'], decision: 'GO' | 'NO-GO'): string {
223
+ let details = `Decisão: ${decision}\n`;
224
+ details += `Lint: ${checks.lint.passed ? 'OK' : 'FALHOU'}\n`;
225
+ details += `TypeCheck: ${checks.typecheck.passed ? 'OK' : 'FALHOU'}\n`;
226
+ details += `Build: ${checks.build.passed ? 'OK' : 'FALHOU'}\n`;
227
+ details += `Tests: ${checks.tests.passed ? 'OK' : 'FALHOU'}`;
228
+ return details;
229
+ }
230
+
231
+ /**
232
+ * Especialidade padrão do Quality Agent
233
+ */
234
+ protected getDefaultSpecialty(): string {
235
+ return `# Quality Agent - Especialidade
236
+
237
+ ## Responsabilidades
238
+ - Executar lint (ESLint)
239
+ - Executar type check (TypeScript)
240
+ - Executar build
241
+ - Executar testes unitários e integração
242
+ - Dar decisão final GO/NO-GO
243
+ - Garantir qualidade do código
244
+
245
+ ## Expertise
246
+ - Testing (Jest, Vitest, Playwright, Cypress)
247
+ - Linting (ESLint, Prettier)
248
+ - TypeScript
249
+ - CI/CD
250
+ - Code Quality Metrics
251
+ - Test Coverage
252
+
253
+ ## Regras
254
+ - Zero erros de lint (warnings são aceitáveis)
255
+ - Zero erros de type check
256
+ - Build deve passar
257
+ - Todos os testes devem passar
258
+ - Cobertura de testes > 80% (recomendado)
259
+
260
+ ## Tarefas Típicas
261
+ - Validar qualidade antes de commit
262
+ - Executar suite completa de testes
263
+ - Dar GO/NO-GO para deploy
264
+ - Identificar problemas de qualidade
265
+ - Sugerir melhorias de código
266
+ `;
267
+ }
268
+ }