@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/commands/architect.ts +52 -87
- package/commands/check.ts +22 -23
- package/commands/clear.ts +42 -48
- package/commands/decide.ts +46 -44
- package/commands/discover.ts +81 -94
- package/commands/integration.test.ts +262 -313
- package/commands/knowledge.test.ts +56 -61
- package/commands/knowledge.ts +126 -131
- package/commands/patterns.ts +28 -43
- package/commands/plan.ts +50 -48
- package/commands/product.ts +57 -59
- package/commands/research.ts +64 -77
- package/commands/review.ts +100 -86
- package/commands/simplify.ts +24 -35
- package/commands/spec-resolver.test.ts +52 -48
- package/commands/spec-resolver.ts +21 -23
- package/commands/standards.ts +20 -27
- package/commands/sync.ts +2 -8
- package/commands/task.ts +106 -97
- package/commands/team.test.ts +22 -83
- package/commands/team.ts +62 -50
- package/commands/utils.ts +83 -81
- package/context/assembly.ts +0 -1
- package/context/generator.ts +66 -79
- package/context/sections.ts +8 -14
- package/db/connection.ts +195 -19
- package/db/schema.test.ts +288 -299
- package/db/schema.ts +297 -394
- package/db/test-helpers.ts +18 -29
- package/gates/standards-validator.test.ts +83 -86
- package/gates/standards-validator.ts +9 -41
- package/gates/validator.test.ts +13 -22
- package/gates/validator.ts +69 -107
- package/package.json +2 -1
- package/protocol/process-return.ts +41 -57
- package/simplify/prompt-builder.test.ts +44 -42
- package/simplify/prompt-builder.ts +12 -14
- package/workflow.ts +159 -174
package/commands/knowledge.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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 =
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
298
|
-
"SELECT COUNT(*) as c FROM knowledge_graph WHERE spec_id = ?"
|
|
299
|
-
|
|
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 =
|
|
302
|
-
"SELECT relation, COUNT(*) as c FROM knowledge_graph WHERE spec_id = ? GROUP BY relation ORDER BY c DESC"
|
|
303
|
-
|
|
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
|
|
303
|
+
console.log(JSON.stringify({ totalRelations: totalRelations?.c, relationTypes }));
|
|
307
304
|
return;
|
|
308
305
|
}
|
|
309
306
|
|
|
310
|
-
console.log(`\nKnowledge Graph: ${totalRelations
|
|
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 =
|
|
370
|
-
|
|
371
|
-
|
|
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 =
|
|
407
|
-
.
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
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 =
|
|
430
|
-
.
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
565
|
+
await dbRun(`DELETE FROM reasoning_log WHERE id IN (${ph})`, ids);
|
|
573
566
|
}
|
|
574
567
|
|
|
575
|
-
const
|
|
576
|
-
"SELECT COUNT(*) as c FROM reasoning_log WHERE spec_id = ?"
|
|
577
|
-
|
|
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
|
}
|