@codexa/cli 9.0.31 → 9.0.32

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