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