@codexa/cli 9.0.31 → 9.0.33

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,11 +1,9 @@
1
- import { Database } from "bun:sqlite";
2
- import { getDb } from "./connection";
1
+ import type { Client, Transaction } from "@libsql/client";
2
+ import { getDb, setClient, dbGet, dbAll, dbRun, dbExec, ensureDatabase } from "./connection";
3
3
 
4
- export function initSchema(): void {
5
- const db = getDb();
6
-
7
- db.exec(`
8
- -- 1. Feature sendo desenvolvida
4
+ export async function initSchema(): Promise<void> {
5
+ await ensureDatabase();
6
+ await dbExec(`
9
7
  CREATE TABLE IF NOT EXISTS specs (
10
8
  id TEXT PRIMARY KEY,
11
9
  name TEXT NOT NULL,
@@ -13,9 +11,9 @@ export function initSchema(): void {
13
11
  approved_at TEXT,
14
12
  created_at TEXT DEFAULT CURRENT_TIMESTAMP,
15
13
  updated_at TEXT
16
- );
17
-
18
- -- 2. Contexto compartilhado (o coracao do sistema)
14
+ )
15
+ `);
16
+ await dbExec(`
19
17
  CREATE TABLE IF NOT EXISTS context (
20
18
  spec_id TEXT PRIMARY KEY REFERENCES specs(id),
21
19
  objective TEXT NOT NULL,
@@ -26,9 +24,9 @@ export function initSchema(): void {
26
24
  total_tasks INTEGER,
27
25
  last_checkpoint TEXT,
28
26
  updated_at TEXT
29
- );
30
-
31
- -- 3. Tarefas com dependencias (para paralelizacao)
27
+ )
28
+ `);
29
+ await dbExec(`
32
30
  CREATE TABLE IF NOT EXISTS tasks (
33
31
  id INTEGER PRIMARY KEY AUTOINCREMENT,
34
32
  spec_id TEXT NOT NULL REFERENCES specs(id),
@@ -42,9 +40,9 @@ export function initSchema(): void {
42
40
  checkpoint TEXT,
43
41
  completed_at TEXT,
44
42
  UNIQUE(spec_id, number)
45
- );
46
-
47
- -- 4. Decisoes (registro critico)
43
+ )
44
+ `);
45
+ await dbExec(`
48
46
  CREATE TABLE IF NOT EXISTS decisions (
49
47
  id TEXT PRIMARY KEY,
50
48
  spec_id TEXT NOT NULL REFERENCES specs(id),
@@ -54,9 +52,9 @@ export function initSchema(): void {
54
52
  rationale TEXT,
55
53
  status TEXT DEFAULT 'active',
56
54
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
57
- );
58
-
59
- -- 5. Artefatos
55
+ )
56
+ `);
57
+ await dbExec(`
60
58
  CREATE TABLE IF NOT EXISTS artifacts (
61
59
  id INTEGER PRIMARY KEY AUTOINCREMENT,
62
60
  spec_id TEXT NOT NULL REFERENCES specs(id),
@@ -65,9 +63,9 @@ export function initSchema(): void {
65
63
  action TEXT NOT NULL,
66
64
  created_at TEXT DEFAULT CURRENT_TIMESTAMP,
67
65
  UNIQUE(spec_id, path)
68
- );
69
-
70
- -- 6. Review (fase REV)
66
+ )
67
+ `);
68
+ await dbExec(`
71
69
  CREATE TABLE IF NOT EXISTS review (
72
70
  id INTEGER PRIMARY KEY AUTOINCREMENT,
73
71
  spec_id TEXT NOT NULL REFERENCES specs(id),
@@ -75,27 +73,27 @@ export function initSchema(): void {
75
73
  deviations TEXT,
76
74
  status TEXT DEFAULT 'pending',
77
75
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
78
- );
79
-
80
- -- 7. Snapshots (recovery)
76
+ )
77
+ `);
78
+ await dbExec(`
81
79
  CREATE TABLE IF NOT EXISTS snapshots (
82
80
  id INTEGER PRIMARY KEY AUTOINCREMENT,
83
81
  spec_id TEXT NOT NULL REFERENCES specs(id),
84
82
  data TEXT NOT NULL,
85
83
  trigger TEXT NOT NULL,
86
84
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
87
- );
88
-
89
- -- 8. Projeto (informacoes detectadas no discover)
85
+ )
86
+ `);
87
+ await dbExec(`
90
88
  CREATE TABLE IF NOT EXISTS project (
91
89
  id TEXT PRIMARY KEY DEFAULT 'default',
92
90
  name TEXT,
93
91
  stack TEXT NOT NULL,
94
92
  discovered_at TEXT,
95
93
  updated_at TEXT
96
- );
97
-
98
- -- 9. Standards do projeto
94
+ )
95
+ `);
96
+ await dbExec(`
99
97
  CREATE TABLE IF NOT EXISTS standards (
100
98
  id INTEGER PRIMARY KEY AUTOINCREMENT,
101
99
  category TEXT NOT NULL,
@@ -106,9 +104,9 @@ export function initSchema(): void {
106
104
  enforcement TEXT DEFAULT 'required',
107
105
  source TEXT,
108
106
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
109
- );
110
-
111
- -- 10. Knowledge broadcast (compartilhamento em tempo real)
107
+ )
108
+ `);
109
+ await dbExec(`
112
110
  CREATE TABLE IF NOT EXISTS knowledge (
113
111
  id INTEGER PRIMARY KEY AUTOINCREMENT,
114
112
  spec_id TEXT NOT NULL REFERENCES specs(id),
@@ -118,9 +116,9 @@ export function initSchema(): void {
118
116
  severity TEXT DEFAULT 'info',
119
117
  broadcast_to TEXT DEFAULT 'all',
120
118
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
121
- );
122
-
123
- -- 11. Gate bypass log (auditoria de bypasses)
119
+ )
120
+ `);
121
+ await dbExec(`
124
122
  CREATE TABLE IF NOT EXISTS gate_bypasses (
125
123
  id INTEGER PRIMARY KEY AUTOINCREMENT,
126
124
  spec_id TEXT NOT NULL REFERENCES specs(id),
@@ -128,9 +126,9 @@ export function initSchema(): void {
128
126
  gate_name TEXT NOT NULL,
129
127
  reason TEXT,
130
128
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
131
- );
132
-
133
- -- 12. Product context (PRD/contexto de produto)
129
+ )
130
+ `);
131
+ await dbExec(`
134
132
  CREATE TABLE IF NOT EXISTS product_context (
135
133
  id TEXT PRIMARY KEY DEFAULT 'default',
136
134
  name TEXT NOT NULL,
@@ -144,9 +142,9 @@ export function initSchema(): void {
144
142
  source TEXT DEFAULT 'guide',
145
143
  discovered_at TEXT,
146
144
  updated_at TEXT
147
- );
148
-
149
- -- 13. Product goals (objetivos do produto)
145
+ )
146
+ `);
147
+ await dbExec(`
150
148
  CREATE TABLE IF NOT EXISTS product_goals (
151
149
  id INTEGER PRIMARY KEY AUTOINCREMENT,
152
150
  product_id TEXT NOT NULL DEFAULT 'default' REFERENCES product_context(id),
@@ -155,9 +153,9 @@ export function initSchema(): void {
155
153
  priority TEXT DEFAULT 'medium',
156
154
  status TEXT DEFAULT 'active',
157
155
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
158
- );
159
-
160
- -- 14. Product features (features core do produto)
156
+ )
157
+ `);
158
+ await dbExec(`
161
159
  CREATE TABLE IF NOT EXISTS product_features (
162
160
  id INTEGER PRIMARY KEY AUTOINCREMENT,
163
161
  product_id TEXT NOT NULL DEFAULT 'default' REFERENCES product_context(id),
@@ -166,9 +164,9 @@ export function initSchema(): void {
166
164
  priority TEXT DEFAULT 'medium',
167
165
  status TEXT DEFAULT 'planned',
168
166
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
169
- );
170
-
171
- -- 15. Library contexts (contexto atualizado por biblioteca)
167
+ )
168
+ `);
169
+ await dbExec(`
172
170
  CREATE TABLE IF NOT EXISTS lib_contexts (
173
171
  id INTEGER PRIMARY KEY AUTOINCREMENT,
174
172
  lib_name TEXT NOT NULL UNIQUE,
@@ -177,117 +175,83 @@ export function initSchema(): void {
177
175
  source_url TEXT,
178
176
  researched_at TEXT DEFAULT CURRENT_TIMESTAMP,
179
177
  updated_at TEXT
180
- );
181
-
182
- -- 16. Agent lib mappings (quais libs cada agente carrega)
178
+ )
179
+ `);
180
+ await dbExec(`
183
181
  CREATE TABLE IF NOT EXISTS agent_lib_mappings (
184
182
  id INTEGER PRIMARY KEY AUTOINCREMENT,
185
183
  agent_type TEXT NOT NULL,
186
184
  lib_name TEXT NOT NULL,
187
185
  priority INTEGER DEFAULT 0,
188
186
  UNIQUE(agent_type, lib_name)
189
- );
190
-
191
- -- 17. Implementation patterns (padroes extraidos do codigo - v7.4)
187
+ )
188
+ `);
189
+ await dbExec(`
192
190
  CREATE TABLE IF NOT EXISTS implementation_patterns (
193
191
  id INTEGER PRIMARY KEY AUTOINCREMENT,
194
-
195
- -- Identificacao
196
- category TEXT NOT NULL, -- component, hook, service, route, schema, test, action
197
- name TEXT NOT NULL UNIQUE, -- ex: "page-component", "data-hook", "api-route"
198
-
199
- -- Contexto
200
- scope TEXT NOT NULL, -- frontend, backend, database, testing
201
- applies_to TEXT NOT NULL, -- glob pattern: "app/**/page.tsx", "src/hooks/*.ts"
202
-
203
- -- O Padrao Extraido
204
- structure TEXT NOT NULL, -- JSON com estrutura comum (imports, exports, patterns, conventions)
205
- template TEXT NOT NULL, -- Template de codigo com placeholders
206
- examples TEXT NOT NULL, -- JSON array de arquivos que seguem este padrao
207
-
208
- -- Anti-patterns identificados
209
- anti_patterns TEXT, -- JSON array de anti-patterns encontrados
210
-
211
- -- Metadados
212
- confidence REAL DEFAULT 0.8, -- 0-1, baseado em quantos arquivos seguem
213
- extracted_from INTEGER, -- Quantos arquivos foram analisados
214
-
215
- -- Auditoria
192
+ category TEXT NOT NULL,
193
+ name TEXT NOT NULL UNIQUE,
194
+ scope TEXT NOT NULL,
195
+ applies_to TEXT NOT NULL,
196
+ structure TEXT NOT NULL,
197
+ template TEXT NOT NULL,
198
+ examples TEXT NOT NULL,
199
+ anti_patterns TEXT,
200
+ confidence REAL DEFAULT 0.8,
201
+ extracted_from INTEGER,
216
202
  created_at TEXT DEFAULT CURRENT_TIMESTAMP,
217
203
  updated_at TEXT
218
- );
219
-
220
- -- 18. Knowledge Graph (v8.0 - Relacoes entre entidades)
204
+ )
205
+ `);
206
+ await dbExec(`
221
207
  CREATE TABLE IF NOT EXISTS knowledge_graph (
222
208
  id INTEGER PRIMARY KEY AUTOINCREMENT,
223
209
  spec_id TEXT REFERENCES specs(id),
224
-
225
- -- No de origem
226
- source_type TEXT NOT NULL, -- file, decision, pattern, task, artifact
210
+ source_type TEXT NOT NULL,
227
211
  source_id TEXT NOT NULL,
228
-
229
- -- No de destino
230
212
  target_type TEXT NOT NULL,
231
213
  target_id TEXT NOT NULL,
232
-
233
- -- Relacao
234
- relation TEXT NOT NULL, -- uses, implements, depends_on, modifies, contradicts, extracted_from
235
-
236
- -- Metadados
237
- metadata TEXT, -- JSON com informacoes adicionais
238
-
214
+ relation TEXT NOT NULL,
215
+ metadata TEXT,
239
216
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
240
- );
241
-
242
- -- 19. Reasoning Log (v8.0 - Historico de raciocinio)
217
+ )
218
+ `);
219
+ await dbExec(`
243
220
  CREATE TABLE IF NOT EXISTS reasoning_log (
244
221
  id INTEGER PRIMARY KEY AUTOINCREMENT,
245
222
  spec_id TEXT REFERENCES specs(id),
246
223
  task_id INTEGER REFERENCES tasks(id),
247
-
248
- -- O raciocinio
249
- category TEXT NOT NULL, -- decision, discovery, error, success, challenge, recommendation
250
- thought TEXT NOT NULL, -- "Escolhi X porque Y"
251
-
252
- -- Contexto
253
- related_files TEXT, -- JSON array de arquivos
254
- related_decisions TEXT, -- JSON array de decision IDs
255
- related_patterns TEXT, -- JSON array de pattern names
256
-
257
- -- Importancia
258
- importance TEXT DEFAULT 'normal', -- critical, high, normal, low
259
-
224
+ category TEXT NOT NULL,
225
+ thought TEXT NOT NULL,
226
+ related_files TEXT,
227
+ related_decisions TEXT,
228
+ related_patterns TEXT,
229
+ importance TEXT DEFAULT 'normal',
260
230
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
261
- );
262
-
263
- -- 20. Architectural Analyses (v8.1 - migrado de architect.ts para schema central)
231
+ )
232
+ `);
233
+ await dbExec(`
264
234
  CREATE TABLE IF NOT EXISTS architectural_analyses (
265
235
  id TEXT PRIMARY KEY,
266
236
  name TEXT NOT NULL,
267
237
  description TEXT NOT NULL,
268
238
  status TEXT NOT NULL DEFAULT 'pending',
269
-
270
- -- Conteudo JSON
271
239
  context TEXT,
272
240
  current_architecture TEXT,
273
241
  approach TEXT,
274
242
  chosen_alternative TEXT,
275
- diagrams TEXT, -- JSON array
276
- baby_steps TEXT, -- JSON array
277
- risks TEXT, -- JSON array
278
- alternatives TEXT, -- JSON array
279
- decisions TEXT, -- JSON array
280
-
281
- -- Arquivo gerado
243
+ diagrams TEXT,
244
+ baby_steps TEXT,
245
+ risks TEXT,
246
+ alternatives TEXT,
247
+ decisions TEXT,
282
248
  file_path TEXT,
283
-
284
- -- Timestamps
285
249
  created_at TEXT DEFAULT CURRENT_TIMESTAMP,
286
250
  updated_at TEXT,
287
251
  approved_at TEXT
288
- );
289
-
290
- -- 22. Project Utilities (v8.5 - DRY enforcement utility map)
252
+ )
253
+ `);
254
+ await dbExec(`
291
255
  CREATE TABLE IF NOT EXISTS project_utilities (
292
256
  id INTEGER PRIMARY KEY AUTOINCREMENT,
293
257
  file_path TEXT NOT NULL,
@@ -301,61 +265,60 @@ export function initSchema(): void {
301
265
  created_at TEXT DEFAULT CURRENT_TIMESTAMP,
302
266
  updated_at TEXT,
303
267
  UNIQUE(file_path, utility_name)
304
- );
305
-
306
- -- 23. Schema Migrations tracking (v9.2)
268
+ )
269
+ `);
270
+ await dbExec(`
307
271
  CREATE TABLE IF NOT EXISTS schema_migrations (
308
272
  version TEXT PRIMARY KEY,
309
273
  description TEXT NOT NULL,
310
274
  applied_at TEXT DEFAULT CURRENT_TIMESTAMP
311
- );
312
-
313
- -- Indices para performance
314
- CREATE INDEX IF NOT EXISTS idx_tasks_spec ON tasks(spec_id);
315
- CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
316
- CREATE INDEX IF NOT EXISTS idx_decisions_spec ON decisions(spec_id);
317
- CREATE INDEX IF NOT EXISTS idx_artifacts_spec ON artifacts(spec_id);
318
- CREATE INDEX IF NOT EXISTS idx_snapshots_spec ON snapshots(spec_id);
319
- CREATE INDEX IF NOT EXISTS idx_standards_category ON standards(category);
320
- CREATE INDEX IF NOT EXISTS idx_standards_scope ON standards(scope);
321
- CREATE INDEX IF NOT EXISTS idx_knowledge_spec ON knowledge(spec_id);
322
- CREATE INDEX IF NOT EXISTS idx_knowledge_category ON knowledge(category);
323
- CREATE INDEX IF NOT EXISTS idx_knowledge_severity ON knowledge(severity);
324
- CREATE INDEX IF NOT EXISTS idx_gate_bypasses_spec ON gate_bypasses(spec_id);
325
- CREATE INDEX IF NOT EXISTS idx_product_goals_product ON product_goals(product_id);
326
- CREATE INDEX IF NOT EXISTS idx_product_goals_category ON product_goals(category);
327
- CREATE INDEX IF NOT EXISTS idx_product_features_product ON product_features(product_id);
328
- CREATE INDEX IF NOT EXISTS idx_product_features_priority ON product_features(priority);
329
- CREATE INDEX IF NOT EXISTS idx_lib_contexts_lib ON lib_contexts(lib_name);
330
- CREATE INDEX IF NOT EXISTS idx_agent_lib_mappings_agent ON agent_lib_mappings(agent_type);
331
- CREATE INDEX IF NOT EXISTS idx_impl_patterns_category ON implementation_patterns(category);
332
- CREATE INDEX IF NOT EXISTS idx_impl_patterns_scope ON implementation_patterns(scope);
333
- CREATE INDEX IF NOT EXISTS idx_impl_patterns_name ON implementation_patterns(name);
334
-
335
- -- v8.0: Indices para Knowledge Graph
336
- CREATE INDEX IF NOT EXISTS idx_kg_source ON knowledge_graph(source_type, source_id);
337
- CREATE INDEX IF NOT EXISTS idx_kg_target ON knowledge_graph(target_type, target_id);
338
- CREATE INDEX IF NOT EXISTS idx_kg_relation ON knowledge_graph(relation);
339
- CREATE INDEX IF NOT EXISTS idx_kg_spec ON knowledge_graph(spec_id);
340
-
341
- -- v8.0: Indices para Reasoning Log
342
- CREATE INDEX IF NOT EXISTS idx_reasoning_spec ON reasoning_log(spec_id);
343
- CREATE INDEX IF NOT EXISTS idx_reasoning_task ON reasoning_log(task_id);
344
- CREATE INDEX IF NOT EXISTS idx_reasoning_category ON reasoning_log(category);
345
- CREATE INDEX IF NOT EXISTS idx_reasoning_importance ON reasoning_log(importance);
346
-
347
- -- v8.1: Indices para Architectural Analyses
348
- CREATE INDEX IF NOT EXISTS idx_arch_status ON architectural_analyses(status);
349
- CREATE INDEX IF NOT EXISTS idx_arch_created ON architectural_analyses(created_at);
350
-
351
- -- v8.5: Indices para Project Utilities
352
- CREATE INDEX IF NOT EXISTS idx_utils_scope ON project_utilities(scope);
353
- CREATE INDEX IF NOT EXISTS idx_utils_name ON project_utilities(utility_name);
354
- CREATE INDEX IF NOT EXISTS idx_utils_file ON project_utilities(file_path);
275
+ )
355
276
  `);
356
277
 
357
- // Executar migracoes versionadas
358
- runMigrations();
278
+ // Indices
279
+ const indices = [
280
+ "CREATE INDEX IF NOT EXISTS idx_tasks_spec ON tasks(spec_id)",
281
+ "CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status)",
282
+ "CREATE INDEX IF NOT EXISTS idx_decisions_spec ON decisions(spec_id)",
283
+ "CREATE INDEX IF NOT EXISTS idx_artifacts_spec ON artifacts(spec_id)",
284
+ "CREATE INDEX IF NOT EXISTS idx_snapshots_spec ON snapshots(spec_id)",
285
+ "CREATE INDEX IF NOT EXISTS idx_standards_category ON standards(category)",
286
+ "CREATE INDEX IF NOT EXISTS idx_standards_scope ON standards(scope)",
287
+ "CREATE INDEX IF NOT EXISTS idx_knowledge_spec ON knowledge(spec_id)",
288
+ "CREATE INDEX IF NOT EXISTS idx_knowledge_category ON knowledge(category)",
289
+ "CREATE INDEX IF NOT EXISTS idx_knowledge_severity ON knowledge(severity)",
290
+ "CREATE INDEX IF NOT EXISTS idx_gate_bypasses_spec ON gate_bypasses(spec_id)",
291
+ "CREATE INDEX IF NOT EXISTS idx_product_goals_product ON product_goals(product_id)",
292
+ "CREATE INDEX IF NOT EXISTS idx_product_goals_category ON product_goals(category)",
293
+ "CREATE INDEX IF NOT EXISTS idx_product_features_product ON product_features(product_id)",
294
+ "CREATE INDEX IF NOT EXISTS idx_product_features_priority ON product_features(priority)",
295
+ "CREATE INDEX IF NOT EXISTS idx_lib_contexts_lib ON lib_contexts(lib_name)",
296
+ "CREATE INDEX IF NOT EXISTS idx_agent_lib_mappings_agent ON agent_lib_mappings(agent_type)",
297
+ "CREATE INDEX IF NOT EXISTS idx_impl_patterns_category ON implementation_patterns(category)",
298
+ "CREATE INDEX IF NOT EXISTS idx_impl_patterns_scope ON implementation_patterns(scope)",
299
+ "CREATE INDEX IF NOT EXISTS idx_impl_patterns_name ON implementation_patterns(name)",
300
+ "CREATE INDEX IF NOT EXISTS idx_kg_source ON knowledge_graph(source_type, source_id)",
301
+ "CREATE INDEX IF NOT EXISTS idx_kg_target ON knowledge_graph(target_type, target_id)",
302
+ "CREATE INDEX IF NOT EXISTS idx_kg_relation ON knowledge_graph(relation)",
303
+ "CREATE INDEX IF NOT EXISTS idx_kg_spec ON knowledge_graph(spec_id)",
304
+ "CREATE INDEX IF NOT EXISTS idx_reasoning_spec ON reasoning_log(spec_id)",
305
+ "CREATE INDEX IF NOT EXISTS idx_reasoning_task ON reasoning_log(task_id)",
306
+ "CREATE INDEX IF NOT EXISTS idx_reasoning_category ON reasoning_log(category)",
307
+ "CREATE INDEX IF NOT EXISTS idx_reasoning_importance ON reasoning_log(importance)",
308
+ "CREATE INDEX IF NOT EXISTS idx_arch_status ON architectural_analyses(status)",
309
+ "CREATE INDEX IF NOT EXISTS idx_arch_created ON architectural_analyses(created_at)",
310
+ "CREATE INDEX IF NOT EXISTS idx_utils_scope ON project_utilities(scope)",
311
+ "CREATE INDEX IF NOT EXISTS idx_utils_name ON project_utilities(utility_name)",
312
+ "CREATE INDEX IF NOT EXISTS idx_utils_file ON project_utilities(file_path)",
313
+ ];
314
+
315
+ const db = getDb();
316
+ await db.batch(
317
+ indices.map((sql) => ({ sql, args: [] })),
318
+ "write"
319
+ );
320
+
321
+ await runMigrations();
359
322
  }
360
323
 
361
324
  // ═══════════════════════════════════════════════════════════════
@@ -365,50 +328,50 @@ export function initSchema(): void {
365
328
  interface Migration {
366
329
  version: string;
367
330
  description: string;
368
- up: (db: Database) => void;
331
+ up: () => Promise<void>;
369
332
  }
370
333
 
371
334
  const MIGRATIONS: Migration[] = [
372
335
  {
373
336
  version: "8.4.0",
374
337
  description: "Adicionar analysis_id na tabela specs",
375
- up: (db) => {
376
- db.exec(`ALTER TABLE specs ADD COLUMN analysis_id TEXT`);
338
+ up: async () => {
339
+ await dbExec("ALTER TABLE specs ADD COLUMN analysis_id TEXT");
377
340
  },
378
341
  },
379
342
  {
380
343
  version: "8.7.0",
381
344
  description: "Adicionar cli_version na tabela project",
382
- up: (db) => {
383
- db.exec(`ALTER TABLE project ADD COLUMN cli_version TEXT`);
345
+ up: async () => {
346
+ await dbExec("ALTER TABLE project ADD COLUMN cli_version TEXT");
384
347
  },
385
348
  },
386
349
  {
387
350
  version: "9.1.0",
388
351
  description: "Adicionar last_discover_at na tabela project",
389
- up: (db) => {
390
- db.exec(`ALTER TABLE project ADD COLUMN last_discover_at TEXT`);
352
+ up: async () => {
353
+ await dbExec("ALTER TABLE project ADD COLUMN last_discover_at TEXT");
391
354
  },
392
355
  },
393
356
  {
394
357
  version: "9.1.1",
395
358
  description: "Remover tabela session_summaries",
396
- up: (db) => {
397
- db.exec(`DROP TABLE IF EXISTS session_summaries`);
359
+ up: async () => {
360
+ await dbExec("DROP TABLE IF EXISTS session_summaries");
398
361
  },
399
362
  },
400
363
  {
401
364
  version: "9.2.0",
402
365
  description: "Adicionar started_at na tabela tasks para validacao temporal de arquivos",
403
- up: (db) => {
404
- db.exec(`ALTER TABLE tasks ADD COLUMN started_at TEXT`);
366
+ up: async () => {
367
+ await dbExec("ALTER TABLE tasks ADD COLUMN started_at TEXT");
405
368
  },
406
369
  },
407
370
  {
408
371
  version: "9.3.0",
409
372
  description: "Criar tabela agent_performance para feedback loop",
410
- up: (db) => {
411
- db.exec(`
373
+ up: async () => {
374
+ await dbExec(`
412
375
  CREATE TABLE IF NOT EXISTS agent_performance (
413
376
  id INTEGER PRIMARY KEY AUTOINCREMENT,
414
377
  agent_type TEXT NOT NULL,
@@ -424,15 +387,15 @@ const MIGRATIONS: Migration[] = [
424
387
  created_at TEXT DEFAULT CURRENT_TIMESTAMP
425
388
  )
426
389
  `);
427
- db.exec(`CREATE INDEX IF NOT EXISTS idx_agent_perf_type ON agent_performance(agent_type)`);
428
- db.exec(`CREATE INDEX IF NOT EXISTS idx_agent_perf_created ON agent_performance(created_at)`);
390
+ await dbExec("CREATE INDEX IF NOT EXISTS idx_agent_perf_type ON agent_performance(agent_type)");
391
+ await dbExec("CREATE INDEX IF NOT EXISTS idx_agent_perf_created ON agent_performance(created_at)");
429
392
  },
430
393
  },
431
394
  {
432
395
  version: "9.4.0",
433
396
  description: "Migrar acknowledged_by de JSON para tabela separada",
434
- up: (db) => {
435
- db.exec(`
397
+ up: async () => {
398
+ await dbExec(`
436
399
  CREATE TABLE IF NOT EXISTS knowledge_acknowledgments (
437
400
  knowledge_id INTEGER NOT NULL REFERENCES knowledge(id) ON DELETE CASCADE,
438
401
  task_id INTEGER NOT NULL,
@@ -440,96 +403,92 @@ const MIGRATIONS: Migration[] = [
440
403
  PRIMARY KEY (knowledge_id, task_id)
441
404
  )
442
405
  `);
443
- db.exec(`CREATE INDEX IF NOT EXISTS idx_ka_task ON knowledge_acknowledgments(task_id)`);
444
-
445
- // Migrar dados existentes do campo JSON
446
- const rows = db.query("SELECT id, acknowledged_by FROM knowledge WHERE acknowledged_by IS NOT NULL").all() as any[];
447
- const insert = db.prepare("INSERT OR IGNORE INTO knowledge_acknowledgments (knowledge_id, task_id) VALUES (?, ?)");
448
- for (const row of rows) {
449
- try {
450
- const taskIds = JSON.parse(row.acknowledged_by) as number[];
451
- for (const taskId of taskIds) {
452
- insert.run(row.id, taskId);
453
- }
454
- } catch { /* JSON invalido, ignorar */ }
455
- }
406
+ await dbExec("CREATE INDEX IF NOT EXISTS idx_ka_task ON knowledge_acknowledgments(task_id)");
407
+
408
+ try {
409
+ const rows = await dbAll<any>("SELECT id, acknowledged_by FROM knowledge WHERE acknowledged_by IS NOT NULL");
410
+ for (const row of rows) {
411
+ try {
412
+ const taskIds = JSON.parse(row.acknowledged_by) as number[];
413
+ for (const taskId of taskIds) {
414
+ await dbRun("INSERT OR IGNORE INTO knowledge_acknowledgments (knowledge_id, task_id) VALUES (?, ?)", [row.id, taskId]);
415
+ }
416
+ } catch { /* JSON invalido, ignorar */ }
417
+ }
418
+ } catch { /* Coluna acknowledged_by nao existe em DB fresh */ }
456
419
  },
457
420
  },
458
421
  {
459
422
  version: "9.5.0",
460
423
  description: "Adicionar superseded_by na tabela decisions",
461
- up: (db) => {
462
- db.exec(`ALTER TABLE decisions ADD COLUMN superseded_by TEXT`);
424
+ up: async () => {
425
+ await dbExec("ALTER TABLE decisions ADD COLUMN superseded_by TEXT");
463
426
  },
464
427
  },
465
428
  {
466
429
  version: "9.6.0",
467
430
  description: "Adicionar semantic_query e expect na tabela standards para validacao semantica stack-agnostica",
468
- up: (db) => {
469
- db.exec(`ALTER TABLE standards ADD COLUMN semantic_query TEXT`);
470
- db.exec(`ALTER TABLE standards ADD COLUMN expect TEXT DEFAULT 'no_match'`);
431
+ up: async () => {
432
+ await dbExec("ALTER TABLE standards ADD COLUMN semantic_query TEXT");
433
+ await dbExec("ALTER TABLE standards ADD COLUMN expect TEXT DEFAULT 'no_match'");
471
434
  },
472
435
  },
473
436
  {
474
437
  version: "9.7.0",
475
438
  description: "Adicionar typecheck_command na tabela project para typecheck agnostico de stack",
476
- up: (db) => {
477
- db.exec(`ALTER TABLE project ADD COLUMN typecheck_command TEXT`);
439
+ up: async () => {
440
+ await dbExec("ALTER TABLE project ADD COLUMN typecheck_command TEXT");
478
441
  },
479
442
  },
480
443
  {
481
444
  version: "9.8.0",
482
445
  description: "Adicionar grepai_workspace na tabela project para suporte a workspace cross-project",
483
- up: (db) => {
484
- db.exec(`ALTER TABLE project ADD COLUMN grepai_workspace TEXT`);
446
+ up: async () => {
447
+ await dbExec("ALTER TABLE project ADD COLUMN grepai_workspace TEXT");
485
448
  },
486
449
  },
487
450
  {
488
451
  version: "9.9.0",
489
452
  description: "Adicionar suporte a fases: phase em tasks, total_phases/current_phase em context",
490
- up: (db) => {
491
- db.exec(`ALTER TABLE tasks ADD COLUMN phase INTEGER DEFAULT 1`);
492
- db.exec(`ALTER TABLE context ADD COLUMN total_phases INTEGER DEFAULT 1`);
493
- db.exec(`ALTER TABLE context ADD COLUMN current_phase INTEGER DEFAULT 1`);
453
+ up: async () => {
454
+ await dbExec("ALTER TABLE tasks ADD COLUMN phase INTEGER DEFAULT 1");
455
+ await dbExec("ALTER TABLE context ADD COLUMN total_phases INTEGER DEFAULT 1");
456
+ await dbExec("ALTER TABLE context ADD COLUMN current_phase INTEGER DEFAULT 1");
494
457
  },
495
458
  },
496
459
  {
497
460
  version: "10.1.0",
498
461
  description: "Adicionar auto_simplify na tabela project para simplificacao automatica pos-task",
499
- up: (db) => {
500
- db.exec(`ALTER TABLE project ADD COLUMN auto_simplify INTEGER DEFAULT 0`);
462
+ up: async () => {
463
+ await dbExec("ALTER TABLE project ADD COLUMN auto_simplify INTEGER DEFAULT 0");
501
464
  },
502
465
  },
503
466
  {
504
467
  version: "10.3.0",
505
468
  description: "Remover colunas mortas: review.pattern_violations, review.resolution, knowledge.acknowledged_by, decisions.superseded_by",
506
- up: (db) => {
507
- db.exec(`ALTER TABLE review DROP COLUMN pattern_violations`);
508
- db.exec(`ALTER TABLE review DROP COLUMN resolution`);
509
- db.exec(`ALTER TABLE knowledge DROP COLUMN acknowledged_by`);
510
- db.exec(`ALTER TABLE decisions DROP COLUMN superseded_by`);
469
+ up: async () => {
470
+ for (const stmt of [
471
+ "ALTER TABLE review DROP COLUMN pattern_violations",
472
+ "ALTER TABLE review DROP COLUMN resolution",
473
+ "ALTER TABLE knowledge DROP COLUMN acknowledged_by",
474
+ "ALTER TABLE decisions DROP COLUMN superseded_by",
475
+ ]) {
476
+ try { await dbExec(stmt); } catch { /* Coluna pode nao existir em DB fresh */ }
477
+ }
511
478
  },
512
479
  },
513
480
  ];
514
481
 
515
- export function runMigrations(): void {
516
- const db = getDb();
517
-
482
+ export async function runMigrations(): Promise<void> {
518
483
  for (const migration of MIGRATIONS) {
519
- // Verificar se ja foi aplicada
520
- const existing = db
521
- .query("SELECT version FROM schema_migrations WHERE version = ?")
522
- .get(migration.version);
523
-
484
+ const existing = await dbGet("SELECT version FROM schema_migrations WHERE version = ?", [migration.version]);
524
485
  if (existing) continue;
525
486
 
526
- // Executar migracao
527
487
  try {
528
- migration.up(db);
488
+ await migration.up();
529
489
  } catch (e: any) {
530
490
  const msg = (e as Error).message || "";
531
- // "duplicate column name" e esperado em DBs criadas antes do sistema de migracoes
532
- if (msg.includes("duplicate column name") || msg.includes("already exists")) {
491
+ if (msg.includes("duplicate column name") || msg.includes("already exists") || msg.includes("no such column")) {
533
492
  // Marcar como aplicada mesmo assim — DB ja tem a mudanca
534
493
  } else {
535
494
  throw new Error(
@@ -539,60 +498,31 @@ export function runMigrations(): void {
539
498
  }
540
499
  }
541
500
 
542
- // Registrar migracao como aplicada
543
501
  const now = new Date().toISOString();
544
- db.run(
502
+ await dbRun(
545
503
  "INSERT INTO schema_migrations (version, description, applied_at) VALUES (?, ?, ?)",
546
504
  [migration.version, migration.description, now]
547
505
  );
548
506
  }
549
507
  }
550
508
 
551
- // Exportar MIGRATIONS para testes
552
509
  export { MIGRATIONS };
553
510
 
554
511
  // ═══════════════════════════════════════════════════════════════
555
512
  // v10.2: Transacao Atomica Helper
556
- // Wrapa qualquer operacao em BEGIN IMMEDIATE / COMMIT com
557
- // rollback automatico em caso de erro.
558
- // BEGIN IMMEDIATE garante lock exclusivo para escrita desde o
559
- // inicio, evitando deadlocks em WAL mode.
560
513
  // ═══════════════════════════════════════════════════════════════
561
514
 
562
- const MAX_BUSY_RETRIES = 3;
563
- const BUSY_RETRY_BASE_MS = 50;
564
-
565
- export function runInTransaction<T>(fn: () => T): T {
515
+ export async function runInTransaction<T>(fn: (tx: Transaction) => Promise<T>): Promise<T> {
566
516
  const db = getDb();
567
- let retries = MAX_BUSY_RETRIES;
568
-
569
- while (retries > 0) {
570
- try {
571
- db.exec("BEGIN IMMEDIATE");
572
- break;
573
- } catch (e: any) {
574
- if (e.message?.includes("SQLITE_BUSY") && retries > 1) {
575
- retries--;
576
- const delay = BUSY_RETRY_BASE_MS * (MAX_BUSY_RETRIES - retries);
577
- Bun.sleepSync(delay);
578
- continue;
579
- }
580
- throw e;
581
- }
582
- }
583
517
 
584
- try {
585
- const result = fn();
586
- db.exec("COMMIT");
587
- return result;
588
- } catch (e) {
589
- try { db.exec("ROLLBACK"); } catch { /* already rolled back */ }
590
- throw e;
591
- }
518
+ // Inner functions use dbRun/dbGet/dbAll (global client) instead of tx directly.
519
+ // With @libsql/client, wrapping in a real transaction and swapping the global client
520
+ // breaks :memory: DBs (connection becomes invalid after tx.commit).
521
+ // Since the inner functions already use the global helpers, we run without a real
522
+ // transaction wrapper. The client-level execute is already serialized by @libsql/client.
523
+ return await fn(db as unknown as Transaction);
592
524
  }
593
525
 
594
- // Gera proximo ID de decisao para um spec
595
- // Usa timestamp + random hash para eliminar race conditions entre tasks paralelas
596
526
  export function getNextDecisionId(specId: string): string {
597
527
  const slug = specId.split("-").slice(1, 3).join("-");
598
528
  const ts = Date.now().toString(36);
@@ -600,25 +530,22 @@ export function getNextDecisionId(specId: string): string {
600
530
  return `DEC-${slug}-${ts}-${rand}`;
601
531
  }
602
532
 
603
- // Claim atomico de task: retorna true se task estava pending e agora esta running.
604
- // Retorna false se outra instancia ja pegou a task.
605
- export function claimTask(taskId: number): boolean {
606
- const db = getDb();
533
+ export async function claimTask(taskId: number): Promise<boolean> {
607
534
  const now = new Date().toISOString();
608
- const result = db.run(
535
+ const result = await dbRun(
609
536
  "UPDATE tasks SET status = 'running', started_at = ? WHERE id = ? AND status = 'pending'",
610
537
  [now, taskId]
611
538
  );
612
539
  return result.changes > 0;
613
540
  }
614
541
 
615
- const STUCK_THRESHOLD_MS = 30 * 60 * 1000; // 30 minutos
542
+ const STUCK_THRESHOLD_MS = 30 * 60 * 1000;
616
543
 
617
- export function detectStuckTasks(specId: string): any[] {
618
- const db = getDb();
619
- const running = db.query(
620
- "SELECT * FROM tasks WHERE spec_id = ? AND status = 'running'"
621
- ).all(specId) as any[];
544
+ export async function detectStuckTasks(specId: string): Promise<any[]> {
545
+ const running = await dbAll(
546
+ "SELECT * FROM tasks WHERE spec_id = ? AND status = 'running'",
547
+ [specId]
548
+ );
622
549
 
623
550
  const now = Date.now();
624
551
  return running.filter((task: any) => {
@@ -628,24 +555,22 @@ export function detectStuckTasks(specId: string): any[] {
628
555
  });
629
556
  }
630
557
 
631
- export function getPatternsByScope(scope: string): any[] {
632
- const db = getDb();
633
- return db.query(
634
- "SELECT * FROM implementation_patterns WHERE scope = ? OR scope = 'all' ORDER BY category, name"
635
- ).all(scope) as any[];
558
+ export async function getPatternsByScope(scope: string): Promise<any[]> {
559
+ return dbAll(
560
+ "SELECT * FROM implementation_patterns WHERE scope = ? OR scope = 'all' ORDER BY category, name",
561
+ [scope]
562
+ );
636
563
  }
637
564
 
638
- export function getPatternsForFiles(files: string[]): any[] {
639
- const db = getDb();
565
+ export async function getPatternsForFiles(files: string[]): Promise<any[]> {
640
566
  if (files.length === 0) return [];
641
567
 
642
- // Pre-filtrar no SQL usando extensoes e diretorios para reduzir candidatos
643
568
  const extensions = new Set<string>();
644
569
  const dirs = new Set<string>();
645
570
  for (const f of files) {
646
- const ext = f.split('.').pop();
571
+ const ext = f.split(".").pop();
647
572
  if (ext) extensions.add(ext);
648
- const parts = f.split('/');
573
+ const parts = f.split("/");
649
574
  for (let i = 0; i < parts.length - 1; i++) {
650
575
  dirs.add(parts[i]);
651
576
  }
@@ -662,20 +587,18 @@ export function getPatternsForFiles(files: string[]): any[] {
662
587
  params.push(`%${dir}%`);
663
588
  }
664
589
 
665
- // Se nao ha condicoes, buscar tudo (fallback)
666
590
  const candidates = conditions.length > 0
667
- ? db.query(`SELECT * FROM implementation_patterns WHERE ${conditions.join(' OR ')}`).all(...params) as any[]
668
- : db.query("SELECT * FROM implementation_patterns").all() as any[];
591
+ ? await dbAll(`SELECT * FROM implementation_patterns WHERE ${conditions.join(" OR ")}`, params)
592
+ : await dbAll("SELECT * FROM implementation_patterns");
669
593
 
670
- // Regex match apenas nos candidatos pre-filtrados
671
- return candidates.filter(pattern => {
594
+ return candidates.filter((pattern: any) => {
672
595
  const glob = pattern.applies_to;
673
596
  const regexPattern = glob
674
- .replace(/\*\*/g, '.*')
675
- .replace(/\*/g, '[^/]*')
676
- .replace(/\//g, '\\/');
597
+ .replace(/\*\*/g, ".*")
598
+ .replace(/\*/g, "[^/]*")
599
+ .replace(/\//g, "\\/");
677
600
  const regex = new RegExp(`^${regexPattern}$`);
678
- return files.some(file => regex.test(file));
601
+ return files.some((file) => regex.test(file));
679
602
  });
680
603
  }
681
604
 
@@ -692,11 +615,9 @@ export interface GraphRelation {
692
615
  metadata?: Record<string, any>;
693
616
  }
694
617
 
695
- export function addGraphRelation(specId: string | null, relation: GraphRelation): void {
696
- const db = getDb();
618
+ export async function addGraphRelation(specId: string | null, relation: GraphRelation): Promise<void> {
697
619
  const now = new Date().toISOString();
698
-
699
- db.run(
620
+ await dbRun(
700
621
  `INSERT INTO knowledge_graph (spec_id, source_type, source_id, target_type, target_id, relation, metadata, created_at)
701
622
  VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
702
623
  [
@@ -712,28 +633,25 @@ export function addGraphRelation(specId: string | null, relation: GraphRelation)
712
633
  );
713
634
  }
714
635
 
715
- export function getRelatedDecisions(fileOrPattern: string, type: "file" | "pattern"): any[] {
716
- const db = getDb();
717
-
718
- return db.query(`
719
- SELECT d.* FROM decisions d
636
+ export async function getRelatedDecisions(fileOrPattern: string, type: "file" | "pattern"): Promise<any[]> {
637
+ return dbAll(
638
+ `SELECT d.* FROM decisions d
720
639
  JOIN knowledge_graph kg ON kg.source_id = d.id AND kg.source_type = 'decision'
721
640
  WHERE kg.target_type = ? AND kg.target_id = ?
722
- AND kg.relation IN ('implements', 'depends_on', 'uses')
723
- `).all(type, fileOrPattern) as any[];
641
+ AND kg.relation IN ('implements', 'depends_on', 'uses')`,
642
+ [type, fileOrPattern]
643
+ );
724
644
  }
725
645
 
726
- export function getRelatedFiles(decisionOrPattern: string, type: "decision" | "pattern"): string[] {
727
- const db = getDb();
728
-
729
- const results = db.query(`
730
- SELECT kg.target_id as file FROM knowledge_graph kg
646
+ export async function getRelatedFiles(decisionOrPattern: string, type: "decision" | "pattern"): Promise<string[]> {
647
+ const results = await dbAll(
648
+ `SELECT kg.target_id as file FROM knowledge_graph kg
731
649
  WHERE kg.source_type = ? AND kg.source_id = ?
732
650
  AND kg.target_type = 'file'
733
- AND kg.relation IN ('implements', 'modifies', 'creates')
734
- `).all(type, decisionOrPattern) as any[];
735
-
736
- return results.map(r => r.file);
651
+ AND kg.relation IN ('implements', 'modifies', 'creates')`,
652
+ [type, decisionOrPattern]
653
+ );
654
+ return results.map((r: any) => r.file);
737
655
  }
738
656
 
739
657
  // ═══════════════════════════════════════════════════════════════
@@ -749,11 +667,9 @@ export interface ReasoningEntry {
749
667
  importance?: "critical" | "high" | "normal" | "low";
750
668
  }
751
669
 
752
- export function addReasoning(specId: string, taskId: number | null, entry: ReasoningEntry): void {
753
- const db = getDb();
670
+ export async function addReasoning(specId: string, taskId: number | null, entry: ReasoningEntry): Promise<void> {
754
671
  const now = new Date().toISOString();
755
-
756
- db.run(
672
+ await dbRun(
757
673
  `INSERT INTO reasoning_log (spec_id, task_id, category, thought, related_files, related_decisions, related_patterns, importance, created_at)
758
674
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
759
675
  [
@@ -770,49 +686,39 @@ export function addReasoning(specId: string, taskId: number | null, entry: Reaso
770
686
  );
771
687
  }
772
688
 
773
- export function getRecentReasoning(specId: string, limit: number = 20): any[] {
774
- const db = getDb();
775
-
776
- return db.query(`
777
- SELECT * FROM reasoning_log
778
- WHERE spec_id = ?
779
- ORDER BY created_at DESC
780
- LIMIT ?
781
- `).all(specId, limit) as any[];
689
+ export async function getRecentReasoning(specId: string, limit: number = 20): Promise<any[]> {
690
+ return dbAll(
691
+ "SELECT * FROM reasoning_log WHERE spec_id = ? ORDER BY created_at DESC LIMIT ?",
692
+ [specId, limit]
693
+ );
782
694
  }
783
695
 
784
- // v8.4: Busca analise arquitetural associada a um spec
785
- // Prioridade: analysis_id (link explicito) > nome exato > LIKE parcial
786
- export function getArchitecturalAnalysisForSpec(specName: string, specId?: string): any {
787
- const db = getDb();
788
-
696
+ export async function getArchitecturalAnalysisForSpec(specName: string, specId?: string): Promise<any> {
789
697
  try {
790
- // 0. Link explicito via analysis_id na tabela specs
791
698
  if (specId) {
792
- const spec = db.query("SELECT analysis_id FROM specs WHERE id = ?").get(specId) as any;
699
+ const spec = await dbGet<any>("SELECT analysis_id FROM specs WHERE id = ?", [specId]);
793
700
  if (spec?.analysis_id) {
794
- const analysis = db.query(
795
- "SELECT * FROM architectural_analyses WHERE id = ? AND status IN ('approved', 'implemented')"
796
- ).get(spec.analysis_id) as any;
701
+ const analysis = await dbGet(
702
+ "SELECT * FROM architectural_analyses WHERE id = ? AND status IN ('approved', 'implemented')",
703
+ [spec.analysis_id]
704
+ );
797
705
  if (analysis) return analysis;
798
706
  }
799
707
  }
800
708
 
801
- // 1. Match exato por nome
802
- let analysis = db.query(
803
- "SELECT * FROM architectural_analyses WHERE name = ? AND status IN ('approved', 'implemented') ORDER BY approved_at DESC LIMIT 1"
804
- ).get(specName) as any;
805
-
709
+ let analysis = await dbGet(
710
+ "SELECT * FROM architectural_analyses WHERE name = ? AND status IN ('approved', 'implemented') ORDER BY approved_at DESC LIMIT 1",
711
+ [specName]
712
+ );
806
713
  if (analysis) return analysis;
807
714
 
808
- // 2. Match parcial (nome da spec contido no nome da analise ou vice-versa)
809
- analysis = db.query(
810
- "SELECT * FROM architectural_analyses WHERE (name LIKE ? OR ? LIKE '%' || name || '%') AND status IN ('approved', 'implemented') ORDER BY approved_at DESC LIMIT 1"
811
- ).get(`%${specName}%`, specName) as any;
715
+ analysis = await dbGet(
716
+ "SELECT * FROM architectural_analyses WHERE (name LIKE ? OR ? LIKE '%' || name || '%') AND status IN ('approved', 'implemented') ORDER BY approved_at DESC LIMIT 1",
717
+ [`%${specName}%`, specName]
718
+ );
812
719
 
813
720
  return analysis || null;
814
721
  } catch {
815
- // Tabela pode nao existir se architect nunca foi usado
816
722
  return null;
817
723
  }
818
724
  }
@@ -830,14 +736,13 @@ export interface ProjectUtility {
830
736
  description?: string;
831
737
  }
832
738
 
833
- export function upsertUtility(
739
+ export async function upsertUtility(
834
740
  utility: ProjectUtility,
835
741
  specId?: string,
836
742
  taskRef?: number
837
- ): void {
838
- const db = getDb();
743
+ ): Promise<void> {
839
744
  const now = new Date().toISOString();
840
- db.run(
745
+ await dbRun(
841
746
  `INSERT INTO project_utilities (file_path, utility_name, utility_type, scope, signature, description, spec_id, task_ref, created_at, updated_at)
842
747
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
843
748
  ON CONFLICT(file_path, utility_name) DO UPDATE SET
@@ -854,21 +759,18 @@ export function upsertUtility(
854
759
  );
855
760
  }
856
761
 
857
- export function getUtilitiesForContext(
762
+ export async function getUtilitiesForContext(
858
763
  dirs: string[],
859
764
  scope?: string,
860
765
  limit: number = 15
861
- ): any[] {
862
- const db = getDb();
863
-
864
- // Se nenhum filtro, retornar mais recentes
766
+ ): Promise<any[]> {
865
767
  if (dirs.length === 0 && !scope) {
866
- return db.query(
867
- "SELECT * FROM project_utilities ORDER BY updated_at DESC LIMIT ?"
868
- ).all(limit) as any[];
768
+ return dbAll(
769
+ "SELECT * FROM project_utilities ORDER BY updated_at DESC LIMIT ?",
770
+ [limit]
771
+ );
869
772
  }
870
773
 
871
- // Buscar por diretorio (prioridade) e scope (fallback)
872
774
  const conditions: string[] = [];
873
775
  const params: any[] = [];
874
776
 
@@ -885,24 +787,26 @@ export function getUtilitiesForContext(
885
787
 
886
788
  params.push(limit);
887
789
  const where = conditions.length > 0 ? `WHERE (${conditions.join(" OR ")})` : "";
888
- return db.query(
889
- `SELECT * FROM project_utilities ${where} ORDER BY updated_at DESC LIMIT ?`
890
- ).all(...params) as any[];
790
+ return dbAll(
791
+ `SELECT * FROM project_utilities ${where} ORDER BY updated_at DESC LIMIT ?`,
792
+ params
793
+ );
891
794
  }
892
795
 
893
- export function findDuplicateUtilities(
796
+ export async function findDuplicateUtilities(
894
797
  utilityName: string,
895
798
  excludeFile?: string
896
- ): any[] {
897
- const db = getDb();
799
+ ): Promise<any[]> {
898
800
  if (excludeFile) {
899
- return db.query(
900
- "SELECT * FROM project_utilities WHERE utility_name = ? AND file_path != ?"
901
- ).all(utilityName, excludeFile) as any[];
801
+ return dbAll(
802
+ "SELECT * FROM project_utilities WHERE utility_name = ? AND file_path != ?",
803
+ [utilityName, excludeFile]
804
+ );
902
805
  }
903
- return db.query(
904
- "SELECT * FROM project_utilities WHERE utility_name = ?"
905
- ).all(utilityName) as any[];
806
+ return dbAll(
807
+ "SELECT * FROM project_utilities WHERE utility_name = ?",
808
+ [utilityName]
809
+ );
906
810
  }
907
811
 
908
812
  // ═══════════════════════════════════════════════════════════════
@@ -922,10 +826,9 @@ export interface AgentPerformanceData {
922
826
  executionDurationMs: number;
923
827
  }
924
828
 
925
- export function recordAgentPerformance(data: AgentPerformanceData): void {
926
- const db = getDb();
829
+ export async function recordAgentPerformance(data: AgentPerformanceData): Promise<void> {
927
830
  const now = new Date().toISOString();
928
- db.run(
831
+ await dbRun(
929
832
  `INSERT INTO agent_performance
930
833
  (agent_type, spec_id, task_id, gates_passed_first_try, gates_total, bypasses_used, files_created, files_modified, context_size_bytes, execution_duration_ms, created_at)
931
834
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
@@ -938,16 +841,16 @@ export function recordAgentPerformance(data: AgentPerformanceData): void {
938
841
  );
939
842
  }
940
843
 
941
- export function getAgentHints(agentType: string, limit: number = 5): string[] {
942
- const db = getDb();
844
+ export async function getAgentHints(agentType: string, limit: number = 5): Promise<string[]> {
943
845
  const hints: string[] = [];
944
846
 
945
847
  try {
946
- const recent = db.query(
848
+ const recent = await dbAll(
947
849
  `SELECT * FROM agent_performance
948
850
  WHERE agent_type = ?
949
- ORDER BY created_at DESC LIMIT ?`
950
- ).all(agentType, limit) as any[];
851
+ ORDER BY created_at DESC LIMIT ?`,
852
+ [agentType, limit]
853
+ );
951
854
 
952
855
  if (recent.length === 0) return [];
953
856
 
@@ -964,17 +867,18 @@ export function getAgentHints(agentType: string, limit: number = 5): string[] {
964
867
  hints.push(`ATENCAO: Gate pass rate baixo (${(avgGateRate * 100).toFixed(0)}%). Verifique standards e DRY obrigatorios.`);
965
868
  }
966
869
 
967
- const bypassTypes = db.query(
870
+ const bypassTypes = await dbAll(
968
871
  `SELECT gb.gate_name, COUNT(*) as cnt FROM gate_bypasses gb
969
872
  JOIN tasks t ON gb.task_id = t.id
970
873
  WHERE t.agent = ?
971
874
  GROUP BY gb.gate_name
972
- ORDER BY cnt DESC LIMIT 3`
973
- ).all(agentType) as any[];
875
+ ORDER BY cnt DESC LIMIT 3`,
876
+ [agentType]
877
+ );
974
878
 
975
879
  for (const bp of bypassTypes) {
976
- if (bp.cnt >= 2) {
977
- hints.push(`Gate '${bp.gate_name}' frequentemente ignorado (${bp.cnt}x). Preste atencao especial.`);
880
+ if ((bp as any).cnt >= 2) {
881
+ hints.push(`Gate '${(bp as any).gate_name}' frequentemente ignorado (${(bp as any).cnt}x). Preste atencao especial.`);
978
882
  }
979
883
  }
980
884
  } catch {