@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/commands/architect.ts +52 -87
- package/commands/check.ts +22 -23
- package/commands/clear.ts +42 -48
- package/commands/decide.ts +49 -47
- 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 +304 -298
- package/db/schema.ts +302 -392
- 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/review.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dbGet, dbAll, dbRun } from "../db/connection";
|
|
2
2
|
import { initSchema, getArchitecturalAnalysisForSpec } from "../db/schema";
|
|
3
3
|
import { enforceGate } from "../gates/validator";
|
|
4
4
|
import { resolveSpec } from "./spec-resolver";
|
|
@@ -22,18 +22,22 @@ export interface ReviewScore {
|
|
|
22
22
|
mustReviewItems: string[]; // Items que exigem atencao humana
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
export function calculateReviewScore(specId: string): ReviewScore {
|
|
26
|
-
const db = getDb();
|
|
27
|
-
|
|
25
|
+
export async function calculateReviewScore(specId: string): Promise<ReviewScore> {
|
|
28
26
|
const mustReviewItems: string[] = [];
|
|
29
27
|
|
|
30
28
|
// 1. Tasks: (completed / total) * 25
|
|
31
|
-
const
|
|
32
|
-
"SELECT COUNT(*) as c FROM tasks WHERE spec_id = ?"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
const totalTasksRow = await dbGet<any>(
|
|
30
|
+
"SELECT COUNT(*) as c FROM tasks WHERE spec_id = ?",
|
|
31
|
+
[specId]
|
|
32
|
+
);
|
|
33
|
+
const totalTasks = totalTasksRow?.c ?? 0;
|
|
34
|
+
|
|
35
|
+
const completedTasksRow = await dbGet<any>(
|
|
36
|
+
"SELECT COUNT(*) as c FROM tasks WHERE spec_id = ? AND status = 'done'",
|
|
37
|
+
[specId]
|
|
38
|
+
);
|
|
39
|
+
const completedTasks = completedTasksRow?.c ?? 0;
|
|
40
|
+
|
|
37
41
|
const tasksCompleted = totalTasks > 0
|
|
38
42
|
? Math.round((completedTasks / totalTasks) * 25)
|
|
39
43
|
: 25;
|
|
@@ -45,26 +49,30 @@ export function calculateReviewScore(specId: string): ReviewScore {
|
|
|
45
49
|
// 2. Gates: (clean passes / total gate events) * 25
|
|
46
50
|
// Total gate events = tasks * 7 gates per task
|
|
47
51
|
const totalGateEvents = totalTasks * 7;
|
|
48
|
-
const
|
|
49
|
-
"SELECT COUNT(*) as c FROM gate_bypasses WHERE spec_id = ?"
|
|
50
|
-
|
|
52
|
+
const bypassCountRow = await dbGet<any>(
|
|
53
|
+
"SELECT COUNT(*) as c FROM gate_bypasses WHERE spec_id = ?",
|
|
54
|
+
[specId]
|
|
55
|
+
);
|
|
56
|
+
const bypassCount = bypassCountRow?.c ?? 0;
|
|
51
57
|
const cleanGateEvents = Math.max(0, totalGateEvents - bypassCount);
|
|
52
58
|
const gatesPassedClean = totalGateEvents > 0
|
|
53
59
|
? Math.round((cleanGateEvents / totalGateEvents) * 25)
|
|
54
60
|
: 25;
|
|
55
61
|
|
|
56
62
|
// Check for critical bypasses
|
|
57
|
-
const criticalBypasses =
|
|
58
|
-
"SELECT * FROM gate_bypasses WHERE spec_id = ? AND gate_name IN ('standards-follow', 'dry-check', 'typecheck-pass')"
|
|
59
|
-
|
|
63
|
+
const criticalBypasses = await dbAll<any>(
|
|
64
|
+
"SELECT * FROM gate_bypasses WHERE spec_id = ? AND gate_name IN ('standards-follow', 'dry-check', 'typecheck-pass')",
|
|
65
|
+
[specId]
|
|
66
|
+
);
|
|
60
67
|
if (criticalBypasses.length > 0) {
|
|
61
68
|
mustReviewItems.push(`${criticalBypasses.length} bypass(es) de gates criticos`);
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
// 3. Files: (delivered / planned) * 25
|
|
65
|
-
const plannedFiles =
|
|
66
|
-
"SELECT files FROM tasks WHERE spec_id = ? AND files IS NOT NULL"
|
|
67
|
-
|
|
72
|
+
const plannedFiles = await dbAll<any>(
|
|
73
|
+
"SELECT files FROM tasks WHERE spec_id = ? AND files IS NOT NULL",
|
|
74
|
+
[specId]
|
|
75
|
+
);
|
|
68
76
|
const allPlannedFiles = new Set<string>();
|
|
69
77
|
for (const t of plannedFiles) {
|
|
70
78
|
try {
|
|
@@ -73,11 +81,11 @@ export function calculateReviewScore(specId: string): ReviewScore {
|
|
|
73
81
|
} catch { /* ignore */ }
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
).all(specId) as any[]).map(a => a.path)
|
|
84
|
+
const deliveredArtifacts = await dbAll<any>(
|
|
85
|
+
"SELECT DISTINCT path FROM artifacts WHERE spec_id = ?",
|
|
86
|
+
[specId]
|
|
80
87
|
);
|
|
88
|
+
const deliveredFiles = new Set(deliveredArtifacts.map(a => a.path));
|
|
81
89
|
|
|
82
90
|
let filesDelivered: number;
|
|
83
91
|
if (allPlannedFiles.size === 0) {
|
|
@@ -97,9 +105,11 @@ export function calculateReviewScore(specId: string): ReviewScore {
|
|
|
97
105
|
|
|
98
106
|
// 4. Standards: (followed / total) * 25
|
|
99
107
|
// Inverse of standards-follow bypasses relative to total tasks
|
|
100
|
-
const
|
|
101
|
-
"SELECT COUNT(*) as c FROM gate_bypasses WHERE spec_id = ? AND gate_name = 'standards-follow'"
|
|
102
|
-
|
|
108
|
+
const standardsBypassesRow = await dbGet<any>(
|
|
109
|
+
"SELECT COUNT(*) as c FROM gate_bypasses WHERE spec_id = ? AND gate_name = 'standards-follow'",
|
|
110
|
+
[specId]
|
|
111
|
+
);
|
|
112
|
+
const standardsBypasses = standardsBypassesRow?.c ?? 0;
|
|
103
113
|
const standardsFollowed = totalTasks > 0
|
|
104
114
|
? Math.round(((totalTasks - standardsBypasses) / totalTasks) * 25)
|
|
105
115
|
: 25;
|
|
@@ -124,32 +134,35 @@ export function calculateReviewScore(specId: string): ReviewScore {
|
|
|
124
134
|
};
|
|
125
135
|
}
|
|
126
136
|
|
|
127
|
-
export function reviewStart(json: boolean = false, specId?: string): void {
|
|
128
|
-
initSchema();
|
|
137
|
+
export async function reviewStart(json: boolean = false, specId?: string): Promise<void> {
|
|
138
|
+
await initSchema();
|
|
129
139
|
enforceGate("review-start");
|
|
130
140
|
|
|
131
|
-
const db = getDb();
|
|
132
141
|
const now = new Date().toISOString();
|
|
133
142
|
|
|
134
|
-
const spec = resolveSpec(specId, ["implementing"]);
|
|
143
|
+
const spec = await resolveSpec(specId, ["implementing"]);
|
|
135
144
|
|
|
136
|
-
const tasks =
|
|
137
|
-
|
|
138
|
-
|
|
145
|
+
const tasks = await dbAll<any>(
|
|
146
|
+
"SELECT * FROM tasks WHERE spec_id = ? ORDER BY number",
|
|
147
|
+
[spec.id]
|
|
148
|
+
);
|
|
139
149
|
|
|
140
|
-
const artifacts =
|
|
141
|
-
|
|
142
|
-
|
|
150
|
+
const artifacts = await dbAll<any>(
|
|
151
|
+
"SELECT * FROM artifacts WHERE spec_id = ?",
|
|
152
|
+
[spec.id]
|
|
153
|
+
);
|
|
143
154
|
|
|
144
|
-
const decisions =
|
|
145
|
-
|
|
146
|
-
|
|
155
|
+
const decisions = await dbAll<any>(
|
|
156
|
+
"SELECT * FROM decisions WHERE spec_id = ? AND status = 'active'",
|
|
157
|
+
[spec.id]
|
|
158
|
+
);
|
|
147
159
|
|
|
148
160
|
// v8.4: Buscar analise arquitetural (link explicito via analysis_id ou nome)
|
|
149
|
-
const archAnalysis = getArchitecturalAnalysisForSpec(spec.name, spec.id);
|
|
150
|
-
const gateBypasses =
|
|
151
|
-
"SELECT * FROM gate_bypasses WHERE spec_id = ? ORDER BY created_at"
|
|
152
|
-
|
|
161
|
+
const archAnalysis = await getArchitecturalAnalysisForSpec(spec.name, spec.id);
|
|
162
|
+
const gateBypasses = await dbAll<any>(
|
|
163
|
+
"SELECT * FROM gate_bypasses WHERE spec_id = ? ORDER BY created_at",
|
|
164
|
+
[spec.id]
|
|
165
|
+
);
|
|
153
166
|
|
|
154
167
|
// Analisar plano vs implementado
|
|
155
168
|
const plannedFiles = tasks.flatMap((t) => (t.files ? JSON.parse(t.files) : []));
|
|
@@ -166,6 +179,15 @@ export function reviewStart(json: boolean = false, specId?: string): void {
|
|
|
166
179
|
deviations.push(`Arquivos extras criados: ${extraFiles.join(", ")}`);
|
|
167
180
|
}
|
|
168
181
|
|
|
182
|
+
// v8.5: Utilities criadas nesta feature (DRY audit)
|
|
183
|
+
let utilitiesCreated: any[] = [];
|
|
184
|
+
try {
|
|
185
|
+
utilitiesCreated = await dbAll<any>(
|
|
186
|
+
"SELECT * FROM project_utilities WHERE spec_id = ? ORDER BY file_path",
|
|
187
|
+
[spec.id]
|
|
188
|
+
);
|
|
189
|
+
} catch { utilitiesCreated = []; }
|
|
190
|
+
|
|
169
191
|
// Criar registro de review
|
|
170
192
|
const reviewData = {
|
|
171
193
|
tasks_completed: tasks.length,
|
|
@@ -183,17 +205,10 @@ export function reviewStart(json: boolean = false, specId?: string): void {
|
|
|
183
205
|
decisions: archAnalysis.decisions ? JSON.parse(archAnalysis.decisions) : [],
|
|
184
206
|
} : null,
|
|
185
207
|
gate_bypasses: gateBypasses,
|
|
186
|
-
|
|
187
|
-
utilities_created: (() => {
|
|
188
|
-
try {
|
|
189
|
-
return db.query(
|
|
190
|
-
"SELECT * FROM project_utilities WHERE spec_id = ? ORDER BY file_path"
|
|
191
|
-
).all(spec.id) as any[];
|
|
192
|
-
} catch { return []; }
|
|
193
|
-
})(),
|
|
208
|
+
utilities_created: utilitiesCreated,
|
|
194
209
|
};
|
|
195
210
|
|
|
196
|
-
|
|
211
|
+
await dbRun(
|
|
197
212
|
`INSERT INTO review (spec_id, planned_vs_done, deviations, status, created_at)
|
|
198
213
|
VALUES (?, ?, ?, 'pending', ?)`,
|
|
199
214
|
[
|
|
@@ -205,10 +220,10 @@ export function reviewStart(json: boolean = false, specId?: string): void {
|
|
|
205
220
|
);
|
|
206
221
|
|
|
207
222
|
// Atualizar fase
|
|
208
|
-
|
|
223
|
+
await dbRun("UPDATE specs SET phase = 'reviewing', updated_at = ? WHERE id = ?", [now, spec.id]);
|
|
209
224
|
|
|
210
225
|
// P1-2: Calcular score automatico
|
|
211
|
-
const score = calculateReviewScore(spec.id);
|
|
226
|
+
const score = await calculateReviewScore(spec.id);
|
|
212
227
|
|
|
213
228
|
if (json) {
|
|
214
229
|
console.log(JSON.stringify({ spec, reviewData, deviations, score }));
|
|
@@ -284,11 +299,10 @@ export function reviewStart(json: boolean = false, specId?: string): void {
|
|
|
284
299
|
console.log(`Para ver status: status\n`);
|
|
285
300
|
}
|
|
286
301
|
|
|
287
|
-
export function reviewApprove(options?: { specId?: string; force?: boolean; forceReason?: string } | string): void {
|
|
288
|
-
initSchema();
|
|
302
|
+
export async function reviewApprove(options?: { specId?: string; force?: boolean; forceReason?: string } | string): Promise<void> {
|
|
303
|
+
await initSchema();
|
|
289
304
|
enforceGate("review-approve");
|
|
290
305
|
|
|
291
|
-
const db = getDb();
|
|
292
306
|
const now = new Date().toISOString();
|
|
293
307
|
|
|
294
308
|
// Backward compat: accept string (old API) or options object
|
|
@@ -296,10 +310,10 @@ export function reviewApprove(options?: { specId?: string; force?: boolean; forc
|
|
|
296
310
|
? { specId: options }
|
|
297
311
|
: (options || {});
|
|
298
312
|
|
|
299
|
-
const spec = resolveSpec(opts.specId, ["reviewing"]);
|
|
313
|
+
const spec = await resolveSpec(opts.specId, ["reviewing"]);
|
|
300
314
|
|
|
301
315
|
// P1-2: Enforce minimum score
|
|
302
|
-
const score = calculateReviewScore(spec.id);
|
|
316
|
+
const score = await calculateReviewScore(spec.id);
|
|
303
317
|
if (score.total < 50 && !opts.force) {
|
|
304
318
|
throw new GateError(
|
|
305
319
|
`Review score muito baixo: ${score.total}/100.\n` +
|
|
@@ -312,7 +326,7 @@ export function reviewApprove(options?: { specId?: string; force?: boolean; forc
|
|
|
312
326
|
|
|
313
327
|
if (score.total < 50 && opts.force) {
|
|
314
328
|
// Log the forced approval
|
|
315
|
-
|
|
329
|
+
await dbRun(
|
|
316
330
|
`INSERT INTO gate_bypasses (spec_id, task_id, gate_name, reason, created_at)
|
|
317
331
|
VALUES (?, 0, 'review-low-score', ?, ?)`,
|
|
318
332
|
[spec.id, opts.forceReason || `Score ${score.total}/100 - forced approval`, now]
|
|
@@ -321,20 +335,20 @@ export function reviewApprove(options?: { specId?: string; force?: boolean; forc
|
|
|
321
335
|
}
|
|
322
336
|
|
|
323
337
|
// Atualizar review
|
|
324
|
-
|
|
338
|
+
await dbRun("UPDATE review SET status = 'passed' WHERE spec_id = ?", [spec.id]);
|
|
325
339
|
|
|
326
340
|
// Atualizar spec para completed
|
|
327
|
-
|
|
341
|
+
await dbRun("UPDATE specs SET phase = 'completed', updated_at = ? WHERE id = ?", [now, spec.id]);
|
|
328
342
|
|
|
329
343
|
|
|
330
344
|
|
|
331
345
|
// Buscar todos os dados para snapshot e relatorio
|
|
332
|
-
const tasks =
|
|
333
|
-
const decisions =
|
|
334
|
-
const artifacts =
|
|
335
|
-
const context =
|
|
336
|
-
const review =
|
|
337
|
-
const knowledge =
|
|
346
|
+
const tasks = await dbAll<any>("SELECT * FROM tasks WHERE spec_id = ? ORDER BY number", [spec.id]);
|
|
347
|
+
const decisions = await dbAll<any>("SELECT * FROM decisions WHERE spec_id = ?", [spec.id]);
|
|
348
|
+
const artifacts = await dbAll<any>("SELECT * FROM artifacts WHERE spec_id = ?", [spec.id]);
|
|
349
|
+
const context = await dbGet<any>("SELECT * FROM context WHERE spec_id = ?", [spec.id]);
|
|
350
|
+
const review = await dbGet<any>("SELECT * FROM review WHERE spec_id = ?", [spec.id]);
|
|
351
|
+
const knowledge = await dbAll<any>("SELECT * FROM knowledge WHERE spec_id = ?", [spec.id]);
|
|
338
352
|
|
|
339
353
|
// Criar snapshot final
|
|
340
354
|
const allData = {
|
|
@@ -347,7 +361,7 @@ export function reviewApprove(options?: { specId?: string; force?: boolean; forc
|
|
|
347
361
|
knowledge,
|
|
348
362
|
};
|
|
349
363
|
|
|
350
|
-
|
|
364
|
+
await dbRun("INSERT INTO snapshots (spec_id, data, trigger, created_at) VALUES (?, ?, 'final', ?)", [
|
|
351
365
|
spec.id,
|
|
352
366
|
JSON.stringify(allData),
|
|
353
367
|
now,
|
|
@@ -360,44 +374,44 @@ export function reviewApprove(options?: { specId?: string; force?: boolean; forc
|
|
|
360
374
|
/**
|
|
361
375
|
* Pula o review e finaliza a feature diretamente
|
|
362
376
|
*/
|
|
363
|
-
export function reviewSkip(specId?: string): void {
|
|
364
|
-
initSchema();
|
|
377
|
+
export async function reviewSkip(specId?: string): Promise<void> {
|
|
378
|
+
await initSchema();
|
|
365
379
|
|
|
366
|
-
const db = getDb();
|
|
367
380
|
const now = new Date().toISOString();
|
|
368
381
|
|
|
369
|
-
const spec = resolveSpec(specId, ["implementing"]);
|
|
382
|
+
const spec = await resolveSpec(specId, ["implementing"]);
|
|
370
383
|
|
|
371
384
|
// Verificar se todas tasks estao done
|
|
372
|
-
const pending =
|
|
373
|
-
|
|
374
|
-
|
|
385
|
+
const pending = await dbGet<any>(
|
|
386
|
+
"SELECT COUNT(*) as c FROM tasks WHERE spec_id = ? AND status != 'done'",
|
|
387
|
+
[spec.id]
|
|
388
|
+
);
|
|
375
389
|
|
|
376
|
-
if (pending.c > 0) {
|
|
390
|
+
if (pending && pending.c > 0) {
|
|
377
391
|
throw new CodexaError(`Ainda existem ${pending.c} tasks pendentes.\nComplete todas as tasks antes de pular o review.`);
|
|
378
392
|
}
|
|
379
393
|
|
|
380
394
|
// Buscar dados
|
|
381
|
-
const tasks =
|
|
382
|
-
const decisions =
|
|
383
|
-
const artifacts =
|
|
384
|
-
const context =
|
|
385
|
-
const knowledge =
|
|
395
|
+
const tasks = await dbAll<any>("SELECT * FROM tasks WHERE spec_id = ? ORDER BY number", [spec.id]);
|
|
396
|
+
const decisions = await dbAll<any>("SELECT * FROM decisions WHERE spec_id = ?", [spec.id]);
|
|
397
|
+
const artifacts = await dbAll<any>("SELECT * FROM artifacts WHERE spec_id = ?", [spec.id]);
|
|
398
|
+
const context = await dbGet<any>("SELECT * FROM context WHERE spec_id = ?", [spec.id]);
|
|
399
|
+
const knowledge = await dbAll<any>("SELECT * FROM knowledge WHERE spec_id = ?", [spec.id]);
|
|
386
400
|
|
|
387
401
|
// Criar registro de review como skipped
|
|
388
|
-
|
|
402
|
+
await dbRun(
|
|
389
403
|
`INSERT INTO review (spec_id, planned_vs_done, deviations, status, created_at)
|
|
390
404
|
VALUES (?, ?, NULL, 'skipped', ?)`,
|
|
391
405
|
[spec.id, JSON.stringify({ skipped: true, reason: "User chose to skip review" }), now]
|
|
392
406
|
);
|
|
393
407
|
|
|
394
408
|
// Atualizar spec para completed
|
|
395
|
-
|
|
409
|
+
await dbRun("UPDATE specs SET phase = 'completed', updated_at = ? WHERE id = ?", [now, spec.id]);
|
|
396
410
|
|
|
397
411
|
|
|
398
412
|
|
|
399
413
|
// Criar snapshot final
|
|
400
|
-
const review =
|
|
414
|
+
const review = await dbGet<any>("SELECT * FROM review WHERE spec_id = ?", [spec.id]);
|
|
401
415
|
const allData = {
|
|
402
416
|
spec,
|
|
403
417
|
context,
|
|
@@ -408,7 +422,7 @@ export function reviewSkip(specId?: string): void {
|
|
|
408
422
|
knowledge,
|
|
409
423
|
};
|
|
410
424
|
|
|
411
|
-
|
|
425
|
+
await dbRun("INSERT INTO snapshots (spec_id, data, trigger, created_at) VALUES (?, ?, 'final', ?)", [
|
|
412
426
|
spec.id,
|
|
413
427
|
JSON.stringify(allData),
|
|
414
428
|
now,
|
package/commands/simplify.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// Used post-task (Option A) and pre-review (Option C).
|
|
5
5
|
// ═══════════════════════════════════════════════════════════════
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { dbGet, dbAll, dbRun } from "../db/connection";
|
|
8
8
|
import { initSchema } from "../db/schema";
|
|
9
9
|
import { resolveSpec } from "./spec-resolver";
|
|
10
10
|
import { getAgentDomain, domainToScope } from "../context/domains";
|
|
@@ -20,15 +20,14 @@ import {
|
|
|
20
20
|
// SIMPLIFY FOR TASK (Option A)
|
|
21
21
|
// ═══════════════════════════════════════════════════════════════
|
|
22
22
|
|
|
23
|
-
export function simplifyTask(
|
|
23
|
+
export async function simplifyTask(
|
|
24
24
|
taskId: string,
|
|
25
25
|
options: { audit?: boolean; specId?: string } = {}
|
|
26
|
-
): void {
|
|
27
|
-
initSchema();
|
|
26
|
+
): Promise<void> {
|
|
27
|
+
await initSchema();
|
|
28
28
|
|
|
29
|
-
const db = getDb();
|
|
30
29
|
const id = parseInt(taskId);
|
|
31
|
-
const task =
|
|
30
|
+
const task = await dbGet<any>("SELECT * FROM tasks WHERE id = ?", [id]);
|
|
32
31
|
|
|
33
32
|
if (!task) {
|
|
34
33
|
throw new CodexaError(`Task #${id} nao encontrada.`);
|
|
@@ -40,12 +39,10 @@ export function simplifyTask(
|
|
|
40
39
|
);
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
.
|
|
46
|
-
|
|
47
|
-
)
|
|
48
|
-
.all(task.spec_id, task.number) as any[];
|
|
42
|
+
const artifacts = await dbAll<any>(
|
|
43
|
+
"SELECT path, action FROM artifacts WHERE spec_id = ? AND task_ref = ?",
|
|
44
|
+
[task.spec_id, task.number]
|
|
45
|
+
);
|
|
49
46
|
|
|
50
47
|
if (artifacts.length === 0) {
|
|
51
48
|
console.log(`\nTask #${task.number} nao tem arquivos registrados. Nada para simplificar.\n`);
|
|
@@ -57,18 +54,14 @@ export function simplifyTask(
|
|
|
57
54
|
action: a.action === "created" ? "created" as const : "modified" as const,
|
|
58
55
|
}));
|
|
59
56
|
|
|
60
|
-
// Determine agent domain for scope filtering
|
|
61
57
|
const domain = getAgentDomain(task.agent);
|
|
62
58
|
const scope = domainToScope(domain);
|
|
63
59
|
const mode: SimplifyMode = options.audit ? "audit" : "refactor";
|
|
64
60
|
|
|
65
|
-
// Build prompt with standards
|
|
66
61
|
const prompt = buildSimplifyPrompt(files, scope, mode);
|
|
67
62
|
|
|
68
|
-
// Write context file
|
|
69
63
|
const contextPath = writeSimplifyContext(`task-${task.number}`, prompt);
|
|
70
64
|
|
|
71
|
-
// Output
|
|
72
65
|
const modeLabel = mode === "audit" ? "AUDITORIA" : "REFATORACAO";
|
|
73
66
|
console.log(`\n${"=".repeat(55)}`);
|
|
74
67
|
console.log(`SIMPLIFY (${modeLabel}) — Task #${task.number}`);
|
|
@@ -86,16 +79,15 @@ export function simplifyTask(
|
|
|
86
79
|
// SIMPLIFY AUDIT FOR REVIEW (Option C)
|
|
87
80
|
// ═══════════════════════════════════════════════════════════════
|
|
88
81
|
|
|
89
|
-
export function simplifyAudit(specId?: string): string | null {
|
|
90
|
-
initSchema();
|
|
82
|
+
export async function simplifyAudit(specId?: string): Promise<string | null> {
|
|
83
|
+
await initSchema();
|
|
91
84
|
|
|
92
|
-
const
|
|
93
|
-
const spec = resolveSpec(specId, ["implementing", "reviewing"]);
|
|
85
|
+
const spec = await resolveSpec(specId, ["implementing", "reviewing"]);
|
|
94
86
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
.
|
|
98
|
-
|
|
87
|
+
const artifacts = await dbAll<any>(
|
|
88
|
+
"SELECT DISTINCT path, action FROM artifacts WHERE spec_id = ?",
|
|
89
|
+
[spec.id]
|
|
90
|
+
);
|
|
99
91
|
|
|
100
92
|
if (artifacts.length === 0) {
|
|
101
93
|
return null;
|
|
@@ -106,10 +98,8 @@ export function simplifyAudit(specId?: string): string | null {
|
|
|
106
98
|
action: a.action === "created" ? "created" as const : "modified" as const,
|
|
107
99
|
}));
|
|
108
100
|
|
|
109
|
-
// Use "all" scope for review (covers all domains)
|
|
110
101
|
const prompt = buildSimplifyPrompt(files, "all", "audit");
|
|
111
102
|
|
|
112
|
-
// Write context file
|
|
113
103
|
const contextPath = writeSimplifyContext(`audit-${spec.id}`, prompt);
|
|
114
104
|
|
|
115
105
|
return contextPath;
|
|
@@ -119,22 +109,21 @@ export function simplifyAudit(specId?: string): string | null {
|
|
|
119
109
|
// AUTO-SIMPLIFY CHECK
|
|
120
110
|
// ═══════════════════════════════════════════════════════════════
|
|
121
111
|
|
|
122
|
-
export function isAutoSimplifyEnabled(): boolean {
|
|
112
|
+
export async function isAutoSimplifyEnabled(): Promise<boolean> {
|
|
123
113
|
try {
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
114
|
+
const project = await dbGet<any>(
|
|
115
|
+
"SELECT auto_simplify FROM project WHERE id = 'default'",
|
|
116
|
+
[]
|
|
117
|
+
);
|
|
128
118
|
return project?.auto_simplify === 1;
|
|
129
119
|
} catch {
|
|
130
120
|
return false;
|
|
131
121
|
}
|
|
132
122
|
}
|
|
133
123
|
|
|
134
|
-
export function setAutoSimplify(enabled: boolean): void {
|
|
135
|
-
initSchema();
|
|
136
|
-
|
|
137
|
-
db.run(
|
|
124
|
+
export async function setAutoSimplify(enabled: boolean): Promise<void> {
|
|
125
|
+
await initSchema();
|
|
126
|
+
await dbRun(
|
|
138
127
|
"UPDATE project SET auto_simplify = ?, updated_at = ? WHERE id = 'default'",
|
|
139
128
|
[enabled ? 1 : 0, new Date().toISOString()]
|
|
140
129
|
);
|
|
@@ -1,106 +1,110 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from "bun:test";
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
2
2
|
import { resolveSpec, getAllActiveSpecs } from "./spec-resolver";
|
|
3
|
-
import {
|
|
3
|
+
import { createClient } from "@libsql/client";
|
|
4
|
+
import { setClient, resetClient, dbRun } from "../db/connection";
|
|
4
5
|
import { initSchema } from "../db/schema";
|
|
5
6
|
import { cleanDb } from "../db/test-helpers";
|
|
6
7
|
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
beforeEach(async () => {
|
|
9
|
+
const client = createClient({ url: ":memory:" });
|
|
10
|
+
setClient(client);
|
|
11
|
+
await initSchema();
|
|
12
|
+
await cleanDb();
|
|
10
13
|
});
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
afterEach(() => {
|
|
16
|
+
resetClient();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
async function createSpec(id: string, name: string, phase: string, createdAt?: string) {
|
|
14
20
|
const now = createdAt || new Date().toISOString();
|
|
15
|
-
|
|
21
|
+
await dbRun(
|
|
16
22
|
"INSERT INTO specs (id, name, phase, created_at, updated_at) VALUES (?, ?, ?, ?, ?)",
|
|
17
23
|
[id, name, phase, now, now]
|
|
18
24
|
);
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
describe("resolveSpec", () => {
|
|
22
|
-
it("returns spec by ID when specId provided", () => {
|
|
23
|
-
createSpec("spec-a", "Feature A", "planning");
|
|
24
|
-
createSpec("spec-b", "Feature B", "implementing");
|
|
28
|
+
it("returns spec by ID when specId provided", async () => {
|
|
29
|
+
await createSpec("spec-a", "Feature A", "planning");
|
|
30
|
+
await createSpec("spec-b", "Feature B", "implementing");
|
|
25
31
|
|
|
26
|
-
const result = resolveSpec("spec-a");
|
|
32
|
+
const result = await resolveSpec("spec-a");
|
|
27
33
|
expect(result.id).toBe("spec-a");
|
|
28
34
|
expect(result.name).toBe("Feature A");
|
|
29
35
|
});
|
|
30
36
|
|
|
31
|
-
it("returns most recent active spec when no specId", () => {
|
|
32
|
-
createSpec("spec-old", "Old Feature", "planning", "2026-01-01T00:00:00Z");
|
|
33
|
-
createSpec("spec-new", "New Feature", "implementing", "2026-02-01T00:00:00Z");
|
|
37
|
+
it("returns most recent active spec when no specId", async () => {
|
|
38
|
+
await createSpec("spec-old", "Old Feature", "planning", "2026-01-01T00:00:00Z");
|
|
39
|
+
await createSpec("spec-new", "New Feature", "implementing", "2026-02-01T00:00:00Z");
|
|
34
40
|
|
|
35
|
-
const result = resolveSpec();
|
|
41
|
+
const result = await resolveSpec();
|
|
36
42
|
expect(result.id).toBe("spec-new");
|
|
37
43
|
});
|
|
38
44
|
|
|
39
|
-
it("validates requiredPhases", () => {
|
|
40
|
-
createSpec("spec-a", "Feature A", "planning");
|
|
45
|
+
it("validates requiredPhases", async () => {
|
|
46
|
+
await createSpec("spec-a", "Feature A", "planning");
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
const result = resolveSpec("spec-a", ["planning"]);
|
|
48
|
+
const result = await resolveSpec("spec-a", ["planning"]);
|
|
44
49
|
expect(result.phase).toBe("planning");
|
|
45
50
|
});
|
|
46
51
|
|
|
47
|
-
it("accepts multiple requiredPhases", () => {
|
|
48
|
-
createSpec("spec-a", "Feature A", "checking");
|
|
49
|
-
const result = resolveSpec("spec-a", ["planning", "checking"]);
|
|
52
|
+
it("accepts multiple requiredPhases", async () => {
|
|
53
|
+
await createSpec("spec-a", "Feature A", "checking");
|
|
54
|
+
const result = await resolveSpec("spec-a", ["planning", "checking"]);
|
|
50
55
|
expect(result.phase).toBe("checking");
|
|
51
56
|
});
|
|
52
57
|
|
|
53
|
-
it("skips completed specs when no specId", () => {
|
|
54
|
-
createSpec("spec-done", "Done Feature", "completed", "2026-02-01T00:00:00Z");
|
|
55
|
-
createSpec("spec-active", "Active Feature", "planning", "2026-01-01T00:00:00Z");
|
|
58
|
+
it("skips completed specs when no specId", async () => {
|
|
59
|
+
await createSpec("spec-done", "Done Feature", "completed", "2026-02-01T00:00:00Z");
|
|
60
|
+
await createSpec("spec-active", "Active Feature", "planning", "2026-01-01T00:00:00Z");
|
|
56
61
|
|
|
57
|
-
const result = resolveSpec();
|
|
62
|
+
const result = await resolveSpec();
|
|
58
63
|
expect(result.id).toBe("spec-active");
|
|
59
64
|
});
|
|
60
65
|
|
|
61
|
-
it("skips cancelled specs when no specId", () => {
|
|
62
|
-
createSpec("spec-cancelled", "Cancelled Feature", "cancelled", "2026-02-01T00:00:00Z");
|
|
63
|
-
createSpec("spec-active", "Active Feature", "implementing", "2026-01-01T00:00:00Z");
|
|
66
|
+
it("skips cancelled specs when no specId", async () => {
|
|
67
|
+
await createSpec("spec-cancelled", "Cancelled Feature", "cancelled", "2026-02-01T00:00:00Z");
|
|
68
|
+
await createSpec("spec-active", "Active Feature", "implementing", "2026-01-01T00:00:00Z");
|
|
64
69
|
|
|
65
|
-
const result = resolveSpec();
|
|
70
|
+
const result = await resolveSpec();
|
|
66
71
|
expect(result.id).toBe("spec-active");
|
|
67
72
|
});
|
|
68
73
|
});
|
|
69
74
|
|
|
70
75
|
describe("getAllActiveSpecs", () => {
|
|
71
|
-
it("returns empty array when no active specs", () => {
|
|
72
|
-
const result = getAllActiveSpecs();
|
|
76
|
+
it("returns empty array when no active specs", async () => {
|
|
77
|
+
const result = await getAllActiveSpecs();
|
|
73
78
|
expect(result).toEqual([]);
|
|
74
79
|
});
|
|
75
80
|
|
|
76
|
-
it("returns all active specs ordered by created_at DESC", () => {
|
|
77
|
-
createSpec("spec-old", "Old Feature", "planning", "2026-01-01T00:00:00Z");
|
|
78
|
-
createSpec("spec-new", "New Feature", "implementing", "2026-02-01T00:00:00Z");
|
|
81
|
+
it("returns all active specs ordered by created_at DESC", async () => {
|
|
82
|
+
await createSpec("spec-old", "Old Feature", "planning", "2026-01-01T00:00:00Z");
|
|
83
|
+
await createSpec("spec-new", "New Feature", "implementing", "2026-02-01T00:00:00Z");
|
|
79
84
|
|
|
80
|
-
const result = getAllActiveSpecs();
|
|
85
|
+
const result = await getAllActiveSpecs();
|
|
81
86
|
expect(result.length).toBe(2);
|
|
82
87
|
expect(result[0].id).toBe("spec-new");
|
|
83
88
|
expect(result[1].id).toBe("spec-old");
|
|
84
89
|
});
|
|
85
90
|
|
|
86
|
-
it("excludes completed and cancelled specs", () => {
|
|
87
|
-
createSpec("spec-active", "Active", "planning");
|
|
88
|
-
createSpec("spec-done", "Done", "completed");
|
|
89
|
-
createSpec("spec-cancelled", "Cancelled", "cancelled");
|
|
91
|
+
it("excludes completed and cancelled specs", async () => {
|
|
92
|
+
await createSpec("spec-active", "Active", "planning");
|
|
93
|
+
await createSpec("spec-done", "Done", "completed");
|
|
94
|
+
await createSpec("spec-cancelled", "Cancelled", "cancelled");
|
|
90
95
|
|
|
91
|
-
const result = getAllActiveSpecs();
|
|
96
|
+
const result = await getAllActiveSpecs();
|
|
92
97
|
expect(result.length).toBe(1);
|
|
93
98
|
expect(result[0].id).toBe("spec-active");
|
|
94
99
|
});
|
|
95
100
|
|
|
96
|
-
it("returns multiple specs in different phases", () => {
|
|
97
|
-
createSpec("spec-plan", "Planning Feature", "planning", "2026-01-01T00:00:00Z");
|
|
98
|
-
createSpec("spec-impl", "Implementing Feature", "implementing", "2026-01-02T00:00:00Z");
|
|
99
|
-
createSpec("spec-rev", "Reviewing Feature", "reviewing", "2026-01-03T00:00:00Z");
|
|
101
|
+
it("returns multiple specs in different phases", async () => {
|
|
102
|
+
await createSpec("spec-plan", "Planning Feature", "planning", "2026-01-01T00:00:00Z");
|
|
103
|
+
await createSpec("spec-impl", "Implementing Feature", "implementing", "2026-01-02T00:00:00Z");
|
|
104
|
+
await createSpec("spec-rev", "Reviewing Feature", "reviewing", "2026-01-03T00:00:00Z");
|
|
100
105
|
|
|
101
|
-
const result = getAllActiveSpecs();
|
|
106
|
+
const result = await getAllActiveSpecs();
|
|
102
107
|
expect(result.length).toBe(3);
|
|
103
|
-
// Ordered by created_at DESC
|
|
104
108
|
expect(result[0].id).toBe("spec-rev");
|
|
105
109
|
expect(result[1].id).toBe("spec-impl");
|
|
106
110
|
expect(result[2].id).toBe("spec-plan");
|