@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.
@@ -1,4 +1,4 @@
1
- import { getDb } from "../db/connection";
1
+ import { dbGet, dbAll, dbRun } from "../db/connection";
2
2
  import { initSchema, getRelatedDecisions, getRelatedFiles } from "../db/schema";
3
3
  import { resolveSpec } from "./spec-resolver";
4
4
  import { CodexaError, ValidationError } from "../errors";
@@ -15,22 +15,21 @@ interface AddKnowledgeOptions {
15
15
  specId?: string;
16
16
  }
17
17
 
18
- function getCurrentTask(specId: string): any {
19
- const db = getDb();
20
- return db
21
- .query("SELECT * FROM tasks WHERE spec_id = ? AND status = 'running' LIMIT 1")
22
- .get(specId);
18
+ async function getCurrentTask(specId: string): Promise<any> {
19
+ return await dbGet<any>(
20
+ "SELECT * FROM tasks WHERE spec_id = ? AND status = 'running' LIMIT 1",
21
+ [specId]
22
+ );
23
23
  }
24
24
 
25
- export function addKnowledge(options: AddKnowledgeOptions): void {
26
- initSchema();
25
+ export async function addKnowledge(options: AddKnowledgeOptions): Promise<void> {
26
+ await initSchema();
27
27
 
28
- const db = getDb();
29
28
  const now = new Date().toISOString();
30
29
 
31
30
  const spec = resolveSpec(options.specId);
32
31
 
33
- const currentTask = getCurrentTask(spec.id);
32
+ const currentTask = await getCurrentTask(spec.id);
34
33
  if (!currentTask) {
35
34
  throw new CodexaError("Nenhuma task em execucao.\nKnowledge so pode ser adicionado durante execucao de uma task.");
36
35
  }
@@ -43,7 +42,7 @@ export function addKnowledge(options: AddKnowledgeOptions): void {
43
42
  const severity = options.severity || "info";
44
43
  const broadcastTo = options.broadcastTo || "all";
45
44
 
46
- db.run(
45
+ await dbRun(
47
46
  `INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
48
47
  VALUES (?, ?, ?, ?, ?, ?, ?)`,
49
48
  [spec.id, currentTask.id, options.category, options.content, severity, broadcastTo, now]
@@ -63,16 +62,14 @@ export function addKnowledge(options: AddKnowledgeOptions): void {
63
62
  console.log(`Conteudo: ${options.content}\n`);
64
63
  }
65
64
 
66
- export function listKnowledge(options: {
65
+ export async function listKnowledge(options: {
67
66
  unread?: boolean;
68
67
  category?: string;
69
68
  severity?: string; // v8.0: Filtro por severidade
70
69
  json?: boolean;
71
70
  specId?: string;
72
- }): void {
73
- initSchema();
74
-
75
- const db = getDb();
71
+ }): Promise<void> {
72
+ await initSchema();
76
73
 
77
74
  const spec = resolveSpec(options.specId);
78
75
 
@@ -96,19 +93,22 @@ export function listKnowledge(options: {
96
93
 
97
94
  query += " ORDER BY severity DESC, created_at DESC";
98
95
 
99
- const knowledge = db.query(query).all(...params) as any[];
96
+ const knowledge = await dbAll<any>(query, params);
100
97
 
101
98
  // Filtrar unread se solicitado
102
99
  let filtered = knowledge;
103
100
  if (options.unread) {
104
- const currentTask = getCurrentTask(spec.id);
101
+ const currentTask = await getCurrentTask(spec.id);
105
102
  if (currentTask) {
106
- filtered = knowledge.filter((k) => {
107
- const isAcked = db.query(
108
- "SELECT 1 FROM knowledge_acknowledgments WHERE knowledge_id = ? AND task_id = ?"
109
- ).get(k.id, currentTask.id);
110
- return !isAcked;
111
- });
103
+ const filteredResults: any[] = [];
104
+ for (const k of knowledge) {
105
+ const isAcked = await dbGet(
106
+ "SELECT 1 FROM knowledge_acknowledgments WHERE knowledge_id = ? AND task_id = ?",
107
+ [k.id, currentTask.id]
108
+ );
109
+ if (!isAcked) filteredResults.push(k);
110
+ }
111
+ filtered = filteredResults;
112
112
  }
113
113
  }
114
114
 
@@ -141,26 +141,24 @@ export function listKnowledge(options: {
141
141
  }
142
142
  }
143
143
 
144
- export function acknowledgeKnowledge(knowledgeId: string, specId?: string): void {
145
- initSchema();
146
-
147
- const db = getDb();
144
+ export async function acknowledgeKnowledge(knowledgeId: string, specId?: string): Promise<void> {
145
+ await initSchema();
148
146
 
149
147
  const spec = resolveSpec(specId);
150
148
 
151
- const currentTask = getCurrentTask(spec.id);
149
+ const currentTask = await getCurrentTask(spec.id);
152
150
  if (!currentTask) {
153
151
  throw new CodexaError("Nenhuma task em execucao.");
154
152
  }
155
153
 
156
154
  const kid = parseInt(knowledgeId);
157
- const knowledge = db.query("SELECT * FROM knowledge WHERE id = ?").get(kid) as any;
155
+ const knowledge = await dbGet<any>("SELECT * FROM knowledge WHERE id = ?", [kid]);
158
156
 
159
157
  if (!knowledge) {
160
158
  throw new CodexaError(`Knowledge #${kid} nao encontrado.`);
161
159
  }
162
160
 
163
- db.run(
161
+ await dbRun(
164
162
  "INSERT OR IGNORE INTO knowledge_acknowledgments (knowledge_id, task_id) VALUES (?, ?)",
165
163
  [kid, currentTask.id]
166
164
  );
@@ -168,19 +166,16 @@ export function acknowledgeKnowledge(knowledgeId: string, specId?: string): void
168
166
  console.log(`\nKnowledge #${kid} marcado como lido pela Task #${currentTask.number}.\n`);
169
167
  }
170
168
 
171
- export function getKnowledgeForTask(specId: string, taskId: number): any[] {
172
- const db = getDb();
173
-
169
+ export async function getKnowledgeForTask(specId: string, taskId: number): Promise<any[]> {
174
170
  // Knowledge do spec atual (todas as severities)
175
171
  // Fix: LIKE '%taskId%' causava substring match (taskId=1 matchava "1,10,11")
176
- const specKnowledge = db
177
- .query(
178
- `SELECT * FROM knowledge
179
- WHERE spec_id = ?
180
- ORDER BY severity DESC, created_at DESC
181
- LIMIT 50`
182
- )
183
- .all(specId) as any[];
172
+ const specKnowledge = await dbAll<any>(
173
+ `SELECT * FROM knowledge
174
+ WHERE spec_id = ?
175
+ ORDER BY severity DESC, created_at DESC
176
+ LIMIT 50`,
177
+ [specId]
178
+ );
184
179
 
185
180
  const filtered = specKnowledge.filter((k) => {
186
181
  if (k.broadcast_to === 'all') return true;
@@ -189,14 +184,13 @@ export function getKnowledgeForTask(specId: string, taskId: number): any[] {
189
184
  });
190
185
 
191
186
  // Cross-feature: knowledge critical de outros specs (aprendizado global)
192
- const crossFeature = db
193
- .query(
194
- `SELECT * FROM knowledge
195
- WHERE spec_id != ? AND severity = 'critical'
196
- ORDER BY created_at DESC
197
- LIMIT 10`
198
- )
199
- .all(specId) as any[];
187
+ const crossFeature = await dbAll<any>(
188
+ `SELECT * FROM knowledge
189
+ WHERE spec_id != ? AND severity = 'critical'
190
+ ORDER BY created_at DESC
191
+ LIMIT 10`,
192
+ [specId]
193
+ );
200
194
 
201
195
  // Deduplicar por content (priorizar o do spec atual)
202
196
  const seenContent = new Set(filtered.map((k) => k.content));
@@ -205,34 +199,35 @@ export function getKnowledgeForTask(specId: string, taskId: number): any[] {
205
199
  return [...filtered, ...uniqueCross].slice(0, 25);
206
200
  }
207
201
 
208
- export function getUnreadKnowledgeForTask(specId: string, taskId: number): any[] {
209
- const all = getKnowledgeForTask(specId, taskId);
210
- const db = getDb();
202
+ export async function getUnreadKnowledgeForTask(specId: string, taskId: number): Promise<any[]> {
203
+ const all = await getKnowledgeForTask(specId, taskId);
211
204
 
212
- return all.filter((k) => {
213
- const isAcked = db.query(
214
- "SELECT 1 FROM knowledge_acknowledgments WHERE knowledge_id = ? AND task_id = ?"
215
- ).get(k.id, taskId);
216
- return !isAcked;
217
- });
205
+ const results: any[] = [];
206
+ for (const k of all) {
207
+ const isAcked = await dbGet(
208
+ "SELECT 1 FROM knowledge_acknowledgments WHERE knowledge_id = ? AND task_id = ?",
209
+ [k.id, taskId]
210
+ );
211
+ if (!isAcked) results.push(k);
212
+ }
213
+ return results;
218
214
  }
219
215
 
220
216
  // v8.1: Consultar knowledge_graph - encontrar relacoes para arquivo, decisao ou pattern
221
- export function queryGraph(options: {
217
+ export async function queryGraph(options: {
222
218
  file?: string;
223
219
  decision?: string;
224
220
  json?: boolean;
225
221
  specId?: string;
226
- }): void {
227
- initSchema();
228
- const db = getDb();
222
+ }): Promise<void> {
223
+ await initSchema();
229
224
 
230
225
  const spec = resolveSpec(options.specId);
231
226
 
232
227
  // Buscar relacoes para um arquivo
233
228
  if (options.file) {
234
- const decisions = getRelatedDecisions(options.file, "file");
235
- const relatedFiles = db.query(`
229
+ const decisions = await getRelatedDecisions(options.file, "file");
230
+ const relatedFiles = await dbAll<any>(`
236
231
  SELECT DISTINCT kg.target_id as file, kg.relation
237
232
  FROM knowledge_graph kg
238
233
  WHERE kg.source_id = ? AND kg.target_type = 'file'
@@ -240,7 +235,7 @@ export function queryGraph(options: {
240
235
  SELECT DISTINCT kg.source_id as file, kg.relation
241
236
  FROM knowledge_graph kg
242
237
  WHERE kg.target_id = ? AND kg.source_type = 'file'
243
- `).all(options.file, options.file) as any[];
238
+ `, [options.file, options.file]);
244
239
 
245
240
  if (options.json) {
246
241
  console.log(JSON.stringify({ file: options.file, decisions, relatedFiles }));
@@ -273,7 +268,7 @@ export function queryGraph(options: {
273
268
 
274
269
  // Buscar arquivos afetados por uma decisao
275
270
  if (options.decision) {
276
- const files = getRelatedFiles(options.decision, "decision");
271
+ const files = await getRelatedFiles(options.decision, "decision");
277
272
 
278
273
  if (options.json) {
279
274
  console.log(JSON.stringify({ decision: options.decision, files }));
@@ -294,20 +289,22 @@ export function queryGraph(options: {
294
289
  }
295
290
 
296
291
  // Sem opcao: mostrar resumo do grafo
297
- const totalRelations = db.query(
298
- "SELECT COUNT(*) as c FROM knowledge_graph WHERE spec_id = ?"
299
- ).get(spec.id) as any;
292
+ const totalRelations = await dbGet<any>(
293
+ "SELECT COUNT(*) as c FROM knowledge_graph WHERE spec_id = ?",
294
+ [spec.id]
295
+ );
300
296
 
301
- const relationTypes = db.query(
302
- "SELECT relation, COUNT(*) as c FROM knowledge_graph WHERE spec_id = ? GROUP BY relation ORDER BY c DESC"
303
- ).all(spec.id) as any[];
297
+ const relationTypes = await dbAll<any>(
298
+ "SELECT relation, COUNT(*) as c FROM knowledge_graph WHERE spec_id = ? GROUP BY relation ORDER BY c DESC",
299
+ [spec.id]
300
+ );
304
301
 
305
302
  if (options.json) {
306
- console.log(JSON.stringify({ totalRelations: totalRelations.c, relationTypes }));
303
+ console.log(JSON.stringify({ totalRelations: totalRelations?.c, relationTypes }));
307
304
  return;
308
305
  }
309
306
 
310
- console.log(`\nKnowledge Graph: ${totalRelations.c} relacoes`);
307
+ console.log(`\nKnowledge Graph: ${totalRelations?.c} relacoes`);
311
308
  console.log(`${"─".repeat(50)}`);
312
309
  if (relationTypes.length > 0) {
313
310
  for (const rt of relationTypes) {
@@ -348,13 +345,12 @@ export function jaccardSimilarity(a: string, b: string): number {
348
345
  }
349
346
 
350
347
  // v9.2: Knowledge compaction — merge similar, archive old, archive completed
351
- export function compactKnowledge(options: {
348
+ export async function compactKnowledge(options: {
352
349
  specId?: string;
353
350
  dryRun?: boolean;
354
351
  json?: boolean;
355
- }): void {
356
- initSchema();
357
- const db = getDb();
352
+ }): Promise<void> {
353
+ await initSchema();
358
354
 
359
355
  let merged = 0;
360
356
  let archivedOld = 0;
@@ -366,11 +362,10 @@ export function compactKnowledge(options: {
366
362
  : "";
367
363
  const specParams = options.specId ? [options.specId] : [];
368
364
 
369
- const entries = db
370
- .query(
371
- `SELECT * FROM knowledge WHERE severity != 'archived' ${specFilter} ORDER BY category, spec_id, created_at DESC`
372
- )
373
- .all(...specParams) as any[];
365
+ const entries = await dbAll<any>(
366
+ `SELECT * FROM knowledge WHERE severity != 'archived' ${specFilter} ORDER BY category, spec_id, created_at DESC`,
367
+ specParams
368
+ );
374
369
 
375
370
  // Group by category + spec_id
376
371
  const groups = new Map<string, any[]>();
@@ -403,20 +398,19 @@ export function compactKnowledge(options: {
403
398
 
404
399
  // Phase 2: Archive old info entries (>7 days, no graph references)
405
400
  const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
406
- const oldInfoEntries = db
407
- .query(
408
- `SELECT k.id FROM knowledge k
409
- WHERE k.severity = 'info' AND k.created_at < ? ${specFilter}
410
- AND NOT EXISTS (
411
- SELECT 1 FROM knowledge_graph kg
412
- WHERE kg.source_id = CAST(k.id AS TEXT) AND kg.source_type = 'knowledge'
413
- )
414
- AND NOT EXISTS (
415
- SELECT 1 FROM knowledge_graph kg
416
- WHERE kg.target_id = CAST(k.id AS TEXT) AND kg.target_type = 'knowledge'
417
- )`
418
- )
419
- .all(sevenDaysAgo, ...specParams) as any[];
401
+ const oldInfoEntries = await dbAll<any>(
402
+ `SELECT k.id FROM knowledge k
403
+ WHERE k.severity = 'info' AND k.created_at < ? ${specFilter}
404
+ AND NOT EXISTS (
405
+ SELECT 1 FROM knowledge_graph kg
406
+ WHERE kg.source_id = CAST(k.id AS TEXT) AND kg.source_type = 'knowledge'
407
+ )
408
+ AND NOT EXISTS (
409
+ SELECT 1 FROM knowledge_graph kg
410
+ WHERE kg.target_id = CAST(k.id AS TEXT) AND kg.target_type = 'knowledge'
411
+ )`,
412
+ [sevenDaysAgo, ...specParams]
413
+ );
420
414
 
421
415
  for (const entry of oldInfoEntries) {
422
416
  if (!toArchive.has(entry.id)) {
@@ -426,15 +420,14 @@ export function compactKnowledge(options: {
426
420
  }
427
421
 
428
422
  // Phase 3: Archive all entries from completed/cancelled specs
429
- const completedEntries = db
430
- .query(
431
- `SELECT k.id FROM knowledge k
432
- JOIN specs s ON k.spec_id = s.id
433
- WHERE s.phase IN ('completed', 'cancelled')
434
- AND k.severity != 'archived'
435
- ${options.specId ? "AND k.spec_id = ?" : ""}`
436
- )
437
- .all(...specParams) as any[];
423
+ const completedEntries = await dbAll<any>(
424
+ `SELECT k.id FROM knowledge k
425
+ JOIN specs s ON k.spec_id = s.id
426
+ WHERE s.phase IN ('completed', 'cancelled')
427
+ AND k.severity != 'archived'
428
+ ${options.specId ? "AND k.spec_id = ?" : ""}`,
429
+ specParams
430
+ );
438
431
 
439
432
  for (const entry of completedEntries) {
440
433
  if (!toArchive.has(entry.id)) {
@@ -447,7 +440,7 @@ export function compactKnowledge(options: {
447
440
  if (!options.dryRun && toArchive.size > 0) {
448
441
  const ids = Array.from(toArchive);
449
442
  const placeholders = ids.map(() => "?").join(",");
450
- db.run(
443
+ await dbRun(
451
444
  `UPDATE knowledge SET severity = 'archived' WHERE id IN (${placeholders})`,
452
445
  ids
453
446
  );
@@ -481,9 +474,8 @@ export function compactKnowledge(options: {
481
474
  }
482
475
 
483
476
  // v9.0: Resolver/reconhecer knowledge critico (para desbloquear task start)
484
- export function resolveKnowledge(ids: string, resolution?: string, specId?: string): void {
485
- initSchema();
486
- const db = getDb();
477
+ export async function resolveKnowledge(ids: string, resolution?: string, specId?: string): Promise<void> {
478
+ await initSchema();
487
479
  const now = new Date().toISOString();
488
480
 
489
481
  const spec = resolveSpec(specId);
@@ -491,7 +483,7 @@ export function resolveKnowledge(ids: string, resolution?: string, specId?: stri
491
483
  const knowledgeIds = ids.split(",").map(s => parseInt(s.trim()));
492
484
 
493
485
  for (const kid of knowledgeIds) {
494
- const knowledge = db.query("SELECT * FROM knowledge WHERE id = ?").get(kid) as any;
486
+ const knowledge = await dbGet<any>("SELECT * FROM knowledge WHERE id = ?", [kid]);
495
487
 
496
488
  if (!knowledge) {
497
489
  console.error(`Knowledge #${kid} nao encontrado.`);
@@ -499,7 +491,7 @@ export function resolveKnowledge(ids: string, resolution?: string, specId?: stri
499
491
  }
500
492
 
501
493
  // Usar -1 como marker de "resolvido pelo orquestrador"
502
- db.run(
494
+ await dbRun(
503
495
  "INSERT OR IGNORE INTO knowledge_acknowledgments (knowledge_id, task_id) VALUES (?, ?)",
504
496
  [kid, -1]
505
497
  );
@@ -508,7 +500,7 @@ export function resolveKnowledge(ids: string, resolution?: string, specId?: stri
508
500
  }
509
501
 
510
502
  if (resolution) {
511
- db.run(
503
+ await dbRun(
512
504
  `INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
513
505
  VALUES (?, 0, 'decision', ?, 'info', 'all', ?)`,
514
506
  [spec.id, `Resolucao de blockers: ${resolution}`, now]
@@ -520,15 +512,14 @@ export function resolveKnowledge(ids: string, resolution?: string, specId?: stri
520
512
  }
521
513
 
522
514
  // v10.0: Compact reasoning_log during phase advance
523
- export function compactReasoning(options: {
515
+ export async function compactReasoning(options: {
524
516
  specId: string | number;
525
517
  dryRun?: boolean;
526
- }): { archived: number; kept: number } {
527
- const db = getDb();
528
-
529
- const doneTasks = db.query(
530
- "SELECT id FROM tasks WHERE spec_id = ? AND status = 'done'"
531
- ).all(options.specId) as any[];
518
+ }): Promise<{ archived: number; kept: number }> {
519
+ const doneTasks = await dbAll<any>(
520
+ "SELECT id FROM tasks WHERE spec_id = ? AND status = 'done'",
521
+ [options.specId]
522
+ );
532
523
 
533
524
  if (doneTasks.length === 0) return { archived: 0, kept: 0 };
534
525
 
@@ -536,23 +527,25 @@ export function compactReasoning(options: {
536
527
  const placeholders = doneTaskIds.map(() => "?").join(",");
537
528
 
538
529
  // Phase 1: Remove low-importance entries from completed tasks (keep recommendations always)
539
- const lowImportance = db.query(
530
+ const lowImportance = await dbAll<any>(
540
531
  `SELECT id FROM reasoning_log
541
532
  WHERE spec_id = ? AND task_id IN (${placeholders})
542
533
  AND importance IN ('normal', 'low')
543
- AND category != 'recommendation'`
544
- ).all(options.specId, ...doneTaskIds) as any[];
534
+ AND category != 'recommendation'`,
535
+ [options.specId, ...doneTaskIds]
536
+ );
545
537
 
546
538
  // Phase 2: Cap at 5 entries per task (keep most important)
547
539
  const overflow: number[] = [];
548
540
  for (const taskId of doneTaskIds) {
549
- const entries = db.query(
541
+ const entries = await dbAll<any>(
550
542
  `SELECT id FROM reasoning_log
551
543
  WHERE spec_id = ? AND task_id = ?
552
544
  ORDER BY
553
545
  CASE importance WHEN 'critical' THEN 1 WHEN 'high' THEN 2 WHEN 'normal' THEN 3 ELSE 4 END,
554
- created_at DESC`
555
- ).all(options.specId, taskId) as any[];
546
+ created_at DESC`,
547
+ [options.specId, taskId]
548
+ );
556
549
 
557
550
  if (entries.length > 5) {
558
551
  for (let i = 5; i < entries.length; i++) {
@@ -569,12 +562,14 @@ export function compactReasoning(options: {
569
562
  if (!options.dryRun && archiveIds.size > 0) {
570
563
  const ids = Array.from(archiveIds);
571
564
  const ph = ids.map(() => "?").join(",");
572
- db.run(`DELETE FROM reasoning_log WHERE id IN (${ph})`, ids);
565
+ await dbRun(`DELETE FROM reasoning_log WHERE id IN (${ph})`, ids);
573
566
  }
574
567
 
575
- const kept = (db.query(
576
- "SELECT COUNT(*) as c FROM reasoning_log WHERE spec_id = ?"
577
- ).get(options.specId) as any)?.c || 0;
568
+ const keptRow = await dbGet<any>(
569
+ "SELECT COUNT(*) as c FROM reasoning_log WHERE spec_id = ?",
570
+ [options.specId]
571
+ );
572
+ const kept = keptRow?.c || 0;
578
573
 
579
574
  return { archived: archiveIds.size, kept };
580
575
  }