@codexa/cli 8.5.0 → 8.6.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.
package/db/schema.ts CHANGED
@@ -1,788 +1,719 @@
1
- import { getDb } from "./connection";
2
-
3
- export function initSchema(): void {
4
- const db = getDb();
5
-
6
- db.exec(`
7
- -- 1. Feature sendo desenvolvida
8
- CREATE TABLE IF NOT EXISTS specs (
9
- id TEXT PRIMARY KEY,
10
- name TEXT NOT NULL,
11
- phase TEXT NOT NULL DEFAULT 'planning',
12
- approved_at TEXT,
13
- created_at TEXT DEFAULT CURRENT_TIMESTAMP,
14
- updated_at TEXT
15
- );
16
-
17
- -- 2. Contexto compartilhado (o coracao do sistema)
18
- CREATE TABLE IF NOT EXISTS context (
19
- spec_id TEXT PRIMARY KEY REFERENCES specs(id),
20
- objective TEXT NOT NULL,
21
- approach TEXT,
22
- constraints TEXT,
23
- patterns TEXT,
24
- current_task INTEGER,
25
- total_tasks INTEGER,
26
- last_checkpoint TEXT,
27
- updated_at TEXT
28
- );
29
-
30
- -- 3. Tarefas com dependencias (para paralelizacao)
31
- CREATE TABLE IF NOT EXISTS tasks (
32
- id INTEGER PRIMARY KEY AUTOINCREMENT,
33
- spec_id TEXT NOT NULL REFERENCES specs(id),
34
- number INTEGER NOT NULL,
35
- name TEXT NOT NULL,
36
- depends_on TEXT,
37
- can_parallel INTEGER DEFAULT 1,
38
- agent TEXT,
39
- files TEXT,
40
- status TEXT DEFAULT 'pending',
41
- checkpoint TEXT,
42
- completed_at TEXT,
43
- UNIQUE(spec_id, number)
44
- );
45
-
46
- -- 4. Decisoes (registro critico)
47
- CREATE TABLE IF NOT EXISTS decisions (
48
- id TEXT PRIMARY KEY,
49
- spec_id TEXT NOT NULL REFERENCES specs(id),
50
- task_ref INTEGER,
51
- title TEXT NOT NULL,
52
- decision TEXT NOT NULL,
53
- rationale TEXT,
54
- status TEXT DEFAULT 'active',
55
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
56
- );
57
-
58
- -- 5. Artefatos
59
- CREATE TABLE IF NOT EXISTS artifacts (
60
- id INTEGER PRIMARY KEY AUTOINCREMENT,
61
- spec_id TEXT NOT NULL REFERENCES specs(id),
62
- task_ref INTEGER NOT NULL,
63
- path TEXT NOT NULL,
64
- action TEXT NOT NULL,
65
- created_at TEXT DEFAULT CURRENT_TIMESTAMP,
66
- UNIQUE(spec_id, path)
67
- );
68
-
69
- -- 6. Review (fase REV)
70
- CREATE TABLE IF NOT EXISTS review (
71
- id INTEGER PRIMARY KEY AUTOINCREMENT,
72
- spec_id TEXT NOT NULL REFERENCES specs(id),
73
- planned_vs_done TEXT,
74
- deviations TEXT,
75
- pattern_violations TEXT,
76
- status TEXT DEFAULT 'pending',
77
- resolution TEXT,
78
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
79
- );
80
-
81
- -- 7. Snapshots (recovery)
82
- CREATE TABLE IF NOT EXISTS snapshots (
83
- id INTEGER PRIMARY KEY AUTOINCREMENT,
84
- spec_id TEXT NOT NULL REFERENCES specs(id),
85
- data TEXT NOT NULL,
86
- trigger TEXT NOT NULL,
87
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
88
- );
89
-
90
- -- 8. Projeto (informacoes detectadas no discover)
91
- CREATE TABLE IF NOT EXISTS project (
92
- id TEXT PRIMARY KEY DEFAULT 'default',
93
- name TEXT,
94
- stack TEXT NOT NULL,
95
- discovered_at TEXT,
96
- updated_at TEXT
97
- );
98
-
99
- -- 9. Standards do projeto
100
- CREATE TABLE IF NOT EXISTS standards (
101
- id INTEGER PRIMARY KEY AUTOINCREMENT,
102
- category TEXT NOT NULL,
103
- scope TEXT NOT NULL,
104
- rule TEXT NOT NULL,
105
- examples TEXT,
106
- anti_examples TEXT,
107
- enforcement TEXT DEFAULT 'required',
108
- source TEXT,
109
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
110
- );
111
-
112
- -- 10. Knowledge broadcast (compartilhamento em tempo real)
113
- CREATE TABLE IF NOT EXISTS knowledge (
114
- id INTEGER PRIMARY KEY AUTOINCREMENT,
115
- spec_id TEXT NOT NULL REFERENCES specs(id),
116
- task_origin INTEGER NOT NULL,
117
- category TEXT NOT NULL,
118
- content TEXT NOT NULL,
119
- severity TEXT DEFAULT 'info',
120
- broadcast_to TEXT DEFAULT 'all',
121
- acknowledged_by TEXT,
122
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
123
- );
124
-
125
- -- 11. Gate bypass log (auditoria de bypasses)
126
- CREATE TABLE IF NOT EXISTS gate_bypasses (
127
- id INTEGER PRIMARY KEY AUTOINCREMENT,
128
- spec_id TEXT NOT NULL REFERENCES specs(id),
129
- task_id INTEGER NOT NULL,
130
- gate_name TEXT NOT NULL,
131
- reason TEXT,
132
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
133
- );
134
-
135
- -- 12. Product context (PRD/contexto de produto)
136
- CREATE TABLE IF NOT EXISTS product_context (
137
- id TEXT PRIMARY KEY DEFAULT 'default',
138
- name TEXT NOT NULL,
139
- problem TEXT NOT NULL,
140
- solution TEXT,
141
- target_users TEXT,
142
- value_proposition TEXT,
143
- success_metrics TEXT,
144
- out_of_scope TEXT,
145
- constraints TEXT,
146
- source TEXT DEFAULT 'guide',
147
- discovered_at TEXT,
148
- updated_at TEXT
149
- );
150
-
151
- -- 13. Product goals (objetivos do produto)
152
- CREATE TABLE IF NOT EXISTS product_goals (
153
- id INTEGER PRIMARY KEY AUTOINCREMENT,
154
- product_id TEXT NOT NULL DEFAULT 'default' REFERENCES product_context(id),
155
- category TEXT NOT NULL,
156
- goal TEXT NOT NULL,
157
- priority TEXT DEFAULT 'medium',
158
- status TEXT DEFAULT 'active',
159
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
160
- );
161
-
162
- -- 14. Product features (features core do produto)
163
- CREATE TABLE IF NOT EXISTS product_features (
164
- id INTEGER PRIMARY KEY AUTOINCREMENT,
165
- product_id TEXT NOT NULL DEFAULT 'default' REFERENCES product_context(id),
166
- name TEXT NOT NULL,
167
- description TEXT,
168
- priority TEXT DEFAULT 'medium',
169
- status TEXT DEFAULT 'planned',
170
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
171
- );
172
-
173
- -- 15. Library contexts (contexto atualizado por biblioteca)
174
- CREATE TABLE IF NOT EXISTS lib_contexts (
175
- id INTEGER PRIMARY KEY AUTOINCREMENT,
176
- lib_name TEXT NOT NULL UNIQUE,
177
- version TEXT NOT NULL,
178
- context_file TEXT NOT NULL,
179
- source_url TEXT,
180
- researched_at TEXT DEFAULT CURRENT_TIMESTAMP,
181
- updated_at TEXT
182
- );
183
-
184
- -- 16. Agent lib mappings (quais libs cada agente carrega)
185
- CREATE TABLE IF NOT EXISTS agent_lib_mappings (
186
- id INTEGER PRIMARY KEY AUTOINCREMENT,
187
- agent_type TEXT NOT NULL,
188
- lib_name TEXT NOT NULL,
189
- priority INTEGER DEFAULT 0,
190
- UNIQUE(agent_type, lib_name)
191
- );
192
-
193
- -- 17. Implementation patterns (padroes extraidos do codigo - v7.4)
194
- CREATE TABLE IF NOT EXISTS implementation_patterns (
195
- id INTEGER PRIMARY KEY AUTOINCREMENT,
196
-
197
- -- Identificacao
198
- category TEXT NOT NULL, -- component, hook, service, route, schema, test, action
199
- name TEXT NOT NULL UNIQUE, -- ex: "page-component", "data-hook", "api-route"
200
-
201
- -- Contexto
202
- scope TEXT NOT NULL, -- frontend, backend, database, testing
203
- applies_to TEXT NOT NULL, -- glob pattern: "app/**/page.tsx", "src/hooks/*.ts"
204
-
205
- -- O Padrao Extraido
206
- structure TEXT NOT NULL, -- JSON com estrutura comum (imports, exports, patterns, conventions)
207
- template TEXT NOT NULL, -- Template de codigo com placeholders
208
- examples TEXT NOT NULL, -- JSON array de arquivos que seguem este padrao
209
-
210
- -- Anti-patterns identificados
211
- anti_patterns TEXT, -- JSON array de anti-patterns encontrados
212
-
213
- -- Metadados
214
- confidence REAL DEFAULT 0.8, -- 0-1, baseado em quantos arquivos seguem
215
- extracted_from INTEGER, -- Quantos arquivos foram analisados
216
-
217
- -- Auditoria
218
- created_at TEXT DEFAULT CURRENT_TIMESTAMP,
219
- updated_at TEXT
220
- );
221
-
222
- -- 18. Knowledge Graph (v8.0 - Relacoes entre entidades)
223
- CREATE TABLE IF NOT EXISTS knowledge_graph (
224
- id INTEGER PRIMARY KEY AUTOINCREMENT,
225
- spec_id TEXT REFERENCES specs(id),
226
-
227
- -- No de origem
228
- source_type TEXT NOT NULL, -- file, decision, pattern, task, artifact
229
- source_id TEXT NOT NULL,
230
-
231
- -- No de destino
232
- target_type TEXT NOT NULL,
233
- target_id TEXT NOT NULL,
234
-
235
- -- Relacao
236
- relation TEXT NOT NULL, -- uses, implements, depends_on, modifies, contradicts, extracted_from
237
-
238
- -- Metadados
239
- strength REAL DEFAULT 1.0, -- 0-1, peso da relacao
240
- metadata TEXT, -- JSON com informacoes adicionais
241
-
242
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
243
- );
244
-
245
- -- 19. Reasoning Log (v8.0 - Historico de raciocinio)
246
- CREATE TABLE IF NOT EXISTS reasoning_log (
247
- id INTEGER PRIMARY KEY AUTOINCREMENT,
248
- spec_id TEXT REFERENCES specs(id),
249
- task_id INTEGER REFERENCES tasks(id),
250
-
251
- -- O raciocinio
252
- category TEXT NOT NULL, -- decision, discovery, error, success, challenge, recommendation
253
- thought TEXT NOT NULL, -- "Escolhi X porque Y"
254
-
255
- -- Contexto
256
- related_files TEXT, -- JSON array de arquivos
257
- related_decisions TEXT, -- JSON array de decision IDs
258
- related_patterns TEXT, -- JSON array de pattern names
259
-
260
- -- Importancia
261
- importance TEXT DEFAULT 'normal', -- critical, high, normal, low
262
-
263
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
264
- );
265
-
266
- -- 20. Session Summaries (v8.0 - Resumos de sessao para continuidade)
267
- CREATE TABLE IF NOT EXISTS session_summaries (
268
- id INTEGER PRIMARY KEY AUTOINCREMENT,
269
- spec_id TEXT NOT NULL REFERENCES specs(id),
270
-
271
- -- Periodo
272
- start_time TEXT NOT NULL,
273
- end_time TEXT NOT NULL,
274
-
275
- -- Conteudo
276
- summary TEXT NOT NULL, -- O que foi feito
277
- decisions TEXT, -- JSON array de decisoes tomadas
278
- blockers TEXT, -- JSON array de problemas encontrados
279
- next_steps TEXT, -- JSON array de recomendacoes
280
-
281
- -- Estatisticas
282
- tasks_completed INTEGER DEFAULT 0,
283
- files_created INTEGER DEFAULT 0,
284
- files_modified INTEGER DEFAULT 0,
285
-
286
- created_at TEXT DEFAULT CURRENT_TIMESTAMP
287
- );
288
-
289
- -- 21. Architectural Analyses (v8.1 - migrado de architect.ts para schema central)
290
- CREATE TABLE IF NOT EXISTS architectural_analyses (
291
- id TEXT PRIMARY KEY,
292
- name TEXT NOT NULL,
293
- description TEXT NOT NULL,
294
- status TEXT NOT NULL DEFAULT 'pending',
295
-
296
- -- Conteudo JSON
297
- context TEXT,
298
- current_architecture TEXT,
299
- approach TEXT,
300
- chosen_alternative TEXT,
301
- diagrams TEXT, -- JSON array
302
- baby_steps TEXT, -- JSON array
303
- risks TEXT, -- JSON array
304
- alternatives TEXT, -- JSON array
305
- decisions TEXT, -- JSON array
306
-
307
- -- Arquivo gerado
308
- file_path TEXT,
309
-
310
- -- Timestamps
311
- created_at TEXT DEFAULT CURRENT_TIMESTAMP,
312
- updated_at TEXT,
313
- approved_at TEXT
314
- );
315
-
316
- -- 22. Project Utilities (v8.5 - DRY enforcement utility map)
317
- CREATE TABLE IF NOT EXISTS project_utilities (
318
- id INTEGER PRIMARY KEY AUTOINCREMENT,
319
- file_path TEXT NOT NULL,
320
- utility_name TEXT NOT NULL,
321
- utility_type TEXT NOT NULL,
322
- scope TEXT NOT NULL,
323
- signature TEXT,
324
- description TEXT,
325
- spec_id TEXT,
326
- task_ref INTEGER,
327
- created_at TEXT DEFAULT CURRENT_TIMESTAMP,
328
- updated_at TEXT,
329
- UNIQUE(file_path, utility_name)
330
- );
331
-
332
- -- Indices para performance
333
- CREATE INDEX IF NOT EXISTS idx_tasks_spec ON tasks(spec_id);
334
- CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
335
- CREATE INDEX IF NOT EXISTS idx_decisions_spec ON decisions(spec_id);
336
- CREATE INDEX IF NOT EXISTS idx_artifacts_spec ON artifacts(spec_id);
337
- CREATE INDEX IF NOT EXISTS idx_snapshots_spec ON snapshots(spec_id);
338
- CREATE INDEX IF NOT EXISTS idx_standards_category ON standards(category);
339
- CREATE INDEX IF NOT EXISTS idx_standards_scope ON standards(scope);
340
- CREATE INDEX IF NOT EXISTS idx_knowledge_spec ON knowledge(spec_id);
341
- CREATE INDEX IF NOT EXISTS idx_knowledge_category ON knowledge(category);
342
- CREATE INDEX IF NOT EXISTS idx_knowledge_severity ON knowledge(severity);
343
- CREATE INDEX IF NOT EXISTS idx_gate_bypasses_spec ON gate_bypasses(spec_id);
344
- CREATE INDEX IF NOT EXISTS idx_product_goals_product ON product_goals(product_id);
345
- CREATE INDEX IF NOT EXISTS idx_product_goals_category ON product_goals(category);
346
- CREATE INDEX IF NOT EXISTS idx_product_features_product ON product_features(product_id);
347
- CREATE INDEX IF NOT EXISTS idx_product_features_priority ON product_features(priority);
348
- CREATE INDEX IF NOT EXISTS idx_lib_contexts_lib ON lib_contexts(lib_name);
349
- CREATE INDEX IF NOT EXISTS idx_agent_lib_mappings_agent ON agent_lib_mappings(agent_type);
350
- CREATE INDEX IF NOT EXISTS idx_impl_patterns_category ON implementation_patterns(category);
351
- CREATE INDEX IF NOT EXISTS idx_impl_patterns_scope ON implementation_patterns(scope);
352
- CREATE INDEX IF NOT EXISTS idx_impl_patterns_name ON implementation_patterns(name);
353
-
354
- -- v8.0: Indices para Knowledge Graph
355
- CREATE INDEX IF NOT EXISTS idx_kg_source ON knowledge_graph(source_type, source_id);
356
- CREATE INDEX IF NOT EXISTS idx_kg_target ON knowledge_graph(target_type, target_id);
357
- CREATE INDEX IF NOT EXISTS idx_kg_relation ON knowledge_graph(relation);
358
- CREATE INDEX IF NOT EXISTS idx_kg_spec ON knowledge_graph(spec_id);
359
-
360
- -- v8.0: Indices para Reasoning Log
361
- CREATE INDEX IF NOT EXISTS idx_reasoning_spec ON reasoning_log(spec_id);
362
- CREATE INDEX IF NOT EXISTS idx_reasoning_task ON reasoning_log(task_id);
363
- CREATE INDEX IF NOT EXISTS idx_reasoning_category ON reasoning_log(category);
364
- CREATE INDEX IF NOT EXISTS idx_reasoning_importance ON reasoning_log(importance);
365
-
366
- -- v8.0: Indices para Session Summaries
367
- CREATE INDEX IF NOT EXISTS idx_session_spec ON session_summaries(spec_id);
368
- CREATE INDEX IF NOT EXISTS idx_session_time ON session_summaries(end_time);
369
-
370
- -- v8.1: Indices para Architectural Analyses
371
- CREATE INDEX IF NOT EXISTS idx_arch_status ON architectural_analyses(status);
372
- CREATE INDEX IF NOT EXISTS idx_arch_created ON architectural_analyses(created_at);
373
-
374
- -- v8.5: Indices para Project Utilities
375
- CREATE INDEX IF NOT EXISTS idx_utils_scope ON project_utilities(scope);
376
- CREATE INDEX IF NOT EXISTS idx_utils_name ON project_utilities(utility_name);
377
- CREATE INDEX IF NOT EXISTS idx_utils_file ON project_utilities(file_path);
378
- `);
379
-
380
- // v8.4: Migracao - adicionar analysis_id na tabela specs
381
- try {
382
- db.exec(`ALTER TABLE specs ADD COLUMN analysis_id TEXT`);
383
- } catch {
384
- // Coluna ja existe - ignorar
385
- }
386
- }
387
-
388
- export function resetDb(): void {
389
- const db = getDb();
390
- db.exec(`
391
- DROP TABLE IF EXISTS session_summaries;
392
- DROP TABLE IF EXISTS reasoning_log;
393
- DROP TABLE IF EXISTS knowledge_graph;
394
- DROP TABLE IF EXISTS implementation_patterns;
395
- DROP TABLE IF EXISTS agent_lib_mappings;
396
- DROP TABLE IF EXISTS lib_contexts;
397
- DROP TABLE IF EXISTS product_features;
398
- DROP TABLE IF EXISTS product_goals;
399
- DROP TABLE IF EXISTS product_context;
400
- DROP TABLE IF EXISTS gate_bypasses;
401
- DROP TABLE IF EXISTS knowledge;
402
- DROP TABLE IF EXISTS standards;
403
- DROP TABLE IF EXISTS project;
404
- DROP TABLE IF EXISTS snapshots;
405
- DROP TABLE IF EXISTS review;
406
- DROP TABLE IF EXISTS artifacts;
407
- DROP TABLE IF EXISTS decisions;
408
- DROP TABLE IF EXISTS tasks;
409
- DROP TABLE IF EXISTS context;
410
- DROP TABLE IF EXISTS specs;
411
- `);
412
- initSchema();
413
- }
414
-
415
- export function hasDiscoveredProject(): boolean {
416
- const db = getDb();
417
- const project = db.query("SELECT * FROM project WHERE id = 'default'").get() as any;
418
- const standardsCount = db.query("SELECT COUNT(*) as c FROM standards").get() as any;
419
- return project !== null && standardsCount.c > 0;
420
- }
421
-
422
- export function hasProductContext(): boolean {
423
- const db = getDb();
424
- const product = db.query("SELECT * FROM product_context WHERE id = 'default'").get() as any;
425
- return product !== null;
426
- }
427
-
428
- export function hasImplementationPatterns(): boolean {
429
- const db = getDb();
430
- const count = db.query("SELECT COUNT(*) as c FROM implementation_patterns").get() as any;
431
- return count.c > 0;
432
- }
433
-
434
- export function getPatternsByScope(scope: string): any[] {
435
- const db = getDb();
436
- return db.query(
437
- "SELECT * FROM implementation_patterns WHERE scope = ? OR scope = 'all' ORDER BY category, name"
438
- ).all(scope) as any[];
439
- }
440
-
441
- export function getPatternByName(name: string): any {
442
- const db = getDb();
443
- return db.query("SELECT * FROM implementation_patterns WHERE name = ?").get(name) as any;
444
- }
445
-
446
- export function getPatternsForFiles(files: string[]): any[] {
447
- const db = getDb();
448
- const patterns = db.query("SELECT * FROM implementation_patterns").all() as any[];
449
-
450
- // Filtrar patterns que correspondem aos arquivos da task
451
- return patterns.filter(pattern => {
452
- const glob = pattern.applies_to;
453
- return files.some(file => {
454
- // Simplificado: verificar se o arquivo corresponde ao glob pattern
455
- // Ex: "app/**/page.tsx" deve corresponder a "app/dashboard/page.tsx"
456
- const regexPattern = glob
457
- .replace(/\*\*/g, '.*')
458
- .replace(/\*/g, '[^/]*')
459
- .replace(/\//g, '\\/');
460
- const regex = new RegExp(`^${regexPattern}$`);
461
- return regex.test(file);
462
- });
463
- });
464
- }
465
-
466
- // ═══════════════════════════════════════════════════════════════
467
- // v8.0: Knowledge Graph Helpers
468
- // ═══════════════════════════════════════════════════════════════
469
-
470
- export interface GraphRelation {
471
- sourceType: string;
472
- sourceId: string;
473
- targetType: string;
474
- targetId: string;
475
- relation: string;
476
- strength?: number;
477
- metadata?: Record<string, any>;
478
- }
479
-
480
- export function addGraphRelation(specId: string | null, relation: GraphRelation): void {
481
- const db = getDb();
482
- const now = new Date().toISOString();
483
-
484
- db.run(
485
- `INSERT INTO knowledge_graph (spec_id, source_type, source_id, target_type, target_id, relation, strength, metadata, created_at)
486
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
487
- [
488
- specId,
489
- relation.sourceType,
490
- relation.sourceId,
491
- relation.targetType,
492
- relation.targetId,
493
- relation.relation,
494
- relation.strength || 1.0,
495
- relation.metadata ? JSON.stringify(relation.metadata) : null,
496
- now,
497
- ]
498
- );
499
- }
500
-
501
- export function getRelatedDecisions(fileOrPattern: string, type: "file" | "pattern"): any[] {
502
- const db = getDb();
503
-
504
- return db.query(`
505
- SELECT d.* FROM decisions d
506
- JOIN knowledge_graph kg ON kg.source_id = d.id AND kg.source_type = 'decision'
507
- WHERE kg.target_type = ? AND kg.target_id = ?
508
- AND kg.relation IN ('implements', 'depends_on', 'uses')
509
- `).all(type, fileOrPattern) as any[];
510
- }
511
-
512
- export function getRelatedFiles(decisionOrPattern: string, type: "decision" | "pattern"): string[] {
513
- const db = getDb();
514
-
515
- const results = db.query(`
516
- SELECT kg.target_id as file FROM knowledge_graph kg
517
- WHERE kg.source_type = ? AND kg.source_id = ?
518
- AND kg.target_type = 'file'
519
- AND kg.relation IN ('implements', 'modifies', 'creates')
520
- `).all(type, decisionOrPattern) as any[];
521
-
522
- return results.map(r => r.file);
523
- }
524
-
525
- export function findContradictions(specId: string): Array<{ decision1: string; decision2: string; createdAt: string }> {
526
- const db = getDb();
527
-
528
- return db.query(`
529
- SELECT
530
- d1.title as decision1,
531
- d2.title as decision2,
532
- kg.created_at as createdAt
533
- FROM knowledge_graph kg
534
- JOIN decisions d1 ON kg.source_id = d1.id
535
- JOIN decisions d2 ON kg.target_id = d2.id
536
- WHERE kg.relation = 'contradicts'
537
- AND kg.spec_id = ?
538
- `).all(specId) as any[];
539
- }
540
-
541
- // ═══════════════════════════════════════════════════════════════
542
- // v8.0: Reasoning Log Helpers
543
- // ═══════════════════════════════════════════════════════════════
544
-
545
- export interface ReasoningEntry {
546
- category: "decision" | "discovery" | "error" | "success" | "challenge" | "recommendation";
547
- thought: string;
548
- relatedFiles?: string[];
549
- relatedDecisions?: string[];
550
- relatedPatterns?: string[];
551
- importance?: "critical" | "high" | "normal" | "low";
552
- }
553
-
554
- export function addReasoning(specId: string, taskId: number | null, entry: ReasoningEntry): void {
555
- const db = getDb();
556
- const now = new Date().toISOString();
557
-
558
- db.run(
559
- `INSERT INTO reasoning_log (spec_id, task_id, category, thought, related_files, related_decisions, related_patterns, importance, created_at)
560
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
561
- [
562
- specId,
563
- taskId,
564
- entry.category,
565
- entry.thought,
566
- entry.relatedFiles ? JSON.stringify(entry.relatedFiles) : null,
567
- entry.relatedDecisions ? JSON.stringify(entry.relatedDecisions) : null,
568
- entry.relatedPatterns ? JSON.stringify(entry.relatedPatterns) : null,
569
- entry.importance || "normal",
570
- now,
571
- ]
572
- );
573
- }
574
-
575
- export function getReasoningForTask(specId: string, taskId: number, limit: number = 10): any[] {
576
- const db = getDb();
577
-
578
- return db.query(`
579
- SELECT * FROM reasoning_log
580
- WHERE spec_id = ? AND (task_id = ? OR task_id IS NULL)
581
- ORDER BY
582
- CASE importance
583
- WHEN 'critical' THEN 1
584
- WHEN 'high' THEN 2
585
- WHEN 'normal' THEN 3
586
- WHEN 'low' THEN 4
587
- END,
588
- created_at DESC
589
- LIMIT ?
590
- `).all(specId, taskId, limit) as any[];
591
- }
592
-
593
- export function getRecentReasoning(specId: string, limit: number = 20): any[] {
594
- const db = getDb();
595
-
596
- return db.query(`
597
- SELECT * FROM reasoning_log
598
- WHERE spec_id = ?
599
- ORDER BY created_at DESC
600
- LIMIT ?
601
- `).all(specId, limit) as any[];
602
- }
603
-
604
- // ═══════════════════════════════════════════════════════════════
605
- // v8.0: Session Summary Helpers
606
- // ═══════════════════════════════════════════════════════════════
607
-
608
- export interface SessionSummaryData {
609
- startTime: string;
610
- endTime: string;
611
- summary: string;
612
- decisions?: string[];
613
- blockers?: string[];
614
- nextSteps?: string[];
615
- tasksCompleted?: number;
616
- filesCreated?: number;
617
- filesModified?: number;
618
- }
619
-
620
- export function addSessionSummary(specId: string, data: SessionSummaryData): void {
621
- const db = getDb();
622
- const now = new Date().toISOString();
623
-
624
- db.run(
625
- `INSERT INTO session_summaries (spec_id, start_time, end_time, summary, decisions, blockers, next_steps, tasks_completed, files_created, files_modified, created_at)
626
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
627
- [
628
- specId,
629
- data.startTime,
630
- data.endTime,
631
- data.summary,
632
- data.decisions ? JSON.stringify(data.decisions) : null,
633
- data.blockers ? JSON.stringify(data.blockers) : null,
634
- data.nextSteps ? JSON.stringify(data.nextSteps) : null,
635
- data.tasksCompleted || 0,
636
- data.filesCreated || 0,
637
- data.filesModified || 0,
638
- now,
639
- ]
640
- );
641
- }
642
-
643
- export function getLastSessionSummary(specId: string): any {
644
- const db = getDb();
645
-
646
- return db.query(`
647
- SELECT * FROM session_summaries
648
- WHERE spec_id = ?
649
- ORDER BY created_at DESC
650
- LIMIT 1
651
- `).get(specId);
652
- }
653
-
654
- // v8.3: Retorna ultimas N sessoes para contexto composto
655
- export function getSessionSummaries(specId: string, limit: number = 5): any[] {
656
- const db = getDb();
657
-
658
- return db.query(`
659
- SELECT * FROM session_summaries
660
- WHERE spec_id = ?
661
- ORDER BY created_at DESC
662
- LIMIT ?
663
- `).all(specId, limit) as any[];
664
- }
665
-
666
- // v8.4: Busca analise arquitetural associada a um spec
667
- // Prioridade: analysis_id (link explicito) > nome exato > LIKE parcial
668
- export function getArchitecturalAnalysisForSpec(specName: string, specId?: string): any {
669
- const db = getDb();
670
-
671
- try {
672
- // 0. Link explicito via analysis_id na tabela specs
673
- if (specId) {
674
- const spec = db.query("SELECT analysis_id FROM specs WHERE id = ?").get(specId) as any;
675
- if (spec?.analysis_id) {
676
- const analysis = db.query(
677
- "SELECT * FROM architectural_analyses WHERE id = ? AND status IN ('approved', 'implemented')"
678
- ).get(spec.analysis_id) as any;
679
- if (analysis) return analysis;
680
- }
681
- }
682
-
683
- // 1. Match exato por nome
684
- let analysis = db.query(
685
- "SELECT * FROM architectural_analyses WHERE name = ? AND status IN ('approved', 'implemented') ORDER BY approved_at DESC LIMIT 1"
686
- ).get(specName) as any;
687
-
688
- if (analysis) return analysis;
689
-
690
- // 2. Match parcial (nome da spec contido no nome da analise ou vice-versa)
691
- analysis = db.query(
692
- "SELECT * FROM architectural_analyses WHERE (name LIKE ? OR ? LIKE '%' || name || '%') AND status IN ('approved', 'implemented') ORDER BY approved_at DESC LIMIT 1"
693
- ).get(`%${specName}%`, specName) as any;
694
-
695
- return analysis || null;
696
- } catch {
697
- // Tabela pode nao existir se architect nunca foi usado
698
- return null;
699
- }
700
- }
701
-
702
- // ═══════════════════════════════════════════════════════════════
703
- // v8.5: Project Utilities Helpers (DRY enforcement)
704
- // ═══════════════════════════════════════════════════════════════
705
-
706
- export interface ProjectUtility {
707
- filePath: string;
708
- utilityName: string;
709
- utilityType: string;
710
- scope: string;
711
- signature?: string;
712
- description?: string;
713
- }
714
-
715
- export function upsertUtility(
716
- utility: ProjectUtility,
717
- specId?: string,
718
- taskRef?: number
719
- ): void {
720
- const db = getDb();
721
- const now = new Date().toISOString();
722
- db.run(
723
- `INSERT INTO project_utilities (file_path, utility_name, utility_type, scope, signature, description, spec_id, task_ref, created_at, updated_at)
724
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
725
- ON CONFLICT(file_path, utility_name) DO UPDATE SET
726
- utility_type = excluded.utility_type,
727
- scope = excluded.scope,
728
- signature = COALESCE(excluded.signature, project_utilities.signature),
729
- description = COALESCE(excluded.description, project_utilities.description),
730
- updated_at = excluded.updated_at`,
731
- [
732
- utility.filePath, utility.utilityName, utility.utilityType, utility.scope,
733
- utility.signature || null, utility.description || null,
734
- specId || null, taskRef || null, now, now,
735
- ]
736
- );
737
- }
738
-
739
- export function getUtilitiesForContext(
740
- dirs: string[],
741
- scope?: string,
742
- limit: number = 15
743
- ): any[] {
744
- const db = getDb();
745
-
746
- // Se nenhum filtro, retornar mais recentes
747
- if (dirs.length === 0 && !scope) {
748
- return db.query(
749
- "SELECT * FROM project_utilities ORDER BY updated_at DESC LIMIT ?"
750
- ).all(limit) as any[];
751
- }
752
-
753
- // Buscar por diretorio (prioridade) e scope (fallback)
754
- const conditions: string[] = [];
755
- const params: any[] = [];
756
-
757
- for (const dir of dirs) {
758
- const normalized = dir.replace(/\\/g, "/");
759
- conditions.push("REPLACE(file_path, '\\', '/') LIKE ?");
760
- params.push(`${normalized}%`);
761
- }
762
-
763
- if (scope) {
764
- conditions.push("scope = ?");
765
- params.push(scope);
766
- }
767
-
768
- params.push(limit);
769
- const where = conditions.length > 0 ? `WHERE (${conditions.join(" OR ")})` : "";
770
- return db.query(
771
- `SELECT * FROM project_utilities ${where} ORDER BY updated_at DESC LIMIT ?`
772
- ).all(...params) as any[];
773
- }
774
-
775
- export function findDuplicateUtilities(
776
- utilityName: string,
777
- excludeFile?: string
778
- ): any[] {
779
- const db = getDb();
780
- if (excludeFile) {
781
- return db.query(
782
- "SELECT * FROM project_utilities WHERE utility_name = ? AND file_path != ?"
783
- ).all(utilityName, excludeFile) as any[];
784
- }
785
- return db.query(
786
- "SELECT * FROM project_utilities WHERE utility_name = ?"
787
- ).all(utilityName) as any[];
788
- }
1
+ import { getDb } from "./connection";
2
+
3
+ export function initSchema(): void {
4
+ const db = getDb();
5
+
6
+ db.exec(`
7
+ -- 1. Feature sendo desenvolvida
8
+ CREATE TABLE IF NOT EXISTS specs (
9
+ id TEXT PRIMARY KEY,
10
+ name TEXT NOT NULL,
11
+ phase TEXT NOT NULL DEFAULT 'planning',
12
+ approved_at TEXT,
13
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
14
+ updated_at TEXT
15
+ );
16
+
17
+ -- 2. Contexto compartilhado (o coracao do sistema)
18
+ CREATE TABLE IF NOT EXISTS context (
19
+ spec_id TEXT PRIMARY KEY REFERENCES specs(id),
20
+ objective TEXT NOT NULL,
21
+ approach TEXT,
22
+ constraints TEXT,
23
+ patterns TEXT,
24
+ current_task INTEGER,
25
+ total_tasks INTEGER,
26
+ last_checkpoint TEXT,
27
+ updated_at TEXT
28
+ );
29
+
30
+ -- 3. Tarefas com dependencias (para paralelizacao)
31
+ CREATE TABLE IF NOT EXISTS tasks (
32
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
33
+ spec_id TEXT NOT NULL REFERENCES specs(id),
34
+ number INTEGER NOT NULL,
35
+ name TEXT NOT NULL,
36
+ depends_on TEXT,
37
+ can_parallel INTEGER DEFAULT 1,
38
+ agent TEXT,
39
+ files TEXT,
40
+ status TEXT DEFAULT 'pending',
41
+ checkpoint TEXT,
42
+ completed_at TEXT,
43
+ UNIQUE(spec_id, number)
44
+ );
45
+
46
+ -- 4. Decisoes (registro critico)
47
+ CREATE TABLE IF NOT EXISTS decisions (
48
+ id TEXT PRIMARY KEY,
49
+ spec_id TEXT NOT NULL REFERENCES specs(id),
50
+ task_ref INTEGER,
51
+ title TEXT NOT NULL,
52
+ decision TEXT NOT NULL,
53
+ rationale TEXT,
54
+ status TEXT DEFAULT 'active',
55
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
56
+ );
57
+
58
+ -- 5. Artefatos
59
+ CREATE TABLE IF NOT EXISTS artifacts (
60
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
61
+ spec_id TEXT NOT NULL REFERENCES specs(id),
62
+ task_ref INTEGER NOT NULL,
63
+ path TEXT NOT NULL,
64
+ action TEXT NOT NULL,
65
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
66
+ UNIQUE(spec_id, path)
67
+ );
68
+
69
+ -- 6. Review (fase REV)
70
+ CREATE TABLE IF NOT EXISTS review (
71
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
72
+ spec_id TEXT NOT NULL REFERENCES specs(id),
73
+ planned_vs_done TEXT,
74
+ deviations TEXT,
75
+ pattern_violations TEXT,
76
+ status TEXT DEFAULT 'pending',
77
+ resolution TEXT,
78
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
79
+ );
80
+
81
+ -- 7. Snapshots (recovery)
82
+ CREATE TABLE IF NOT EXISTS snapshots (
83
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
84
+ spec_id TEXT NOT NULL REFERENCES specs(id),
85
+ data TEXT NOT NULL,
86
+ trigger TEXT NOT NULL,
87
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
88
+ );
89
+
90
+ -- 8. Projeto (informacoes detectadas no discover)
91
+ CREATE TABLE IF NOT EXISTS project (
92
+ id TEXT PRIMARY KEY DEFAULT 'default',
93
+ name TEXT,
94
+ stack TEXT NOT NULL,
95
+ discovered_at TEXT,
96
+ updated_at TEXT
97
+ );
98
+
99
+ -- 9. Standards do projeto
100
+ CREATE TABLE IF NOT EXISTS standards (
101
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
102
+ category TEXT NOT NULL,
103
+ scope TEXT NOT NULL,
104
+ rule TEXT NOT NULL,
105
+ examples TEXT,
106
+ anti_examples TEXT,
107
+ enforcement TEXT DEFAULT 'required',
108
+ source TEXT,
109
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
110
+ );
111
+
112
+ -- 10. Knowledge broadcast (compartilhamento em tempo real)
113
+ CREATE TABLE IF NOT EXISTS knowledge (
114
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
115
+ spec_id TEXT NOT NULL REFERENCES specs(id),
116
+ task_origin INTEGER NOT NULL,
117
+ category TEXT NOT NULL,
118
+ content TEXT NOT NULL,
119
+ severity TEXT DEFAULT 'info',
120
+ broadcast_to TEXT DEFAULT 'all',
121
+ acknowledged_by TEXT,
122
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
123
+ );
124
+
125
+ -- 11. Gate bypass log (auditoria de bypasses)
126
+ CREATE TABLE IF NOT EXISTS gate_bypasses (
127
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
128
+ spec_id TEXT NOT NULL REFERENCES specs(id),
129
+ task_id INTEGER NOT NULL,
130
+ gate_name TEXT NOT NULL,
131
+ reason TEXT,
132
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
133
+ );
134
+
135
+ -- 12. Product context (PRD/contexto de produto)
136
+ CREATE TABLE IF NOT EXISTS product_context (
137
+ id TEXT PRIMARY KEY DEFAULT 'default',
138
+ name TEXT NOT NULL,
139
+ problem TEXT NOT NULL,
140
+ solution TEXT,
141
+ target_users TEXT,
142
+ value_proposition TEXT,
143
+ success_metrics TEXT,
144
+ out_of_scope TEXT,
145
+ constraints TEXT,
146
+ source TEXT DEFAULT 'guide',
147
+ discovered_at TEXT,
148
+ updated_at TEXT
149
+ );
150
+
151
+ -- 13. Product goals (objetivos do produto)
152
+ CREATE TABLE IF NOT EXISTS product_goals (
153
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
154
+ product_id TEXT NOT NULL DEFAULT 'default' REFERENCES product_context(id),
155
+ category TEXT NOT NULL,
156
+ goal TEXT NOT NULL,
157
+ priority TEXT DEFAULT 'medium',
158
+ status TEXT DEFAULT 'active',
159
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
160
+ );
161
+
162
+ -- 14. Product features (features core do produto)
163
+ CREATE TABLE IF NOT EXISTS product_features (
164
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
165
+ product_id TEXT NOT NULL DEFAULT 'default' REFERENCES product_context(id),
166
+ name TEXT NOT NULL,
167
+ description TEXT,
168
+ priority TEXT DEFAULT 'medium',
169
+ status TEXT DEFAULT 'planned',
170
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
171
+ );
172
+
173
+ -- 15. Library contexts (contexto atualizado por biblioteca)
174
+ CREATE TABLE IF NOT EXISTS lib_contexts (
175
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
176
+ lib_name TEXT NOT NULL UNIQUE,
177
+ version TEXT NOT NULL,
178
+ context_file TEXT NOT NULL,
179
+ source_url TEXT,
180
+ researched_at TEXT DEFAULT CURRENT_TIMESTAMP,
181
+ updated_at TEXT
182
+ );
183
+
184
+ -- 16. Agent lib mappings (quais libs cada agente carrega)
185
+ CREATE TABLE IF NOT EXISTS agent_lib_mappings (
186
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
187
+ agent_type TEXT NOT NULL,
188
+ lib_name TEXT NOT NULL,
189
+ priority INTEGER DEFAULT 0,
190
+ UNIQUE(agent_type, lib_name)
191
+ );
192
+
193
+ -- 17. Implementation patterns (padroes extraidos do codigo - v7.4)
194
+ CREATE TABLE IF NOT EXISTS implementation_patterns (
195
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
196
+
197
+ -- Identificacao
198
+ category TEXT NOT NULL, -- component, hook, service, route, schema, test, action
199
+ name TEXT NOT NULL UNIQUE, -- ex: "page-component", "data-hook", "api-route"
200
+
201
+ -- Contexto
202
+ scope TEXT NOT NULL, -- frontend, backend, database, testing
203
+ applies_to TEXT NOT NULL, -- glob pattern: "app/**/page.tsx", "src/hooks/*.ts"
204
+
205
+ -- O Padrao Extraido
206
+ structure TEXT NOT NULL, -- JSON com estrutura comum (imports, exports, patterns, conventions)
207
+ template TEXT NOT NULL, -- Template de codigo com placeholders
208
+ examples TEXT NOT NULL, -- JSON array de arquivos que seguem este padrao
209
+
210
+ -- Anti-patterns identificados
211
+ anti_patterns TEXT, -- JSON array de anti-patterns encontrados
212
+
213
+ -- Metadados
214
+ confidence REAL DEFAULT 0.8, -- 0-1, baseado em quantos arquivos seguem
215
+ extracted_from INTEGER, -- Quantos arquivos foram analisados
216
+
217
+ -- Auditoria
218
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
219
+ updated_at TEXT
220
+ );
221
+
222
+ -- 18. Knowledge Graph (v8.0 - Relacoes entre entidades)
223
+ CREATE TABLE IF NOT EXISTS knowledge_graph (
224
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
225
+ spec_id TEXT REFERENCES specs(id),
226
+
227
+ -- No de origem
228
+ source_type TEXT NOT NULL, -- file, decision, pattern, task, artifact
229
+ source_id TEXT NOT NULL,
230
+
231
+ -- No de destino
232
+ target_type TEXT NOT NULL,
233
+ target_id TEXT NOT NULL,
234
+
235
+ -- Relacao
236
+ relation TEXT NOT NULL, -- uses, implements, depends_on, modifies, contradicts, extracted_from
237
+
238
+ -- Metadados
239
+ strength REAL DEFAULT 1.0, -- 0-1, peso da relacao
240
+ metadata TEXT, -- JSON com informacoes adicionais
241
+
242
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
243
+ );
244
+
245
+ -- 19. Reasoning Log (v8.0 - Historico de raciocinio)
246
+ CREATE TABLE IF NOT EXISTS reasoning_log (
247
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
248
+ spec_id TEXT REFERENCES specs(id),
249
+ task_id INTEGER REFERENCES tasks(id),
250
+
251
+ -- O raciocinio
252
+ category TEXT NOT NULL, -- decision, discovery, error, success, challenge, recommendation
253
+ thought TEXT NOT NULL, -- "Escolhi X porque Y"
254
+
255
+ -- Contexto
256
+ related_files TEXT, -- JSON array de arquivos
257
+ related_decisions TEXT, -- JSON array de decision IDs
258
+ related_patterns TEXT, -- JSON array de pattern names
259
+
260
+ -- Importancia
261
+ importance TEXT DEFAULT 'normal', -- critical, high, normal, low
262
+
263
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
264
+ );
265
+
266
+ -- 20. Session Summaries (v8.0 - Resumos de sessao para continuidade)
267
+ CREATE TABLE IF NOT EXISTS session_summaries (
268
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
269
+ spec_id TEXT NOT NULL REFERENCES specs(id),
270
+
271
+ -- Periodo
272
+ start_time TEXT NOT NULL,
273
+ end_time TEXT NOT NULL,
274
+
275
+ -- Conteudo
276
+ summary TEXT NOT NULL, -- O que foi feito
277
+ decisions TEXT, -- JSON array de decisoes tomadas
278
+ blockers TEXT, -- JSON array de problemas encontrados
279
+ next_steps TEXT, -- JSON array de recomendacoes
280
+
281
+ -- Estatisticas
282
+ tasks_completed INTEGER DEFAULT 0,
283
+ files_created INTEGER DEFAULT 0,
284
+ files_modified INTEGER DEFAULT 0,
285
+
286
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP
287
+ );
288
+
289
+ -- 21. Architectural Analyses (v8.1 - migrado de architect.ts para schema central)
290
+ CREATE TABLE IF NOT EXISTS architectural_analyses (
291
+ id TEXT PRIMARY KEY,
292
+ name TEXT NOT NULL,
293
+ description TEXT NOT NULL,
294
+ status TEXT NOT NULL DEFAULT 'pending',
295
+
296
+ -- Conteudo JSON
297
+ context TEXT,
298
+ current_architecture TEXT,
299
+ approach TEXT,
300
+ chosen_alternative TEXT,
301
+ diagrams TEXT, -- JSON array
302
+ baby_steps TEXT, -- JSON array
303
+ risks TEXT, -- JSON array
304
+ alternatives TEXT, -- JSON array
305
+ decisions TEXT, -- JSON array
306
+
307
+ -- Arquivo gerado
308
+ file_path TEXT,
309
+
310
+ -- Timestamps
311
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
312
+ updated_at TEXT,
313
+ approved_at TEXT
314
+ );
315
+
316
+ -- 22. Project Utilities (v8.5 - DRY enforcement utility map)
317
+ CREATE TABLE IF NOT EXISTS project_utilities (
318
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
319
+ file_path TEXT NOT NULL,
320
+ utility_name TEXT NOT NULL,
321
+ utility_type TEXT NOT NULL,
322
+ scope TEXT NOT NULL,
323
+ signature TEXT,
324
+ description TEXT,
325
+ spec_id TEXT,
326
+ task_ref INTEGER,
327
+ created_at TEXT DEFAULT CURRENT_TIMESTAMP,
328
+ updated_at TEXT,
329
+ UNIQUE(file_path, utility_name)
330
+ );
331
+
332
+ -- Indices para performance
333
+ CREATE INDEX IF NOT EXISTS idx_tasks_spec ON tasks(spec_id);
334
+ CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
335
+ CREATE INDEX IF NOT EXISTS idx_decisions_spec ON decisions(spec_id);
336
+ CREATE INDEX IF NOT EXISTS idx_artifacts_spec ON artifacts(spec_id);
337
+ CREATE INDEX IF NOT EXISTS idx_snapshots_spec ON snapshots(spec_id);
338
+ CREATE INDEX IF NOT EXISTS idx_standards_category ON standards(category);
339
+ CREATE INDEX IF NOT EXISTS idx_standards_scope ON standards(scope);
340
+ CREATE INDEX IF NOT EXISTS idx_knowledge_spec ON knowledge(spec_id);
341
+ CREATE INDEX IF NOT EXISTS idx_knowledge_category ON knowledge(category);
342
+ CREATE INDEX IF NOT EXISTS idx_knowledge_severity ON knowledge(severity);
343
+ CREATE INDEX IF NOT EXISTS idx_gate_bypasses_spec ON gate_bypasses(spec_id);
344
+ CREATE INDEX IF NOT EXISTS idx_product_goals_product ON product_goals(product_id);
345
+ CREATE INDEX IF NOT EXISTS idx_product_goals_category ON product_goals(category);
346
+ CREATE INDEX IF NOT EXISTS idx_product_features_product ON product_features(product_id);
347
+ CREATE INDEX IF NOT EXISTS idx_product_features_priority ON product_features(priority);
348
+ CREATE INDEX IF NOT EXISTS idx_lib_contexts_lib ON lib_contexts(lib_name);
349
+ CREATE INDEX IF NOT EXISTS idx_agent_lib_mappings_agent ON agent_lib_mappings(agent_type);
350
+ CREATE INDEX IF NOT EXISTS idx_impl_patterns_category ON implementation_patterns(category);
351
+ CREATE INDEX IF NOT EXISTS idx_impl_patterns_scope ON implementation_patterns(scope);
352
+ CREATE INDEX IF NOT EXISTS idx_impl_patterns_name ON implementation_patterns(name);
353
+
354
+ -- v8.0: Indices para Knowledge Graph
355
+ CREATE INDEX IF NOT EXISTS idx_kg_source ON knowledge_graph(source_type, source_id);
356
+ CREATE INDEX IF NOT EXISTS idx_kg_target ON knowledge_graph(target_type, target_id);
357
+ CREATE INDEX IF NOT EXISTS idx_kg_relation ON knowledge_graph(relation);
358
+ CREATE INDEX IF NOT EXISTS idx_kg_spec ON knowledge_graph(spec_id);
359
+
360
+ -- v8.0: Indices para Reasoning Log
361
+ CREATE INDEX IF NOT EXISTS idx_reasoning_spec ON reasoning_log(spec_id);
362
+ CREATE INDEX IF NOT EXISTS idx_reasoning_task ON reasoning_log(task_id);
363
+ CREATE INDEX IF NOT EXISTS idx_reasoning_category ON reasoning_log(category);
364
+ CREATE INDEX IF NOT EXISTS idx_reasoning_importance ON reasoning_log(importance);
365
+
366
+ -- v8.0: Indices para Session Summaries
367
+ CREATE INDEX IF NOT EXISTS idx_session_spec ON session_summaries(spec_id);
368
+ CREATE INDEX IF NOT EXISTS idx_session_time ON session_summaries(end_time);
369
+
370
+ -- v8.1: Indices para Architectural Analyses
371
+ CREATE INDEX IF NOT EXISTS idx_arch_status ON architectural_analyses(status);
372
+ CREATE INDEX IF NOT EXISTS idx_arch_created ON architectural_analyses(created_at);
373
+
374
+ -- v8.5: Indices para Project Utilities
375
+ CREATE INDEX IF NOT EXISTS idx_utils_scope ON project_utilities(scope);
376
+ CREATE INDEX IF NOT EXISTS idx_utils_name ON project_utilities(utility_name);
377
+ CREATE INDEX IF NOT EXISTS idx_utils_file ON project_utilities(file_path);
378
+ `);
379
+
380
+ // v8.4: Migracao - adicionar analysis_id na tabela specs
381
+ try {
382
+ db.exec(`ALTER TABLE specs ADD COLUMN analysis_id TEXT`);
383
+ } catch {
384
+ // Coluna ja existe - ignorar
385
+ }
386
+ }
387
+
388
+ export function getPatternsByScope(scope: string): any[] {
389
+ const db = getDb();
390
+ return db.query(
391
+ "SELECT * FROM implementation_patterns WHERE scope = ? OR scope = 'all' ORDER BY category, name"
392
+ ).all(scope) as any[];
393
+ }
394
+
395
+ export function getPatternsForFiles(files: string[]): any[] {
396
+ const db = getDb();
397
+ const patterns = db.query("SELECT * FROM implementation_patterns").all() as any[];
398
+
399
+ // Filtrar patterns que correspondem aos arquivos da task
400
+ return patterns.filter(pattern => {
401
+ const glob = pattern.applies_to;
402
+ return files.some(file => {
403
+ // Simplificado: verificar se o arquivo corresponde ao glob pattern
404
+ // Ex: "app/**/page.tsx" deve corresponder a "app/dashboard/page.tsx"
405
+ const regexPattern = glob
406
+ .replace(/\*\*/g, '.*')
407
+ .replace(/\*/g, '[^/]*')
408
+ .replace(/\//g, '\\/');
409
+ const regex = new RegExp(`^${regexPattern}$`);
410
+ return regex.test(file);
411
+ });
412
+ });
413
+ }
414
+
415
+ // ═══════════════════════════════════════════════════════════════
416
+ // v8.0: Knowledge Graph Helpers
417
+ // ═══════════════════════════════════════════════════════════════
418
+
419
+ export interface GraphRelation {
420
+ sourceType: string;
421
+ sourceId: string;
422
+ targetType: string;
423
+ targetId: string;
424
+ relation: string;
425
+ strength?: number;
426
+ metadata?: Record<string, any>;
427
+ }
428
+
429
+ export function addGraphRelation(specId: string | null, relation: GraphRelation): void {
430
+ const db = getDb();
431
+ const now = new Date().toISOString();
432
+
433
+ db.run(
434
+ `INSERT INTO knowledge_graph (spec_id, source_type, source_id, target_type, target_id, relation, strength, metadata, created_at)
435
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
436
+ [
437
+ specId,
438
+ relation.sourceType,
439
+ relation.sourceId,
440
+ relation.targetType,
441
+ relation.targetId,
442
+ relation.relation,
443
+ relation.strength || 1.0,
444
+ relation.metadata ? JSON.stringify(relation.metadata) : null,
445
+ now,
446
+ ]
447
+ );
448
+ }
449
+
450
+ export function getRelatedDecisions(fileOrPattern: string, type: "file" | "pattern"): any[] {
451
+ const db = getDb();
452
+
453
+ return db.query(`
454
+ SELECT d.* FROM decisions d
455
+ JOIN knowledge_graph kg ON kg.source_id = d.id AND kg.source_type = 'decision'
456
+ WHERE kg.target_type = ? AND kg.target_id = ?
457
+ AND kg.relation IN ('implements', 'depends_on', 'uses')
458
+ `).all(type, fileOrPattern) as any[];
459
+ }
460
+
461
+ export function getRelatedFiles(decisionOrPattern: string, type: "decision" | "pattern"): string[] {
462
+ const db = getDb();
463
+
464
+ const results = db.query(`
465
+ SELECT kg.target_id as file FROM knowledge_graph kg
466
+ WHERE kg.source_type = ? AND kg.source_id = ?
467
+ AND kg.target_type = 'file'
468
+ AND kg.relation IN ('implements', 'modifies', 'creates')
469
+ `).all(type, decisionOrPattern) as any[];
470
+
471
+ return results.map(r => r.file);
472
+ }
473
+
474
+ export function findContradictions(specId: string): Array<{ decision1: string; decision2: string; createdAt: string }> {
475
+ const db = getDb();
476
+
477
+ return db.query(`
478
+ SELECT
479
+ d1.title as decision1,
480
+ d2.title as decision2,
481
+ kg.created_at as createdAt
482
+ FROM knowledge_graph kg
483
+ JOIN decisions d1 ON kg.source_id = d1.id
484
+ JOIN decisions d2 ON kg.target_id = d2.id
485
+ WHERE kg.relation = 'contradicts'
486
+ AND kg.spec_id = ?
487
+ `).all(specId) as any[];
488
+ }
489
+
490
+ // ═══════════════════════════════════════════════════════════════
491
+ // v8.0: Reasoning Log Helpers
492
+ // ═══════════════════════════════════════════════════════════════
493
+
494
+ export interface ReasoningEntry {
495
+ category: "decision" | "discovery" | "error" | "success" | "challenge" | "recommendation";
496
+ thought: string;
497
+ relatedFiles?: string[];
498
+ relatedDecisions?: string[];
499
+ relatedPatterns?: string[];
500
+ importance?: "critical" | "high" | "normal" | "low";
501
+ }
502
+
503
+ export function addReasoning(specId: string, taskId: number | null, entry: ReasoningEntry): void {
504
+ const db = getDb();
505
+ const now = new Date().toISOString();
506
+
507
+ db.run(
508
+ `INSERT INTO reasoning_log (spec_id, task_id, category, thought, related_files, related_decisions, related_patterns, importance, created_at)
509
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
510
+ [
511
+ specId,
512
+ taskId,
513
+ entry.category,
514
+ entry.thought,
515
+ entry.relatedFiles ? JSON.stringify(entry.relatedFiles) : null,
516
+ entry.relatedDecisions ? JSON.stringify(entry.relatedDecisions) : null,
517
+ entry.relatedPatterns ? JSON.stringify(entry.relatedPatterns) : null,
518
+ entry.importance || "normal",
519
+ now,
520
+ ]
521
+ );
522
+ }
523
+
524
+ export function getRecentReasoning(specId: string, limit: number = 20): any[] {
525
+ const db = getDb();
526
+
527
+ return db.query(`
528
+ SELECT * FROM reasoning_log
529
+ WHERE spec_id = ?
530
+ ORDER BY created_at DESC
531
+ LIMIT ?
532
+ `).all(specId, limit) as any[];
533
+ }
534
+
535
+ // ═══════════════════════════════════════════════════════════════
536
+ // v8.0: Session Summary Helpers
537
+ // ═══════════════════════════════════════════════════════════════
538
+
539
+ export interface SessionSummaryData {
540
+ startTime: string;
541
+ endTime: string;
542
+ summary: string;
543
+ decisions?: string[];
544
+ blockers?: string[];
545
+ nextSteps?: string[];
546
+ tasksCompleted?: number;
547
+ filesCreated?: number;
548
+ filesModified?: number;
549
+ }
550
+
551
+ export function addSessionSummary(specId: string, data: SessionSummaryData): void {
552
+ const db = getDb();
553
+ const now = new Date().toISOString();
554
+
555
+ db.run(
556
+ `INSERT INTO session_summaries (spec_id, start_time, end_time, summary, decisions, blockers, next_steps, tasks_completed, files_created, files_modified, created_at)
557
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
558
+ [
559
+ specId,
560
+ data.startTime,
561
+ data.endTime,
562
+ data.summary,
563
+ data.decisions ? JSON.stringify(data.decisions) : null,
564
+ data.blockers ? JSON.stringify(data.blockers) : null,
565
+ data.nextSteps ? JSON.stringify(data.nextSteps) : null,
566
+ data.tasksCompleted || 0,
567
+ data.filesCreated || 0,
568
+ data.filesModified || 0,
569
+ now,
570
+ ]
571
+ );
572
+ }
573
+
574
+ export function getLastSessionSummary(specId: string): any {
575
+ const db = getDb();
576
+
577
+ return db.query(`
578
+ SELECT * FROM session_summaries
579
+ WHERE spec_id = ?
580
+ ORDER BY created_at DESC
581
+ LIMIT 1
582
+ `).get(specId);
583
+ }
584
+
585
+ // v8.3: Retorna ultimas N sessoes para contexto composto
586
+ export function getSessionSummaries(specId: string, limit: number = 5): any[] {
587
+ const db = getDb();
588
+
589
+ return db.query(`
590
+ SELECT * FROM session_summaries
591
+ WHERE spec_id = ?
592
+ ORDER BY created_at DESC
593
+ LIMIT ?
594
+ `).all(specId, limit) as any[];
595
+ }
596
+
597
+ // v8.4: Busca analise arquitetural associada a um spec
598
+ // Prioridade: analysis_id (link explicito) > nome exato > LIKE parcial
599
+ export function getArchitecturalAnalysisForSpec(specName: string, specId?: string): any {
600
+ const db = getDb();
601
+
602
+ try {
603
+ // 0. Link explicito via analysis_id na tabela specs
604
+ if (specId) {
605
+ const spec = db.query("SELECT analysis_id FROM specs WHERE id = ?").get(specId) as any;
606
+ if (spec?.analysis_id) {
607
+ const analysis = db.query(
608
+ "SELECT * FROM architectural_analyses WHERE id = ? AND status IN ('approved', 'implemented')"
609
+ ).get(spec.analysis_id) as any;
610
+ if (analysis) return analysis;
611
+ }
612
+ }
613
+
614
+ // 1. Match exato por nome
615
+ let analysis = db.query(
616
+ "SELECT * FROM architectural_analyses WHERE name = ? AND status IN ('approved', 'implemented') ORDER BY approved_at DESC LIMIT 1"
617
+ ).get(specName) as any;
618
+
619
+ if (analysis) return analysis;
620
+
621
+ // 2. Match parcial (nome da spec contido no nome da analise ou vice-versa)
622
+ analysis = db.query(
623
+ "SELECT * FROM architectural_analyses WHERE (name LIKE ? OR ? LIKE '%' || name || '%') AND status IN ('approved', 'implemented') ORDER BY approved_at DESC LIMIT 1"
624
+ ).get(`%${specName}%`, specName) as any;
625
+
626
+ return analysis || null;
627
+ } catch {
628
+ // Tabela pode nao existir se architect nunca foi usado
629
+ return null;
630
+ }
631
+ }
632
+
633
+ // ═══════════════════════════════════════════════════════════════
634
+ // v8.5: Project Utilities Helpers (DRY enforcement)
635
+ // ═══════════════════════════════════════════════════════════════
636
+
637
+ export interface ProjectUtility {
638
+ filePath: string;
639
+ utilityName: string;
640
+ utilityType: string;
641
+ scope: string;
642
+ signature?: string;
643
+ description?: string;
644
+ }
645
+
646
+ export function upsertUtility(
647
+ utility: ProjectUtility,
648
+ specId?: string,
649
+ taskRef?: number
650
+ ): void {
651
+ const db = getDb();
652
+ const now = new Date().toISOString();
653
+ db.run(
654
+ `INSERT INTO project_utilities (file_path, utility_name, utility_type, scope, signature, description, spec_id, task_ref, created_at, updated_at)
655
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
656
+ ON CONFLICT(file_path, utility_name) DO UPDATE SET
657
+ utility_type = excluded.utility_type,
658
+ scope = excluded.scope,
659
+ signature = COALESCE(excluded.signature, project_utilities.signature),
660
+ description = COALESCE(excluded.description, project_utilities.description),
661
+ updated_at = excluded.updated_at`,
662
+ [
663
+ utility.filePath, utility.utilityName, utility.utilityType, utility.scope,
664
+ utility.signature || null, utility.description || null,
665
+ specId || null, taskRef || null, now, now,
666
+ ]
667
+ );
668
+ }
669
+
670
+ export function getUtilitiesForContext(
671
+ dirs: string[],
672
+ scope?: string,
673
+ limit: number = 15
674
+ ): any[] {
675
+ const db = getDb();
676
+
677
+ // Se nenhum filtro, retornar mais recentes
678
+ if (dirs.length === 0 && !scope) {
679
+ return db.query(
680
+ "SELECT * FROM project_utilities ORDER BY updated_at DESC LIMIT ?"
681
+ ).all(limit) as any[];
682
+ }
683
+
684
+ // Buscar por diretorio (prioridade) e scope (fallback)
685
+ const conditions: string[] = [];
686
+ const params: any[] = [];
687
+
688
+ for (const dir of dirs) {
689
+ const normalized = dir.replace(/\\/g, "/");
690
+ conditions.push("REPLACE(file_path, '\\', '/') LIKE ?");
691
+ params.push(`${normalized}%`);
692
+ }
693
+
694
+ if (scope) {
695
+ conditions.push("scope = ?");
696
+ params.push(scope);
697
+ }
698
+
699
+ params.push(limit);
700
+ const where = conditions.length > 0 ? `WHERE (${conditions.join(" OR ")})` : "";
701
+ return db.query(
702
+ `SELECT * FROM project_utilities ${where} ORDER BY updated_at DESC LIMIT ?`
703
+ ).all(...params) as any[];
704
+ }
705
+
706
+ export function findDuplicateUtilities(
707
+ utilityName: string,
708
+ excludeFile?: string
709
+ ): any[] {
710
+ const db = getDb();
711
+ if (excludeFile) {
712
+ return db.query(
713
+ "SELECT * FROM project_utilities WHERE utility_name = ? AND file_path != ?"
714
+ ).all(utilityName, excludeFile) as any[];
715
+ }
716
+ return db.query(
717
+ "SELECT * FROM project_utilities WHERE utility_name = ?"
718
+ ).all(utilityName) as any[];
719
+ }