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.
- package/.agents/AGENT_WORKFLOW.md +528 -0
- package/.agents/AI_COMPATIBILITY.md +332 -0
- package/.agents/CLI.md +222 -0
- package/.agents/README.md +130 -0
- package/.agents/cli.js +389 -0
- package/.agents/code-auditor-agent.ts +333 -0
- package/.agents/config.ts +145 -0
- package/.agents/context-loader.ts +300 -0
- package/.agents/core/agent-parallelizer.test.ts +94 -0
- package/.agents/core/agent-parallelizer.ts +108 -0
- package/.agents/core/architecture-agent.ts +237 -0
- package/.agents/core/base-agent.ts +311 -0
- package/.agents/core/cache-manager.test.ts +147 -0
- package/.agents/core/cache-manager.ts +184 -0
- package/.agents/core/documentation-agent.ts +183 -0
- package/.agents/core/feedback-collector.ts +207 -0
- package/.agents/core/frontend-agent.ts +210 -0
- package/.agents/core/inventory-agent.ts +582 -0
- package/.agents/core/memory-system.ts +397 -0
- package/.agents/core/quality-agent.ts +268 -0
- package/.agents/core/retry-utility.test.ts +165 -0
- package/.agents/core/retry-utility.ts +140 -0
- package/.agents/core/security-agent.ts +217 -0
- package/.agents/core/workflow-validator.test.ts +171 -0
- package/.agents/core/workflow-validator.ts +158 -0
- package/.agents/domains/README.md +53 -0
- package/.agents/domains/logistics/route-agent.ts +177 -0
- package/.agents/domains/news/cms-agent.ts +158 -0
- package/.agents/domains/news/seo-agent.ts +170 -0
- package/.agents/domains/production/production-control-agent.ts +169 -0
- package/.agents/example-learning-system.js +118 -0
- package/.agents/init.ts +164 -0
- package/.agents/memory/architecture-agent/failures.json +1 -0
- package/.agents/memory/architecture-agent/learnings.json +1 -0
- package/.agents/memory/architecture-agent/specialty.md +31 -0
- package/.agents/memory/architecture-agent/successes.json +16 -0
- package/.agents/memory/cms-agent/failures.json +1 -0
- package/.agents/memory/cms-agent/learnings.json +1 -0
- package/.agents/memory/cms-agent/specialty.md +30 -0
- package/.agents/memory/cms-agent/successes.json +16 -0
- package/.agents/memory/documentation-agent/failures.json +1 -0
- package/.agents/memory/documentation-agent/learnings.json +1 -0
- package/.agents/memory/documentation-agent/specialty.md +33 -0
- package/.agents/memory/documentation-agent/successes.json +16 -0
- package/.agents/memory/frontend-agent/failures.json +1 -0
- package/.agents/memory/frontend-agent/learnings.json +1 -0
- package/.agents/memory/frontend-agent/specialty.md +30 -0
- package/.agents/memory/frontend-agent/successes.json +16 -0
- package/.agents/memory/inventory-agent/failures.json +1 -0
- package/.agents/memory/inventory-agent/inventory/index.json +8 -0
- package/.agents/memory/inventory-agent/inventory/types.json +77716 -0
- package/.agents/memory/inventory-agent/inventory/variables.json +405 -0
- package/.agents/memory/inventory-agent/learnings.json +1 -0
- package/.agents/memory/inventory-agent/specialty.md +129 -0
- package/.agents/memory/inventory-agent/successes.json +30 -0
- package/.agents/memory/production-control-agent/failures.json +1 -0
- package/.agents/memory/production-control-agent/learnings.json +1 -0
- package/.agents/memory/production-control-agent/specialty.md +29 -0
- package/.agents/memory/production-control-agent/successes.json +16 -0
- package/.agents/memory/quality-agent/failures.json +16 -0
- package/.agents/memory/quality-agent/learnings.json +1 -0
- package/.agents/memory/quality-agent/specialty.md +31 -0
- package/.agents/memory/quality-agent/successes.json +1 -0
- package/.agents/memory/reference-repositories.json +271 -0
- package/.agents/memory/route-agent/failures.json +1 -0
- package/.agents/memory/route-agent/learnings.json +1 -0
- package/.agents/memory/route-agent/specialty.md +29 -0
- package/.agents/memory/route-agent/successes.json +16 -0
- package/.agents/memory/security-agent/failures.json +1 -0
- package/.agents/memory/security-agent/learnings.json +1 -0
- package/.agents/memory/security-agent/specialty.md +31 -0
- package/.agents/memory/security-agent/successes.json +16 -0
- package/.agents/memory/seo-agent/failures.json +1 -0
- package/.agents/memory/seo-agent/learnings.json +1 -0
- package/.agents/memory/seo-agent/specialty.md +31 -0
- package/.agents/memory/seo-agent/successes.json +16 -0
- package/.agents/orchestrator.ts +438 -0
- package/.agents/project-discovery-agent.ts +342 -0
- package/.agents/security/pentesting-agent.py +387 -0
- package/.agents/security/python-bridge.ts +193 -0
- package/.agents/security/vulnerability-db.json +201 -0
- package/.agents/task-analyzer-agent.ts +346 -0
- package/.agents/test-init-context.js +67 -0
- package/INSTALL.md +300 -0
- package/LICENSE +21 -0
- package/README.md +315 -0
- package/docs/AGENT_RULES.md +292 -0
- package/docs/BUILD_HISTORY.md +65 -0
- package/docs/DESIGN_SYSTEM.md +256 -0
- package/docs/LEARNING_SYSTEM.md +326 -0
- package/docs/SYMBOLS_TREE.md +182 -0
- package/docs/VERSION.md +6 -0
- package/docs/architecture.md +111 -0
- 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
|
+
}
|