@codexa/cli 8.5.0

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.
@@ -0,0 +1,1000 @@
1
+ import { getDb } from "../db/connection";
2
+ import { initSchema } from "../db/schema";
3
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
4
+ import { join } from "path";
5
+ import {
6
+ detectUniversal,
7
+ detectStackLegacy,
8
+ formatDetectionResult,
9
+ getDetailedTechnologies,
10
+ type UnifiedDetectionResult,
11
+ } from "../detectors/loader";
12
+
13
+ interface StackDetection {
14
+ frontend?: string;
15
+ backend?: string;
16
+ database?: string;
17
+ orm?: string;
18
+ styling?: string;
19
+ auth?: string;
20
+ testing?: string;
21
+ }
22
+
23
+ interface StructureDetection {
24
+ components?: string;
25
+ services?: string;
26
+ schema?: string;
27
+ types?: string;
28
+ hooks?: string;
29
+ utils?: string;
30
+ api?: string;
31
+ }
32
+
33
+ // Store last detection result for extended info
34
+ let lastDetectionResult: UnifiedDetectionResult | null = null;
35
+
36
+ /**
37
+ * Universal stack detection using the new modular detector system.
38
+ * Supports: Node.js, .NET, Python, Go, Rust, Java/Kotlin, Flutter, and more.
39
+ */
40
+ async function detectStack(): Promise<StackDetection> {
41
+ const result = await detectStackLegacy(process.cwd());
42
+ return result.stack;
43
+ }
44
+
45
+ /**
46
+ * Structure detection using the new modular detector system.
47
+ */
48
+ async function detectStructure(): Promise<StructureDetection> {
49
+ const result = await detectStackLegacy(process.cwd());
50
+
51
+ // Map new structure keys to legacy format
52
+ const structure: StructureDetection = {};
53
+
54
+ if (result.structure.components) structure.components = result.structure.components;
55
+ if (result.structure.services) structure.services = result.structure.services;
56
+ if (result.structure.schema) structure.schema = result.structure.schema;
57
+ if (result.structure.types) structure.types = result.structure.types;
58
+ if (result.structure.hooks) structure.hooks = result.structure.hooks;
59
+ if (result.structure.utils) structure.utils = result.structure.utils;
60
+ if (result.structure.api) structure.api = result.structure.api;
61
+
62
+ return structure;
63
+ }
64
+
65
+ /**
66
+ * Full detection with all details (for extended display)
67
+ */
68
+ async function detectFull(): Promise<UnifiedDetectionResult> {
69
+ const result = await detectUniversal(process.cwd());
70
+ lastDetectionResult = result;
71
+ return result;
72
+ }
73
+
74
+ export async function discoverStart(json: boolean = false): Promise<void> {
75
+ initSchema();
76
+ const db = getDb();
77
+
78
+ // Verificar se ja foi descoberto
79
+ const existing = db.query("SELECT * FROM project WHERE id = 'default'").get();
80
+ if (existing) {
81
+ console.log("\nProjeto ja foi descoberto.");
82
+ console.log("Use: discover show para ver detalhes");
83
+ console.log("Ou: discover reset para refazer\n");
84
+ return;
85
+ }
86
+
87
+ console.log("\nIniciando descoberta do projeto...\n");
88
+
89
+ if (!json) {
90
+ console.log("[1/2] Detectando stack tecnológico (universal)...");
91
+ }
92
+
93
+ // Usar novo sistema de detecção universal
94
+ const fullResult = await detectFull();
95
+
96
+ // Converter para formato legacy para compatibilidade
97
+ const stack = await detectStack();
98
+ const structure = await detectStructure();
99
+
100
+ if (!json) {
101
+ console.log(` ✓ ${fullResult.ecosystems.length > 0 ? fullResult.ecosystems.join(", ") : "Stack"} detectado\n`);
102
+ console.log("[2/2] Analisando estrutura de diretórios...");
103
+ console.log(" ✓ Estrutura mapeada\n");
104
+ }
105
+
106
+ if (json) {
107
+ console.log(JSON.stringify({
108
+ stack,
109
+ structure,
110
+ // Extended info from new detection system
111
+ extended: {
112
+ primary: fullResult.primary,
113
+ ecosystems: fullResult.ecosystems,
114
+ technologies: getDetailedTechnologies(fullResult),
115
+ configFiles: fullResult.configFiles,
116
+ fullStructure: fullResult.structure,
117
+ }
118
+ }, null, 2));
119
+ return;
120
+ }
121
+
122
+ // Use new formatted output
123
+ console.log(formatDetectionResult(fullResult));
124
+
125
+ // Show legacy structure format as well for compatibility
126
+ console.log("\n" + "═".repeat(60));
127
+ console.log("ESTRUTURA DETECTADA (Legacy)");
128
+ console.log("═".repeat(60));
129
+
130
+ if (Object.keys(structure).length === 0) {
131
+ console.log("\nNenhuma estrutura padrao detectada.");
132
+ } else {
133
+ console.log();
134
+ if (structure.components) console.log(` Componentes: ${structure.components}/`);
135
+ if (structure.services) console.log(` Services: ${structure.services}/`);
136
+ if (structure.schema) console.log(` Schema DB: ${structure.schema}/`);
137
+ if (structure.types) console.log(` Tipos: ${structure.types}/`);
138
+ if (structure.hooks) console.log(` Hooks: ${structure.hooks}/`);
139
+ if (structure.utils) console.log(` Utils: ${structure.utils}/`);
140
+ if (structure.api) console.log(` API: ${structure.api}/`);
141
+ }
142
+
143
+ console.log("\n" + "─".repeat(60));
144
+ console.log("\nPara confirmar e salvar: discover confirm");
145
+ console.log("Para ajustar stack: discover set-stack --frontend next --backend bun");
146
+ console.log("Para ajustar estrutura: discover set-structure --components src/ui\n");
147
+
148
+ // Salvar temporariamente para confirm poder usar
149
+ // Include extended info for richer context
150
+ db.run(
151
+ `INSERT OR REPLACE INTO project (id, name, stack, discovered_at, updated_at)
152
+ VALUES ('pending', 'pending', ?, datetime('now'), datetime('now'))`,
153
+ [JSON.stringify({
154
+ stack,
155
+ structure,
156
+ extended: {
157
+ primary: fullResult.primary,
158
+ ecosystems: fullResult.ecosystems,
159
+ allTechnologies: fullResult.allTechnologies,
160
+ configFiles: fullResult.configFiles,
161
+ fullStructure: fullResult.structure,
162
+ }
163
+ })]
164
+ );
165
+ }
166
+
167
+ export function discoverConfirm(): void {
168
+ initSchema();
169
+ const db = getDb();
170
+
171
+ const pending = db.query("SELECT * FROM project WHERE id = 'pending'").get() as any;
172
+ if (!pending) {
173
+ console.error("\nNenhuma descoberta pendente.");
174
+ console.error("Execute: discover start primeiro\n");
175
+ process.exit(1);
176
+ }
177
+
178
+ const data = JSON.parse(pending.stack);
179
+ const now = new Date().toISOString();
180
+
181
+ // Mover de pending para default
182
+ db.run("DELETE FROM project WHERE id = 'pending'");
183
+ db.run(
184
+ `INSERT INTO project (id, name, stack, discovered_at, updated_at)
185
+ VALUES ('default', ?, ?, ?, ?)`,
186
+ ["Projeto", JSON.stringify(data.stack), now, now]
187
+ );
188
+
189
+ // Criar standards baseados na estrutura detectada
190
+ const structure = data.structure as StructureDetection;
191
+
192
+ if (structure.components) {
193
+ db.run(
194
+ `INSERT INTO standards (category, scope, rule, examples, source, created_at)
195
+ VALUES ('structure', 'frontend', ?, ?, 'detected', ?)`,
196
+ [
197
+ `Componentes devem estar em ${structure.components}/`,
198
+ JSON.stringify([`${structure.components}/Button.tsx`]),
199
+ now,
200
+ ]
201
+ );
202
+ }
203
+
204
+ if (structure.services) {
205
+ db.run(
206
+ `INSERT INTO standards (category, scope, rule, examples, source, created_at)
207
+ VALUES ('structure', 'backend', ?, ?, 'detected', ?)`,
208
+ [
209
+ `Services devem estar em ${structure.services}/`,
210
+ JSON.stringify([`${structure.services}/authService.ts`]),
211
+ now,
212
+ ]
213
+ );
214
+ }
215
+
216
+ if (structure.schema) {
217
+ db.run(
218
+ `INSERT INTO standards (category, scope, rule, examples, source, created_at)
219
+ VALUES ('structure', 'database', ?, ?, 'detected', ?)`,
220
+ [
221
+ `Schema do banco deve estar em ${structure.schema}/`,
222
+ JSON.stringify([`${structure.schema}/users.ts`]),
223
+ now,
224
+ ]
225
+ );
226
+ }
227
+
228
+ if (structure.types) {
229
+ db.run(
230
+ `INSERT INTO standards (category, scope, rule, examples, source, created_at)
231
+ VALUES ('structure', 'all', ?, ?, 'detected', ?)`,
232
+ [
233
+ `Tipos devem estar em ${structure.types}/`,
234
+ JSON.stringify([`${structure.types}/user.ts`]),
235
+ now,
236
+ ]
237
+ );
238
+ }
239
+
240
+ // Standards de nomenclatura padrao
241
+ db.run(
242
+ `INSERT INTO standards (category, scope, rule, examples, anti_examples, source, created_at)
243
+ VALUES ('naming', 'frontend', ?, ?, ?, 'detected', ?)`,
244
+ [
245
+ "Componentes usam PascalCase",
246
+ JSON.stringify(["Button.tsx", "LoginForm.tsx"]),
247
+ JSON.stringify(["button.tsx", "login-form.tsx"]),
248
+ now,
249
+ ]
250
+ );
251
+
252
+ db.run(
253
+ `INSERT INTO standards (category, scope, rule, examples, anti_examples, source, created_at)
254
+ VALUES ('naming', 'backend', ?, ?, ?, 'detected', ?)`,
255
+ [
256
+ "Services usam camelCase",
257
+ JSON.stringify(["authService.ts", "userService.ts"]),
258
+ JSON.stringify(["AuthService.ts", "auth-service.ts"]),
259
+ now,
260
+ ]
261
+ );
262
+
263
+ // Gerar arquivo standards.md
264
+ generateStandardsMarkdown();
265
+
266
+ const standardsCount = db.query("SELECT COUNT(*) as c FROM standards").get() as any;
267
+
268
+ console.log("\nProjeto descoberto e configurado!");
269
+ console.log(`Standards criados: ${standardsCount.c}`);
270
+ console.log("\nArquivo gerado: .codexa/standards.md");
271
+ console.log("\nProximo passo: /codexa:feature para iniciar uma feature\n");
272
+ }
273
+
274
+ export function discoverShow(json: boolean = false): void {
275
+ initSchema();
276
+ const db = getDb();
277
+
278
+ const project = db.query("SELECT * FROM project WHERE id = 'default'").get() as any;
279
+
280
+ if (!project) {
281
+ console.error("\nProjeto nao descoberto.");
282
+ console.error("Execute: discover start\n");
283
+ process.exit(1);
284
+ }
285
+
286
+ const standards = db.query("SELECT * FROM standards ORDER BY category, scope").all() as any[];
287
+ const stack = JSON.parse(project.stack);
288
+
289
+ if (json) {
290
+ console.log(JSON.stringify({ project, stack, standards }, null, 2));
291
+ return;
292
+ }
293
+
294
+ console.log("\n" + "═".repeat(60));
295
+ console.log("PROJETO CONFIGURADO");
296
+ console.log("═".repeat(60));
297
+
298
+ console.log("\nStack:");
299
+ for (const [key, value] of Object.entries(stack)) {
300
+ if (value) console.log(` ${key}: ${value}`);
301
+ }
302
+
303
+ console.log(`\nStandards (${standards.length}):`);
304
+ console.log("─".repeat(60));
305
+
306
+ let currentCategory = "";
307
+ for (const std of standards) {
308
+ if (std.category !== currentCategory) {
309
+ currentCategory = std.category;
310
+ console.log(`\n[${currentCategory.toUpperCase()}]`);
311
+ }
312
+ console.log(` (${std.scope}) ${std.rule}`);
313
+ }
314
+
315
+ console.log("\n" + "─".repeat(60));
316
+ console.log("Arquivo: .codexa/standards.md\n");
317
+ }
318
+
319
+ export function discoverSetStack(options: {
320
+ frontend?: string;
321
+ backend?: string;
322
+ database?: string;
323
+ orm?: string;
324
+ styling?: string;
325
+ auth?: string;
326
+ testing?: string;
327
+ }): void {
328
+ initSchema();
329
+ const db = getDb();
330
+
331
+ let pending = db.query("SELECT * FROM project WHERE id = 'pending'").get() as any;
332
+ let data: any;
333
+
334
+ if (pending) {
335
+ data = JSON.parse(pending.stack);
336
+ } else {
337
+ data = { stack: {}, structure: {} };
338
+ }
339
+
340
+ // Atualizar stack com opcoes fornecidas
341
+ if (options.frontend) data.stack.frontend = options.frontend;
342
+ if (options.backend) data.stack.backend = options.backend;
343
+ if (options.database) data.stack.database = options.database;
344
+ if (options.orm) data.stack.orm = options.orm;
345
+ if (options.styling) data.stack.styling = options.styling;
346
+ if (options.auth) data.stack.auth = options.auth;
347
+ if (options.testing) data.stack.testing = options.testing;
348
+
349
+ db.run(
350
+ `INSERT OR REPLACE INTO project (id, name, stack, discovered_at, updated_at)
351
+ VALUES ('pending', 'pending', ?, datetime('now'), datetime('now'))`,
352
+ [JSON.stringify(data)]
353
+ );
354
+
355
+ console.log("\nStack atualizado:");
356
+ for (const [key, value] of Object.entries(data.stack)) {
357
+ if (value) console.log(` ${key}: ${value}`);
358
+ }
359
+ console.log("\nPara confirmar: discover confirm\n");
360
+ }
361
+
362
+ export function discoverReset(): void {
363
+ initSchema();
364
+ const db = getDb();
365
+
366
+ db.run("DELETE FROM project");
367
+ db.run("DELETE FROM standards");
368
+
369
+ console.log("\nDescoberta resetada.");
370
+ console.log("Execute: discover start para refazer\n");
371
+ }
372
+
373
+ export async function discoverRefresh(options: { force?: boolean } = {}): Promise<void> {
374
+ initSchema();
375
+ const db = getDb();
376
+
377
+ const currentProject = db.query("SELECT * FROM project WHERE id = 'default'").get() as any;
378
+ if (!currentProject) {
379
+ console.error("\nProjeto nao descoberto.");
380
+ console.error("Execute: discover start primeiro\n");
381
+ process.exit(1);
382
+ }
383
+
384
+ const currentStack = JSON.parse(currentProject.stack);
385
+ const newStack = await detectStack();
386
+ const newStructure = await detectStructure();
387
+
388
+ // Comparar stacks
389
+ const stackChanges: { key: string; from: string | undefined; to: string | undefined }[] = [];
390
+ const allKeys = new Set([...Object.keys(currentStack), ...Object.keys(newStack)]);
391
+
392
+ for (const key of allKeys) {
393
+ const current = currentStack[key as keyof StackDetection];
394
+ const detected = newStack[key as keyof StackDetection];
395
+ if (current !== detected) {
396
+ stackChanges.push({ key, from: current, to: detected });
397
+ }
398
+ }
399
+
400
+ if (stackChanges.length === 0) {
401
+ console.log("\n✓ Stack continua o mesmo. Nenhuma mudanca detectada.\n");
402
+ return;
403
+ }
404
+
405
+ console.log("\n" + "═".repeat(60));
406
+ console.log("MUDANCAS DETECTADAS NO STACK");
407
+ console.log("═".repeat(60) + "\n");
408
+
409
+ for (const change of stackChanges) {
410
+ const from = change.from || "(nao definido)";
411
+ const to = change.to || "(removido)";
412
+ console.log(` ${change.key}: ${from} → ${to}`);
413
+ }
414
+
415
+ if (options.force) {
416
+ // Aplicar mudanças
417
+ const now = new Date().toISOString();
418
+
419
+ db.run(
420
+ `UPDATE project SET stack = ?, updated_at = ? WHERE id = 'default'`,
421
+ [JSON.stringify(newStack), now]
422
+ );
423
+
424
+ // Atualizar standards de stack
425
+ for (const change of stackChanges) {
426
+ if (change.to) {
427
+ // Adicionar ou atualizar standard de stack
428
+ const existing = db.query(
429
+ `SELECT id FROM standards WHERE category = 'stack' AND rule LIKE ?`
430
+ ).get(`%${change.key}%`) as any;
431
+
432
+ if (existing) {
433
+ db.run(
434
+ `UPDATE standards SET rule = ?, updated_at = ? WHERE id = ?`,
435
+ [`${change.key}: ${change.to}`, now, existing.id]
436
+ );
437
+ } else {
438
+ db.run(
439
+ `INSERT INTO standards (category, scope, rule, enforcement, source, created_at)
440
+ VALUES ('stack', 'all', ?, 'required', 'refresh', ?)`,
441
+ [`${change.key}: ${change.to}`, now]
442
+ );
443
+ }
444
+ }
445
+ }
446
+
447
+ // Regenerar standards.md
448
+ generateStandardsMarkdown();
449
+
450
+ console.log("\n✓ Stack atualizado com sucesso!");
451
+ console.log("✓ Standards regenerados\n");
452
+ } else {
453
+ console.log("\n" + "─".repeat(60));
454
+ console.log("Para aplicar as mudancas: discover refresh --force\n");
455
+ }
456
+ }
457
+
458
+ // Exportar detectStack para uso interno
459
+ export { detectStack, detectStructure, detectFull };
460
+
461
+ // Re-export detector utilities for advanced usage
462
+ export {
463
+ detectUniversal,
464
+ formatDetectionResult,
465
+ getDetailedTechnologies,
466
+ } from "../detectors/loader";
467
+
468
+ function generateStandardsMarkdown(): void {
469
+ const db = getDb();
470
+ const project = db.query("SELECT * FROM project WHERE id = 'default'").get() as any;
471
+ const standards = db.query("SELECT * FROM standards ORDER BY category, scope").all() as any[];
472
+
473
+ if (!project) return;
474
+
475
+ const stack = JSON.parse(project.stack);
476
+ let md = `# Standards do Projeto
477
+
478
+ > Gerado automaticamente do SQLite. Nao edite manualmente.
479
+ > Para modificar, use os comandos CLI.
480
+ > Gerado em: ${new Date().toISOString()}
481
+
482
+ ## Stack
483
+
484
+ `;
485
+
486
+ for (const [key, value] of Object.entries(stack)) {
487
+ if (value) md += `- **${key}:** ${value}\n`;
488
+ }
489
+
490
+ // Agrupar standards por categoria
491
+ const byCategory: Record<string, any[]> = {};
492
+ for (const std of standards) {
493
+ if (!byCategory[std.category]) byCategory[std.category] = [];
494
+ byCategory[std.category].push(std);
495
+ }
496
+
497
+ for (const [category, stds] of Object.entries(byCategory)) {
498
+ md += `\n## ${category.charAt(0).toUpperCase() + category.slice(1)}\n\n`;
499
+
500
+ for (const std of stds) {
501
+ const enforcement = std.enforcement === "required" ? "OBRIGATORIO" : "Recomendado";
502
+ md += `### ${std.rule}\n\n`;
503
+ md += `- **Escopo:** ${std.scope}\n`;
504
+ md += `- **Enforcement:** ${enforcement}\n`;
505
+
506
+ if (std.examples) {
507
+ const examples = JSON.parse(std.examples);
508
+ md += `- **Exemplos:**\n`;
509
+ for (const ex of examples) {
510
+ md += ` - \`${ex}\`\n`;
511
+ }
512
+ }
513
+
514
+ if (std.anti_examples) {
515
+ const antiExamples = JSON.parse(std.anti_examples);
516
+ md += `- **Nao fazer:**\n`;
517
+ for (const ex of antiExamples) {
518
+ md += ` - ~~\`${ex}\`~~\n`;
519
+ }
520
+ }
521
+
522
+ md += "\n";
523
+ }
524
+ }
525
+
526
+ // Garantir que diretorio existe
527
+ const mdPath = join(process.cwd(), ".codexa", "standards.md");
528
+ writeFileSync(mdPath, md);
529
+ }
530
+
531
+ export { generateStandardsMarkdown };
532
+
533
+ // ============================================================
534
+ // PATTERN EXTRACTION (v7.4)
535
+ // ============================================================
536
+
537
+ interface PatternStructure {
538
+ imports: {
539
+ order: string[];
540
+ common: string[];
541
+ conditional: string[];
542
+ };
543
+ exports: {
544
+ style: "named" | "default" | "both";
545
+ async: boolean;
546
+ };
547
+ patterns: {
548
+ hooks_used: string[];
549
+ props_style: "interface" | "type" | "inline";
550
+ error_handling: string;
551
+ data_fetching: string;
552
+ };
553
+ conventions: {
554
+ file_structure: string[];
555
+ naming: {
556
+ functions: string;
557
+ variables: string;
558
+ types: string;
559
+ };
560
+ };
561
+ }
562
+
563
+ interface AntiPattern {
564
+ description: string;
565
+ example: string;
566
+ found_in: string[];
567
+ recommendation?: string;
568
+ }
569
+
570
+ interface ImplementationPattern {
571
+ category: string;
572
+ name: string;
573
+ scope: string;
574
+ applies_to: string;
575
+ structure: PatternStructure;
576
+ template: string;
577
+ examples: { path: string; relevance: number }[];
578
+ anti_patterns: AntiPattern[];
579
+ confidence: number;
580
+ extracted_from: number;
581
+ }
582
+
583
+ export function discoverPatterns(options: { category?: string; json?: boolean } = {}): void {
584
+ initSchema();
585
+ const db = getDb();
586
+
587
+ let query = "SELECT * FROM implementation_patterns";
588
+ const params: string[] = [];
589
+
590
+ if (options.category) {
591
+ query += " WHERE category = ?";
592
+ params.push(options.category);
593
+ }
594
+
595
+ query += " ORDER BY category, name";
596
+
597
+ const patterns = db.query(query).all(...params) as any[];
598
+
599
+ if (patterns.length === 0) {
600
+ console.log("\nNenhum pattern extraido ainda.");
601
+ console.log("Execute: discover start para extrair patterns do codigo\n");
602
+ return;
603
+ }
604
+
605
+ if (options.json) {
606
+ console.log(JSON.stringify(patterns.map(p => ({
607
+ ...p,
608
+ structure: JSON.parse(p.structure || "{}"),
609
+ examples: JSON.parse(p.examples || "[]"),
610
+ anti_patterns: JSON.parse(p.anti_patterns || "[]"),
611
+ })), null, 2));
612
+ return;
613
+ }
614
+
615
+ console.log("\n" + "═".repeat(70));
616
+ console.log("IMPLEMENTATION PATTERNS EXTRAIDOS");
617
+ console.log("═".repeat(70) + "\n");
618
+
619
+ console.log("┌" + "─".repeat(20) + "┬" + "─".repeat(25) + "┬" + "─".repeat(10) + "┬" + "─".repeat(10) + "┐");
620
+ console.log("│ Categoria │ Nome │ Arquivos │ Confianca│");
621
+ console.log("├" + "─".repeat(20) + "┼" + "─".repeat(25) + "┼" + "─".repeat(10) + "┼" + "─".repeat(10) + "┤");
622
+
623
+ for (const p of patterns) {
624
+ const cat = p.category.padEnd(18);
625
+ const name = p.name.substring(0, 23).padEnd(23);
626
+ const files = String(p.extracted_from || 0).padStart(8);
627
+ const conf = ((p.confidence || 0) * 100).toFixed(0).padStart(7) + "%";
628
+ console.log(`│ ${cat} │ ${name} │ ${files} │ ${conf} │`);
629
+ }
630
+
631
+ console.log("└" + "─".repeat(20) + "┴" + "─".repeat(25) + "┴" + "─".repeat(10) + "┴" + "─".repeat(10) + "┘");
632
+
633
+ // Contar anti-patterns
634
+ let totalAntiPatterns = 0;
635
+ for (const p of patterns) {
636
+ const antiPatterns = JSON.parse(p.anti_patterns || "[]");
637
+ totalAntiPatterns += antiPatterns.length;
638
+ }
639
+
640
+ if (totalAntiPatterns > 0) {
641
+ console.log(`\n⚠ Anti-patterns identificados: ${totalAntiPatterns}`);
642
+ console.log(" Use: discover patterns --show <name> para ver detalhes");
643
+ }
644
+
645
+ console.log("\n" + "─".repeat(70));
646
+ console.log("Para ver detalhes: discover patterns --show <name>");
647
+ console.log("Para exportar: discover export-patterns\n");
648
+ }
649
+
650
+ export function discoverPatternsShow(name: string, json: boolean = false): void {
651
+ initSchema();
652
+ const db = getDb();
653
+
654
+ const pattern = db.query("SELECT * FROM implementation_patterns WHERE name = ?").get(name) as any;
655
+
656
+ if (!pattern) {
657
+ console.error(`\nPattern '${name}' nao encontrado.`);
658
+ console.error("Use: discover patterns para listar todos\n");
659
+ process.exit(1);
660
+ }
661
+
662
+ const structure = JSON.parse(pattern.structure || "{}");
663
+ const examples = JSON.parse(pattern.examples || "[]");
664
+ const antiPatterns = JSON.parse(pattern.anti_patterns || "[]");
665
+
666
+ if (json) {
667
+ console.log(JSON.stringify({
668
+ ...pattern,
669
+ structure,
670
+ examples,
671
+ anti_patterns: antiPatterns,
672
+ }, null, 2));
673
+ return;
674
+ }
675
+
676
+ console.log("\n" + "═".repeat(70));
677
+ console.log(`PATTERN: ${pattern.name}`);
678
+ console.log("═".repeat(70));
679
+
680
+ console.log(`\nCategoria: ${pattern.category}`);
681
+ console.log(`Escopo: ${pattern.scope}`);
682
+ console.log(`Aplica-se a: ${pattern.applies_to}`);
683
+ console.log(`Confianca: ${((pattern.confidence || 0) * 100).toFixed(0)}%`);
684
+ console.log(`Extraido de: ${pattern.extracted_from} arquivos`);
685
+
686
+ console.log("\n" + "─".repeat(70));
687
+ console.log("ESTRUTURA");
688
+ console.log("─".repeat(70));
689
+
690
+ if (structure.imports) {
691
+ console.log("\nImports:");
692
+ console.log(` Ordem: ${structure.imports.order?.join(" → ") || "N/A"}`);
693
+ console.log(` Comuns: ${structure.imports.common?.join(", ") || "N/A"}`);
694
+ }
695
+
696
+ if (structure.exports) {
697
+ console.log("\nExports:");
698
+ console.log(` Estilo: ${structure.exports.style || "N/A"}`);
699
+ console.log(` Async: ${structure.exports.async ? "Sim" : "Nao"}`);
700
+ }
701
+
702
+ if (structure.patterns) {
703
+ console.log("\nPadroes:");
704
+ console.log(` Hooks: ${structure.patterns.hooks_used?.join(", ") || "N/A"}`);
705
+ console.log(` Props: ${structure.patterns.props_style || "N/A"}`);
706
+ console.log(` Erro: ${structure.patterns.error_handling || "N/A"}`);
707
+ console.log(` Data: ${structure.patterns.data_fetching || "N/A"}`);
708
+ }
709
+
710
+ console.log("\n" + "─".repeat(70));
711
+ console.log("TEMPLATE");
712
+ console.log("─".repeat(70));
713
+ console.log("\n" + pattern.template);
714
+
715
+ console.log("\n" + "─".repeat(70));
716
+ console.log("EXEMPLOS DE REFERENCIA");
717
+ console.log("─".repeat(70));
718
+
719
+ for (const ex of examples.slice(0, 5)) {
720
+ const relevance = ((ex.relevance || 0) * 100).toFixed(0);
721
+ console.log(` ${ex.path} (${relevance}% similar)`);
722
+ }
723
+
724
+ if (antiPatterns.length > 0) {
725
+ console.log("\n" + "─".repeat(70));
726
+ console.log("⚠ ANTI-PATTERNS IDENTIFICADOS");
727
+ console.log("─".repeat(70));
728
+
729
+ for (const ap of antiPatterns) {
730
+ console.log(`\n ❌ ${ap.description}`);
731
+ console.log(` Encontrado em: ${ap.found_in?.join(", ") || "N/A"}`);
732
+ if (ap.recommendation) {
733
+ console.log(` Recomendacao: ${ap.recommendation}`);
734
+ }
735
+ }
736
+ }
737
+
738
+ console.log("\n");
739
+ }
740
+
741
+ export function discoverPatternAdd(options: {
742
+ category: string;
743
+ name: string;
744
+ scope: string;
745
+ appliesTo: string;
746
+ structure: string;
747
+ template: string;
748
+ examples: string;
749
+ antiPatterns?: string;
750
+ confidence?: number;
751
+ extractedFrom?: number;
752
+ }): void {
753
+ initSchema();
754
+ const db = getDb();
755
+
756
+ // Verificar se pattern ja existe
757
+ const existing = db.query("SELECT id FROM implementation_patterns WHERE name = ?").get(options.name);
758
+ if (existing) {
759
+ console.error(`\nPattern '${options.name}' ja existe.`);
760
+ console.error("Use: discover pattern-edit para modificar\n");
761
+ process.exit(1);
762
+ }
763
+
764
+ const now = new Date().toISOString();
765
+
766
+ db.run(
767
+ `INSERT INTO implementation_patterns
768
+ (category, name, scope, applies_to, structure, template, examples, anti_patterns, confidence, extracted_from, created_at, updated_at)
769
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
770
+ [
771
+ options.category,
772
+ options.name,
773
+ options.scope,
774
+ options.appliesTo,
775
+ options.structure,
776
+ options.template,
777
+ options.examples,
778
+ options.antiPatterns || "[]",
779
+ options.confidence || 0.8,
780
+ options.extractedFrom || 1,
781
+ now,
782
+ now,
783
+ ]
784
+ );
785
+
786
+ // Regenerar patterns.md
787
+ generatePatternsMarkdown();
788
+
789
+ console.log(`\n✓ Pattern '${options.name}' adicionado com sucesso!`);
790
+ console.log("Arquivo atualizado: .codexa/patterns.md\n");
791
+ }
792
+
793
+ export function discoverPatternEdit(name: string, options: {
794
+ category?: string;
795
+ scope?: string;
796
+ appliesTo?: string;
797
+ structure?: string;
798
+ template?: string;
799
+ examples?: string;
800
+ antiPatterns?: string;
801
+ confidence?: number;
802
+ }): void {
803
+ initSchema();
804
+ const db = getDb();
805
+
806
+ const existing = db.query("SELECT * FROM implementation_patterns WHERE name = ?").get(name) as any;
807
+ if (!existing) {
808
+ console.error(`\nPattern '${name}' nao encontrado.`);
809
+ console.error("Use: discover patterns para listar todos\n");
810
+ process.exit(1);
811
+ }
812
+
813
+ const updates: string[] = [];
814
+ const params: any[] = [];
815
+
816
+ if (options.category) {
817
+ updates.push("category = ?");
818
+ params.push(options.category);
819
+ }
820
+ if (options.scope) {
821
+ updates.push("scope = ?");
822
+ params.push(options.scope);
823
+ }
824
+ if (options.appliesTo) {
825
+ updates.push("applies_to = ?");
826
+ params.push(options.appliesTo);
827
+ }
828
+ if (options.structure) {
829
+ updates.push("structure = ?");
830
+ params.push(options.structure);
831
+ }
832
+ if (options.template) {
833
+ updates.push("template = ?");
834
+ params.push(options.template);
835
+ }
836
+ if (options.examples) {
837
+ updates.push("examples = ?");
838
+ params.push(options.examples);
839
+ }
840
+ if (options.antiPatterns) {
841
+ updates.push("anti_patterns = ?");
842
+ params.push(options.antiPatterns);
843
+ }
844
+ if (options.confidence !== undefined) {
845
+ updates.push("confidence = ?");
846
+ params.push(options.confidence);
847
+ }
848
+
849
+ if (updates.length === 0) {
850
+ console.error("\nNenhuma opcao de atualizacao fornecida.\n");
851
+ process.exit(1);
852
+ }
853
+
854
+ updates.push("updated_at = ?");
855
+ params.push(new Date().toISOString());
856
+ params.push(name);
857
+
858
+ db.run(
859
+ `UPDATE implementation_patterns SET ${updates.join(", ")} WHERE name = ?`,
860
+ params
861
+ );
862
+
863
+ // Regenerar patterns.md
864
+ generatePatternsMarkdown();
865
+
866
+ console.log(`\n✓ Pattern '${name}' atualizado com sucesso!`);
867
+ console.log("Arquivo atualizado: .codexa/patterns.md\n");
868
+ }
869
+
870
+ export function discoverPatternRemove(name: string): void {
871
+ initSchema();
872
+ const db = getDb();
873
+
874
+ const existing = db.query("SELECT id FROM implementation_patterns WHERE name = ?").get(name);
875
+ if (!existing) {
876
+ console.error(`\nPattern '${name}' nao encontrado.\n`);
877
+ process.exit(1);
878
+ }
879
+
880
+ db.run("DELETE FROM implementation_patterns WHERE name = ?", [name]);
881
+
882
+ // Regenerar patterns.md
883
+ generatePatternsMarkdown();
884
+
885
+ console.log(`\n✓ Pattern '${name}' removido com sucesso!`);
886
+ console.log("Arquivo atualizado: .codexa/patterns.md\n");
887
+ }
888
+
889
+ export function discoverRefreshPatterns(): void {
890
+ initSchema();
891
+ const db = getDb();
892
+
893
+ // Verificar se projeto foi descoberto
894
+ const project = db.query("SELECT * FROM project WHERE id = 'default'").get() as any;
895
+ if (!project) {
896
+ console.error("\nProjeto nao descoberto.");
897
+ console.error("Execute: discover start primeiro\n");
898
+ process.exit(1);
899
+ }
900
+
901
+ console.log("\n⚠ Refresh de patterns requer analise manual do codigo.");
902
+ console.log("Use o skill /codexa:discover para extrair patterns automaticamente.");
903
+ console.log("\nAlternativamente, adicione patterns manualmente:");
904
+ console.log(" discover pattern-add --category component --name my-pattern ...\n");
905
+ }
906
+
907
+ export function generatePatternsMarkdown(): void {
908
+ const db = getDb();
909
+ const patterns = db.query("SELECT * FROM implementation_patterns ORDER BY category, name").all() as any[];
910
+
911
+ if (patterns.length === 0) return;
912
+
913
+ let md = `# Implementation Patterns
914
+
915
+ > Gerado automaticamente do SQLite. Nao edite manualmente.
916
+ > Para modificar, use os comandos CLI.
917
+ > Gerado em: ${new Date().toISOString()}
918
+
919
+ ## Resumo
920
+
921
+ | Categoria | Nome | Arquivos | Confianca |
922
+ |-----------|------|----------|-----------|
923
+ `;
924
+
925
+ for (const p of patterns) {
926
+ const conf = ((p.confidence || 0) * 100).toFixed(0);
927
+ md += `| ${p.category} | ${p.name} | ${p.extracted_from || 0} | ${conf}% |\n`;
928
+ }
929
+
930
+ // Agrupar por categoria
931
+ const byCategory: Record<string, any[]> = {};
932
+ for (const p of patterns) {
933
+ if (!byCategory[p.category]) byCategory[p.category] = [];
934
+ byCategory[p.category].push(p);
935
+ }
936
+
937
+ for (const [category, categoryPatterns] of Object.entries(byCategory)) {
938
+ md += `\n## ${category.charAt(0).toUpperCase() + category.slice(1)}\n`;
939
+
940
+ for (const p of categoryPatterns) {
941
+ const structure = JSON.parse(p.structure || "{}");
942
+ const examples = JSON.parse(p.examples || "[]");
943
+ const antiPatterns = JSON.parse(p.anti_patterns || "[]");
944
+
945
+ md += `\n### ${p.name}\n\n`;
946
+ md += `- **Escopo:** ${p.scope}\n`;
947
+ md += `- **Aplica-se a:** \`${p.applies_to}\`\n`;
948
+ md += `- **Confianca:** ${((p.confidence || 0) * 100).toFixed(0)}%\n`;
949
+ md += `- **Extraido de:** ${p.extracted_from || 0} arquivos\n`;
950
+
951
+ if (structure.imports?.common?.length > 0) {
952
+ md += `\n**Imports comuns:**\n`;
953
+ for (const imp of structure.imports.common) {
954
+ md += `- \`${imp}\`\n`;
955
+ }
956
+ }
957
+
958
+ if (structure.patterns?.hooks_used?.length > 0) {
959
+ md += `\n**Hooks utilizados:** ${structure.patterns.hooks_used.join(", ")}\n`;
960
+ }
961
+
962
+ md += `\n**Template:**\n\n\`\`\`typescript\n${p.template}\n\`\`\`\n`;
963
+
964
+ if (examples.length > 0) {
965
+ md += `\n**Exemplos de referencia:**\n`;
966
+ for (const ex of examples.slice(0, 5)) {
967
+ const rel = ((ex.relevance || 0) * 100).toFixed(0);
968
+ md += `- \`${ex.path}\` (${rel}% similar)\n`;
969
+ }
970
+ }
971
+
972
+ if (antiPatterns.length > 0) {
973
+ md += `\n**⚠ Anti-patterns:**\n`;
974
+ for (const ap of antiPatterns) {
975
+ md += `- ❌ ${ap.description}\n`;
976
+ if (ap.found_in?.length > 0) {
977
+ md += ` - Encontrado em: ${ap.found_in.join(", ")}\n`;
978
+ }
979
+ if (ap.recommendation) {
980
+ md += ` - Recomendacao: ${ap.recommendation}\n`;
981
+ }
982
+ }
983
+ }
984
+ }
985
+ }
986
+
987
+ // Garantir que diretorio existe
988
+ const dir = join(process.cwd(), ".codexa");
989
+ if (!existsSync(dir)) {
990
+ mkdirSync(dir, { recursive: true });
991
+ }
992
+
993
+ const mdPath = join(dir, "patterns.md");
994
+ writeFileSync(mdPath, md);
995
+ }
996
+
997
+ export function discoverExportPatterns(): void {
998
+ generatePatternsMarkdown();
999
+ console.log("\n✓ Arquivo .codexa/patterns.md regenerado!\n");
1000
+ }