wegho-agentes 7.0.0 → 7.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 (108) hide show
  1. package/.agent/skills/wegho-global-rules/SKILL.md +20 -0
  2. package/.agent/workflows/PROMPT_GUIDE.md +7 -4
  3. package/.agents/cli.ts +29 -21
  4. package/.agents/core/base-agent.ts +28 -12
  5. package/.agents/core/memory-system.ts +45 -13
  6. package/.agents/core/report-generator.ts +26 -19
  7. package/.agents/orchestrator.ts +104 -74
  8. package/.clinerules +318 -1229
  9. package/.cursorrules +0 -189
  10. package/README.md +3 -3
  11. package/docs/VERSION.md +3 -2
  12. package/package.json +3 -3
  13. package/.agents/agents/ai-agents/README.md +0 -175
  14. package/.agents/agents/ai-agents/agent.ts +0 -83
  15. package/.agents/agents/ai-agents/specialty.md +0 -14
  16. package/.agents/agents/architecture/README.md +0 -61
  17. package/.agents/agents/architecture/agent.ts +0 -238
  18. package/.agents/agents/architecture/memory.md +0 -111
  19. package/.agents/agents/architecture/specialty.md +0 -31
  20. package/.agents/agents/automation/README.md +0 -60
  21. package/.agents/agents/automation/agent.ts +0 -61
  22. package/.agents/agents/automation/specialty.md +0 -14
  23. package/.agents/agents/backend/README.md +0 -155
  24. package/.agents/agents/backend/agent.ts +0 -100
  25. package/.agents/agents/backend/specialty.md +0 -14
  26. package/.agents/agents/cloud/README.md +0 -73
  27. package/.agents/agents/cloud/agent.ts +0 -53
  28. package/.agents/agents/cloud/specialty.md +0 -14
  29. package/.agents/agents/code-auditor/README.md +0 -37
  30. package/.agents/agents/code-auditor/agent.ts +0 -334
  31. package/.agents/agents/code-auditor/specialty.md +0 -14
  32. package/.agents/agents/cro/README.md +0 -200
  33. package/.agents/agents/cro/agent.ts +0 -61
  34. package/.agents/agents/cro/specialty.md +0 -14
  35. package/.agents/agents/database/README.md +0 -67
  36. package/.agents/agents/database/agent.ts +0 -93
  37. package/.agents/agents/database/specialty.md +0 -14
  38. package/.agents/agents/devops/README.md +0 -84
  39. package/.agents/agents/devops/agent.ts +0 -54
  40. package/.agents/agents/devops/specialty.md +0 -14
  41. package/.agents/agents/documentation/README.md +0 -107
  42. package/.agents/agents/documentation/agent.ts +0 -253
  43. package/.agents/agents/documentation/memory.md +0 -56
  44. package/.agents/agents/documentation/specialty.md +0 -33
  45. package/.agents/agents/frontend/README.md +0 -188
  46. package/.agents/agents/frontend/agent.ts +0 -211
  47. package/.agents/agents/frontend/memory.md +0 -139
  48. package/.agents/agents/frontend/specialty.md +0 -30
  49. package/.agents/agents/inventory/README.md +0 -35
  50. package/.agents/agents/inventory/agent.ts +0 -758
  51. package/.agents/agents/inventory/memory.md +0 -50
  52. package/.agents/agents/inventory/specialty.md +0 -129
  53. package/.agents/agents/nextjs/README.md +0 -58
  54. package/.agents/agents/nextjs/agent.ts +0 -114
  55. package/.agents/agents/nextjs/specialty.md +0 -14
  56. package/.agents/agents/pentest/README.md +0 -228
  57. package/.agents/agents/pentest/agent.ts +0 -96
  58. package/.agents/agents/pentest/specialty.md +0 -14
  59. package/.agents/agents/planning/README.md +0 -107
  60. package/.agents/agents/planning/agent.ts +0 -389
  61. package/.agents/agents/planning/specialty.md +0 -14
  62. package/.agents/agents/project-discovery/README.md +0 -35
  63. package/.agents/agents/project-discovery/agent.ts +0 -344
  64. package/.agents/agents/project-discovery/specialty.md +0 -14
  65. package/.agents/agents/quality/README.md +0 -81
  66. package/.agents/agents/quality/agent.ts +0 -269
  67. package/.agents/agents/quality/memory.md +0 -110
  68. package/.agents/agents/quality/specialty.md +0 -31
  69. package/.agents/agents/rag/README.md +0 -41
  70. package/.agents/agents/rag/agent.ts +0 -85
  71. package/.agents/agents/rag/specialty.md +0 -14
  72. package/.agents/agents/security/README.md +0 -152
  73. package/.agents/agents/security/agent.ts +0 -218
  74. package/.agents/agents/security/memory.md +0 -91
  75. package/.agents/agents/security/specialty.md +0 -31
  76. package/.agents/agents/task-analyzer/README.md +0 -36
  77. package/.agents/agents/task-analyzer/agent.ts +0 -462
  78. package/.agents/agents/task-analyzer/specialty.md +0 -14
  79. package/.agents/agents/testing/README.md +0 -161
  80. package/.agents/agents/testing/agent.ts +0 -61
  81. package/.agents/agents/testing/specialty.md +0 -14
  82. package/.agents/agents/uiux/README.md +0 -68
  83. package/.agents/agents/uiux/agent.ts +0 -95
  84. package/.agents/agents/uiux/specialty.md +0 -14
  85. package/.agents/base/base-agent.ts +0 -331
  86. package/.agents/base/memory-system.ts +0 -397
  87. package/.agents/base/skill-manager.ts +0 -95
  88. package/.agents/convert-memory.ps1 +0 -153
  89. package/.agents/examples/reporting-example.md +0 -203
  90. package/.agents/managers/build-manager.ts +0 -304
  91. package/.agents/managers/cache-manager.ts +0 -184
  92. package/.agents/managers/checkpoint-manager.ts +0 -299
  93. package/.agents/migrate-agents.ps1 +0 -117
  94. package/.agents/templates/change-report.md +0 -55
  95. package/.agents/templates/execution-plan.md +0 -36
  96. package/.agents/unmapped-skills.txt +0 -0
  97. package/.agents/utils/agent-migrator.ts +0 -360
  98. package/.agents/utils/agent-monitor.ts +0 -102
  99. package/.agents/utils/agent-parallelizer.ts +0 -108
  100. package/.agents/utils/context-monitor.ts +0 -140
  101. package/.agents/utils/feedback-collector.ts +0 -207
  102. package/.agents/utils/file-generator.ts +0 -304
  103. package/.agents/utils/memory-converter.ts +0 -217
  104. package/.agents/utils/memory-dashboard.ts +0 -147
  105. package/.agents/utils/performance-tracker.ts +0 -275
  106. package/.agents/utils/report-generator.ts +0 -193
  107. package/.agents/utils/retry-utility.ts +0 -140
  108. package/.agents/utils/workflow-validator.ts +0 -158
@@ -1,758 +0,0 @@
1
- import { BaseAgent, TaskContext, TaskResult } from '../../base/base-agent.js';
2
- import * as fs from 'fs/promises';
3
- import * as path from 'path';
4
- import { glob } from 'glob';
5
-
6
- // ============================================================
7
- // 🏭 INVENTORY AGENT - ALMOXARIFADO CENTRAL DO SISTEMA
8
- // ============================================================
9
- // Responsável por:
10
- // 1. Mapear TUDO no projeto (componentes, variáveis, banco, APIs)
11
- // 2. Fornecer busca rápida para outros agentes
12
- // 3. IMPEDIR duplicação de código
13
- // 4. Organizar e catalogar recursos
14
- // ============================================================
15
-
16
- interface InventoryItem {
17
- id: string;
18
- name: string;
19
- type: 'component' | 'variable' | 'table' | 'endpoint' | 'type' | 'asset' | 'function';
20
- path: string;
21
- line?: number;
22
- metadata: Record<string, any>;
23
- usedBy: string[];
24
- createdAt: string;
25
- lastModified: string;
26
- }
27
-
28
- interface Component extends InventoryItem {
29
- type: 'component';
30
- exports: string[];
31
- props: string[];
32
- dependencies: string[];
33
- }
34
-
35
- interface Variable extends InventoryItem {
36
- type: 'variable';
37
- scope: 'global' | 'local' | 'exported';
38
- value?: string;
39
- dataType: string;
40
- }
41
-
42
- interface DatabaseTable extends InventoryItem {
43
- type: 'table';
44
- schema: string;
45
- columns: Array<{
46
- name: string;
47
- type: string;
48
- nullable: boolean;
49
- primaryKey?: boolean;
50
- foreignKey?: string;
51
- }>;
52
- relationships: Array<{
53
- table: string;
54
- type: 'one-to-one' | 'one-to-many' | 'many-to-many';
55
- foreignKey: string;
56
- }>;
57
- }
58
-
59
- interface Endpoint extends InventoryItem {
60
- type: 'endpoint';
61
- method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
62
- params?: string[];
63
- body?: Record<string, string>;
64
- response?: string;
65
- authentication: boolean;
66
- }
67
-
68
- interface InventoryQuery {
69
- type?: InventoryItem['type'];
70
- name?: string;
71
- path?: string;
72
- fuzzy?: boolean;
73
- }
74
-
75
- interface DuplicationCheck {
76
- exists: boolean;
77
- duplicates: InventoryItem[];
78
- suggestions: string[];
79
- }
80
-
81
- export class InventoryAgent extends BaseAgent {
82
- private inventory: Map<string, InventoryItem> = new Map();
83
- private inventoryPath: string;
84
- private projectRoot: string;
85
-
86
- constructor(projectRoot: string, memoryBasePath: string = '.agents/memory') {
87
- super('inventory-agent', memoryBasePath);
88
- this.projectRoot = projectRoot;
89
- this.inventoryPath = path.join(memoryBasePath, 'inventory-agent', 'inventory');
90
- }
91
-
92
- async executeTask(taskDescription: string, context: TaskContext): Promise<TaskResult> {
93
- try {
94
- // Determinar ação baseada na descrição da tarefa
95
- if (taskDescription.includes('scan') || taskDescription.includes('escanear')) {
96
- await this.scanProject();
97
- return {
98
- success: true,
99
- details: `Inventário completo: ${this.inventory.size} itens catalogados`,
100
- recommendations: [`Total de ${this.inventory.size} itens no almoxarifado`],
101
- };
102
- }
103
-
104
- if (taskDescription.includes('find') || taskDescription.includes('buscar')) {
105
- const query = this.parseQuery(taskDescription);
106
- const results = await this.findAll(query);
107
- return {
108
- success: true,
109
- details: `Encontrados ${results.length} itens`,
110
- recommendations: results.map((r) => `${r.type}: ${r.name} (${r.path})`),
111
- };
112
- }
113
-
114
- if (taskDescription.includes('cleanup') || taskDescription.includes('limpeza') || taskDescription.includes('unused')) {
115
- await this.scanProject(); // Garantir dados frescos
116
- await this.analyzeUsage(); // Analisar uso
117
- const unused = await this.findUnusedItems();
118
-
119
- // Gerar relatório
120
- const reportPath = path.join(this.projectRoot, 'docs', 'CLEANUP_REPORT.md');
121
- const reportContent = this.generateCleanupReport(unused);
122
-
123
- // Garantir diretório docs
124
- const docsDir = path.join(this.projectRoot, 'docs');
125
- try {
126
- await fs.mkdir(docsDir, { recursive: true });
127
- } catch { }
128
-
129
- await fs.writeFile(reportPath, reportContent);
130
-
131
- return {
132
- success: true,
133
- details: `Análise de limpeza concluída. ${unused.length} itens potencialmente não utilizados.`,
134
- recommendations: [
135
- `Relatório salvo em: ${reportPath}`,
136
- `Revise os itens antes de excluir (Next.js pages/layouts são falsos positivos comuns se não detectados corretamente)`
137
- ],
138
- issues: unused.length > 0 ? unused.slice(0, 5).map(i => `Não utilizado: ${i.name} (${i.type}) - ${i.path}`) : undefined
139
- };
140
- }
141
-
142
- if (taskDescription.includes('check') || taskDescription.includes('verificar')) {
143
- const itemName = this.extractItemName(taskDescription);
144
- const check = await this.checkDuplication(itemName, 'component');
145
- return {
146
- success: !check.exists,
147
- details: check.exists
148
- ? `Item "${itemName}" já existe`
149
- : `Nenhuma duplicação encontrada para "${itemName}"`,
150
- warnings: check.exists ? [`Duplicação detectada: ${itemName}`] : undefined,
151
- recommendations: check.suggestions,
152
- };
153
- }
154
-
155
- // Scan padrão
156
- await this.scanProject();
157
- return {
158
- success: true,
159
- details: `Scan completo: ${this.inventory.size} itens catalogados`,
160
- };
161
- } catch (error) {
162
- return {
163
- success: false,
164
- details: `Erro ao executar tarefa: ${(error as Error).message}`,
165
- issues: [(error as Error).message],
166
- };
167
- }
168
- }
169
-
170
- // ============================================================
171
- // 🔍 MÉTODOS DE BUSCA (Para outros agentes)
172
- // ============================================================
173
-
174
- /**
175
- * Busca um único item no inventário
176
- */
177
- async find(query: InventoryQuery): Promise<InventoryItem | null> {
178
- const results = await this.findAll(query);
179
- return results[0] || null;
180
- }
181
-
182
- /**
183
- * Busca todos os itens que correspondem à query
184
- */
185
- async findAll(query: InventoryQuery): Promise<InventoryItem[]> {
186
- await this.loadInventory();
187
-
188
- let results = Array.from(this.inventory.values());
189
-
190
- // Filtrar por tipo
191
- if (query.type) {
192
- results = results.filter((item) => item.type === query.type);
193
- }
194
-
195
- // Filtrar por nome
196
- if (query.name) {
197
- if (query.fuzzy) {
198
- // Busca fuzzy (case-insensitive, parcial)
199
- const searchTerm = query.name.toLowerCase();
200
- results = results.filter((item) => item.name.toLowerCase().includes(searchTerm));
201
- } else {
202
- // Busca exata
203
- results = results.filter((item) => item.name === query.name);
204
- }
205
- }
206
-
207
- // Filtrar por path
208
- if (query.path) {
209
- results = results.filter((item) => item.path.includes(query.path!));
210
- }
211
-
212
- return results;
213
- }
214
-
215
- /**
216
- * Busca textual em todo o inventário
217
- */
218
- async search(text: string): Promise<InventoryItem[]> {
219
- await this.loadInventory();
220
-
221
- const searchTerm = text.toLowerCase();
222
- return Array.from(this.inventory.values()).filter(
223
- (item) =>
224
- item.name.toLowerCase().includes(searchTerm) ||
225
- item.path.toLowerCase().includes(searchTerm) ||
226
- JSON.stringify(item.metadata).toLowerCase().includes(searchTerm)
227
- );
228
- }
229
-
230
- // ============================================================
231
- // 🛡️ VERIFICAÇÃO ANTI-DUPLICAÇÃO (CRÍTICO!)
232
- // ============================================================
233
-
234
- /**
235
- * Verifica se um item já existe antes de criar
236
- * TODOS os agentes DEVEM chamar isso antes de criar algo novo!
237
- */
238
- async checkDuplication(name: string, type: InventoryItem['type']): Promise<DuplicationCheck> {
239
- await this.loadInventory();
240
-
241
- // Busca exata
242
- const exact = await this.find({ name, type });
243
-
244
- // Busca fuzzy (similares)
245
- const similar = await this.findAll({ name, type, fuzzy: true });
246
-
247
- const exists = exact !== null;
248
- const duplicates = similar.filter((item) => item.name !== name);
249
-
250
- const suggestions: string[] = [];
251
-
252
- if (exists) {
253
- suggestions.push(`⚠️ Item "${name}" já existe em: ${exact.path}`);
254
- suggestions.push(`💡 Use o existente em vez de criar novo`);
255
- }
256
-
257
- if (duplicates.length > 0) {
258
- suggestions.push(`⚠️ Encontrados ${duplicates.length} item(ns) similar(es):`);
259
- duplicates.forEach((dup) => {
260
- suggestions.push(` - ${dup.name} (${dup.path})`);
261
- });
262
- suggestions.push(`💡 Considere reutilizar um desses em vez de criar novo`);
263
- }
264
-
265
- if (!exists && duplicates.length === 0) {
266
- suggestions.push(`✅ Nenhuma duplicação encontrada. Seguro criar "${name}"`);
267
- }
268
-
269
- return {
270
- exists,
271
- duplicates,
272
- suggestions,
273
- };
274
- }
275
-
276
- // ============================================================
277
- // 📊 MÉTODOS DE SCAN
278
- // ============================================================
279
-
280
- /**
281
- * Escaneia o projeto inteiro e atualiza o inventário
282
- */
283
- async scanProject(): Promise<void> {
284
- console.log('🔍 [inventory-agent] Escaneando projeto...');
285
-
286
- this.inventory.clear();
287
-
288
- await Promise.all([
289
- this.scanComponents(),
290
- this.scanVariables(),
291
- this.scanEndpoints(),
292
- this.scanTypes(),
293
- this.scanDatabase(),
294
- ]);
295
-
296
- await this.saveInventory();
297
-
298
- console.log(`✅ [inventory-agent] Scan completo: ${this.inventory.size} itens catalogados`);
299
- }
300
-
301
- /**
302
- * Escaneia componentes React/Next.js
303
- */
304
- private async scanComponents(): Promise<void> {
305
- const patterns = ['**/*.tsx', '**/*.jsx', '!node_modules/**', '!.next/**', '!.agents/**'];
306
- const files = await glob(patterns, { cwd: this.projectRoot });
307
-
308
- for (const file of files) {
309
- const fullPath = path.join(this.projectRoot, file);
310
-
311
- try {
312
- const stat = await fs.stat(fullPath);
313
- if (stat.isDirectory()) continue;
314
-
315
- const content = await fs.readFile(fullPath, 'utf-8');
316
-
317
- // Detectar componentes exportados
318
- const exportMatches = content.matchAll(/export\s+(?:default\s+)?(?:function|const|class)\s+(\w+)/g);
319
-
320
- for (const match of exportMatches) {
321
- const componentName = match[1];
322
-
323
- // Extrair props
324
- const propsMatch = content.match(new RegExp(`${componentName}.*?\\(\\s*{([^}]+)}`, 's'));
325
- const props = propsMatch
326
- ? propsMatch[1]
327
- .split(',')
328
- .map((p) => p.trim().split(':')[0].trim())
329
- .filter(Boolean)
330
- : [];
331
-
332
- // Extrair imports (dependências)
333
- const importMatches = content.matchAll(/import\s+.*?from\s+['"]([^'"]+)['"]/g);
334
- const dependencies = Array.from(importMatches, (m) => m[1]);
335
-
336
- const component: Component = {
337
- id: `component:${file}:${componentName}`,
338
- name: componentName,
339
- type: 'component',
340
- path: file,
341
- exports: [componentName],
342
- props,
343
- dependencies,
344
- metadata: {
345
- isDefaultExport: content.includes(`export default ${componentName}`),
346
- },
347
- usedBy: [],
348
- createdAt: new Date().toISOString(),
349
- lastModified: new Date().toISOString(),
350
- };
351
-
352
- this.inventory.set(component.id, component);
353
- }
354
- } catch (err) {
355
- console.warn(`[inventory-agent] Erro ao ler componente ${file}: ${(err as Error).message}`);
356
- }
357
- }
358
- }
359
-
360
- /**
361
- * Escaneia variáveis globais e exportadas
362
- */
363
- private async scanVariables(): Promise<void> {
364
- const patterns = ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '!node_modules/**', '!.next/**'];
365
- const files = await glob(patterns, { cwd: this.projectRoot });
366
-
367
- for (const file of files) {
368
- const fullPath = path.join(this.projectRoot, file);
369
-
370
- try {
371
- const stat = await fs.stat(fullPath);
372
- if (stat.isDirectory()) continue;
373
-
374
- const content = await fs.readFile(fullPath, 'utf-8');
375
-
376
- // Detectar variáveis exportadas
377
- const varMatches = content.matchAll(/export\s+const\s+(\w+)\s*[:=]\s*(.+?)(?:[;\n])/g);
378
-
379
- for (const match of varMatches) {
380
- const [, varName, value] = match;
381
-
382
- const variable: Variable = {
383
- id: `variable:${file}:${varName}`,
384
- name: varName,
385
- type: 'variable',
386
- path: file,
387
- scope: 'exported',
388
- value: value.trim(),
389
- dataType: this.inferType(value),
390
- metadata: {},
391
- usedBy: [],
392
- createdAt: new Date().toISOString(),
393
- lastModified: new Date().toISOString(),
394
- };
395
-
396
- this.inventory.set(variable.id, variable);
397
- }
398
- } catch (err) {
399
- console.warn(`[inventory-agent] Erro ao ler variável ${file}: ${(err as Error).message}`);
400
- }
401
- }
402
- }
403
-
404
- /**
405
- * Escaneia endpoints de API
406
- */
407
- private async scanEndpoints(): Promise<void> {
408
- const patterns = ['**/api/**/*.ts', '**/api/**/*.js', '!node_modules/**'];
409
- const files = await glob(patterns, { cwd: this.projectRoot });
410
-
411
- for (const file of files) {
412
- const fullPath = path.join(this.projectRoot, file);
413
-
414
- try {
415
- const stat = await fs.stat(fullPath);
416
- if (stat.isDirectory()) continue;
417
-
418
- const content = await fs.readFile(fullPath, 'utf-8');
419
-
420
- // Detectar métodos HTTP
421
- const methods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] as const;
422
-
423
- for (const method of methods) {
424
- const regex = new RegExp(`export\\s+async\\s+function\\s+${method}`, 'g');
425
- if (regex.test(content)) {
426
- const endpoint: Endpoint = {
427
- id: `endpoint:${file}:${method}`,
428
- name: file.replace(/.*\/api\//, '/api/').replace(/\/route\.(ts|js)/, ''),
429
- type: 'endpoint',
430
- path: file,
431
- method,
432
- authentication: content.includes('auth') || content.includes('session'),
433
- metadata: {},
434
- usedBy: [],
435
- createdAt: new Date().toISOString(),
436
- lastModified: new Date().toISOString(),
437
- };
438
-
439
- this.inventory.set(endpoint.id, endpoint);
440
- }
441
- }
442
- } catch (err) {
443
- console.warn(`[inventory-agent] Erro ao ler endpoint ${file}: ${(err as Error).message}`);
444
- }
445
- }
446
- }
447
-
448
- /**
449
- * Escaneia tipos TypeScript
450
- */
451
- private async scanTypes(): Promise<void> {
452
- const patterns = ['**/types/**/*.ts', '**/*.d.ts', '!node_modules/**'];
453
- const files = await glob(patterns, { cwd: this.projectRoot });
454
-
455
- for (const file of files) {
456
- const fullPath = path.join(this.projectRoot, file);
457
-
458
- try {
459
- const stat = await fs.stat(fullPath);
460
- if (stat.isDirectory()) continue;
461
-
462
- const content = await fs.readFile(fullPath, 'utf-8');
463
-
464
- // Detectar interfaces e types
465
- const typeMatches = content.matchAll(/(?:export\s+)?(?:interface|type)\s+(\w+)/g);
466
-
467
- for (const match of typeMatches) {
468
- const typeName = match[1];
469
-
470
- const typeItem: InventoryItem = {
471
- id: `type:${file}:${typeName}`,
472
- name: typeName,
473
- type: 'type',
474
- path: file,
475
- metadata: {
476
- isInterface: content.includes(`interface ${typeName}`),
477
- isType: content.includes(`type ${typeName}`),
478
- },
479
- usedBy: [],
480
- createdAt: new Date().toISOString(),
481
- lastModified: new Date().toISOString(),
482
- };
483
-
484
- this.inventory.set(typeItem.id, typeItem);
485
- }
486
- } catch (err) {
487
- console.warn(`[inventory-agent] Erro ao ler tipo ${file}: ${(err as Error).message}`);
488
- }
489
- }
490
- }
491
-
492
- /**
493
- * Escaneia schema do banco de dados (Supabase/PostgreSQL)
494
- */
495
- private async scanDatabase(): Promise<void> {
496
- const patterns = ['**/supabase/migrations/**/*.sql', '**/prisma/schema.prisma', '!node_modules/**'];
497
- const files = await glob(patterns, { cwd: this.projectRoot });
498
-
499
- for (const file of files) {
500
- const fullPath = path.join(this.projectRoot, file);
501
-
502
- try {
503
- const stat = await fs.stat(fullPath);
504
- if (stat.isDirectory()) continue;
505
-
506
- const content = await fs.readFile(fullPath, 'utf-8');
507
-
508
- // Detectar CREATE TABLE
509
- const tableMatches = content.matchAll(/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(\w+)\s*\(/gi);
510
-
511
- for (const match of tableMatches) {
512
- const tableName = match[1];
513
-
514
- const table: DatabaseTable = {
515
- id: `table:${tableName}`,
516
- name: tableName,
517
- type: 'table',
518
- path: file,
519
- schema: 'public',
520
- columns: [],
521
- relationships: [],
522
- metadata: {},
523
- usedBy: [],
524
- createdAt: new Date().toISOString(),
525
- lastModified: new Date().toISOString(),
526
- };
527
-
528
- this.inventory.set(table.id, table);
529
- }
530
- } catch (err) {
531
- console.warn(`[inventory-agent] Erro ao ler schema ${file}: ${(err as Error).message}`);
532
- }
533
- }
534
- }
535
-
536
- // ============================================================
537
- // 💾 PERSISTÊNCIA
538
- // ============================================================
539
-
540
- /**
541
- * Salva inventário em arquivos JSON
542
- */
543
- private async saveInventory(): Promise<void> {
544
- await fs.mkdir(this.inventoryPath, { recursive: true });
545
-
546
- // Agrupar por tipo
547
- const byType = new Map<string, InventoryItem[]>();
548
-
549
- for (const item of this.inventory.values()) {
550
- if (!byType.has(item.type)) {
551
- byType.set(item.type, []);
552
- }
553
- byType.get(item.type)!.push(item);
554
- }
555
-
556
- // Salvar cada tipo em arquivo separado
557
- for (const [type, items] of byType) {
558
- const filePath = path.join(this.inventoryPath, `${type}s.json`);
559
- await fs.writeFile(filePath, JSON.stringify(items, null, 2));
560
- }
561
-
562
- // Salvar índice geral
563
- const index = {
564
- totalItems: this.inventory.size,
565
- byType: Object.fromEntries(
566
- Array.from(byType.entries()).map(([type, items]) => [type, items.length])
567
- ),
568
- lastUpdated: new Date().toISOString(),
569
- };
570
-
571
- await fs.writeFile(path.join(this.inventoryPath, 'index.json'), JSON.stringify(index, null, 2));
572
- }
573
-
574
- /**
575
- * Carrega inventário dos arquivos JSON
576
- */
577
- private async loadInventory(): Promise<void> {
578
- try {
579
- const indexPath = path.join(this.inventoryPath, 'index.json');
580
- const indexExists = await fs
581
- .access(indexPath)
582
- .then(() => true)
583
- .catch(() => false);
584
-
585
- if (!indexExists) {
586
- await this.scanProject();
587
- return;
588
- }
589
-
590
- this.inventory.clear();
591
- const types = ['component', 'variable', 'table', 'endpoint', 'type'];
592
-
593
- for (const type of types) {
594
- const filePath = path.join(this.inventoryPath, `${type}s.json`);
595
- const fileExists = await fs
596
- .access(filePath)
597
- .then(() => true)
598
- .catch(() => false);
599
-
600
- if (fileExists) {
601
- const content = await fs.readFile(filePath, 'utf-8');
602
- const items = JSON.parse(content) as InventoryItem[];
603
-
604
- for (const item of items) {
605
- this.inventory.set(item.id, item);
606
- }
607
- }
608
- }
609
- } catch (error) {
610
- console.warn('[inventory-agent] Erro ao carregar inventário:', error);
611
- }
612
- }
613
-
614
- // ============================================================
615
- // 🛠️ UTILITÁRIOS
616
- // ============================================================
617
-
618
- private inferType(value: string): string {
619
- if (value.startsWith("'") || value.startsWith('"') || value.startsWith('`')) return 'string';
620
- if (value === 'true' || value === 'false') return 'boolean';
621
- if (!isNaN(Number(value))) return 'number';
622
- if (value.startsWith('[')) return 'array';
623
- if (value.startsWith('{')) return 'object';
624
- return 'unknown';
625
- }
626
-
627
- private parseQuery(description: string): InventoryQuery {
628
- const query: InventoryQuery = { fuzzy: true };
629
-
630
- if (description.includes('component')) query.type = 'component';
631
- if (description.includes('variable')) query.type = 'variable';
632
- if (description.includes('endpoint')) query.type = 'endpoint';
633
- if (description.includes('table')) query.type = 'table';
634
-
635
- return query;
636
- }
637
-
638
- private extractItemName(description: string): string {
639
- const match = description.match(/["']([^"']+)["']/);
640
- return match ? match[1] : '';
641
- }
642
-
643
- // ============================================================
644
- // 🧹 LIMPEZA E ANÁLISE DE USO (OTIMIZADO)
645
- // ============================================================
646
-
647
- /**
648
- * Analisa o uso de cada item no projeto - OTIMIZADO
649
- */
650
- async analyzeUsage(): Promise<void> {
651
- console.log('🕵️ [inventory-agent] Analisando referências cruzadas (Modo Otimizado)...');
652
-
653
- await this.loadInventory();
654
- const items = Array.from(this.inventory.values());
655
-
656
- // Resetar contadores
657
- items.forEach(i => i.usedBy = []);
658
-
659
- const patterns = ['**/*.tsx', '**/*.jsx', '**/*.ts', '**/*.js', '!node_modules/**', '!.next/**', '!.agents/**'];
660
- const files = await glob(patterns, { cwd: this.projectRoot });
661
-
662
- // Mapa para busca rápida de itens por nome
663
- // Tratando colisão de nomes: Map<Name, Item[]>
664
- const itemsByName = new Map<string, InventoryItem[]>();
665
- for (const item of items) {
666
- if (!itemsByName.has(item.name)) {
667
- itemsByName.set(item.name, []);
668
- }
669
- itemsByName.get(item.name)!.push(item);
670
- }
671
-
672
- let processedFiles = 0;
673
-
674
- for (const file of files) {
675
- const fullPath = path.join(this.projectRoot, file);
676
- try {
677
- const content = await fs.readFile(fullPath, 'utf-8');
678
-
679
- // Extrair todos os "tokens" (palavras) do arquivo
680
- // Isso é muito mais rápido que rodar milhares de regex
681
- const tokens = new Set(content.match(/\b\w+\b/g) || []);
682
-
683
- // Verificar quais itens estão presentes nos tokens
684
- for (const [name, potentialItems] of itemsByName) {
685
- if (tokens.has(name)) {
686
- for (const item of potentialItems) {
687
- // Evitar auto-referência (arquivo definindo o item)
688
- // Apenas se o path for exatamente o mesmo
689
- if (item.path !== file) {
690
- item.usedBy.push(file);
691
- }
692
- }
693
- }
694
- }
695
-
696
- processedFiles++;
697
- } catch (err) {
698
- console.warn(`Erro ao ler arquivo ${file}: ${(err as Error).message}`);
699
- }
700
- }
701
-
702
- // Salvar inventário atualizado com contagem de uso
703
- await this.saveInventory();
704
- console.log(`✅ [inventory-agent] Análise completa. ${files.length} arquivos processados.`);
705
- }
706
-
707
- /**
708
- * Encontra itens não utilizados (com whitelist)
709
- */
710
- async findUnusedItems(): Promise<InventoryItem[]> {
711
- await this.loadInventory();
712
- const items = Array.from(this.inventory.values());
713
-
714
- return items.filter(item => {
715
- // Filtrar quem tem uso
716
- if (item.usedBy.length > 0) return false;
717
-
718
- // Whitelists (Falsos positivos conhecidos)
719
-
720
- // 1. Next.js Pages/Layouts/API Routes (Sempre usados pelo framework)
721
- if (item.path.includes('page.tsx') || item.path.includes('layout.tsx') || item.path.includes('route.ts')) return false;
722
-
723
- // 2. Config files
724
- if (item.name.includes('Config') || item.path.includes('config')) return false;
725
-
726
- // 3. Types (muitas vezes importados globalmente ou d.ts)
727
- if (item.type === 'type' && item.path.endsWith('.d.ts')) return false;
728
-
729
- return true;
730
- });
731
- }
732
-
733
- private generateCleanupReport(unused: InventoryItem[]): string {
734
- let report = '# 🧹 Relatório de Limpeza de Código\n\n';
735
- report += `**Data**: ${new Date().toLocaleDateString()}\n`;
736
- report += `**Total de Itens Analisados**: ${this.inventory.size}\n`;
737
- report += `**Potencialmente Não Utilizados**: ${unused.length}\n\n`;
738
-
739
- report += '> ⚠️ **Atenção**: Esta análise busca referências textuais exatas. Valide manualmente antes de excluir, pois podem haver usos dinâmicos ou indiretos.\n\n';
740
-
741
- const byType = new Map<string, InventoryItem[]>();
742
- unused.forEach(i => {
743
- if (!byType.has(i.type)) byType.set(i.type, []);
744
- byType.get(i.type)!.push(i);
745
- });
746
-
747
- for (const [type, items] of byType) {
748
- report += `## ${type.toUpperCase()} (${items.length})\n`;
749
- items.forEach(i => {
750
- report += `- **${i.name}**\n - Path: \`${i.path}\`\n - Last Modified: ${new Date(i.lastModified).toLocaleDateString()}\n`;
751
- });
752
- report += '\n';
753
- }
754
-
755
- return report;
756
- }
757
- }
758
-