@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
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - Patterns descobertos
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import {
|
|
12
|
+
import { dbGet, dbAll, dbRun } from "../db/connection";
|
|
13
13
|
import { SubagentReturn, Knowledge } from "./subagent-protocol";
|
|
14
14
|
import { addReasoning, addGraphRelation, upsertUtility, getNextDecisionId, runInTransaction } from "../db/schema";
|
|
15
15
|
import { extractUtilitiesFromFile, inferScopeFromPath } from "../commands/patterns";
|
|
@@ -22,9 +22,9 @@ interface ProcessResult {
|
|
|
22
22
|
conflictsDetected: number;
|
|
23
23
|
artifactsAdded: number;
|
|
24
24
|
patternsAdded: number;
|
|
25
|
-
reasoningAdded: number;
|
|
26
|
-
relationsAdded: number;
|
|
27
|
-
utilitiesRegistered: number;
|
|
25
|
+
reasoningAdded: number;
|
|
26
|
+
relationsAdded: number;
|
|
27
|
+
utilitiesRegistered: number;
|
|
28
28
|
errors: string[];
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -32,24 +32,21 @@ interface ProcessResult {
|
|
|
32
32
|
* Processa o retorno de um subagent e registra automaticamente
|
|
33
33
|
* todos os dados extraidos no banco
|
|
34
34
|
*/
|
|
35
|
-
export function processSubagentReturn(
|
|
35
|
+
export async function processSubagentReturn(
|
|
36
36
|
specId: string,
|
|
37
37
|
taskId: number,
|
|
38
38
|
taskNumber: number,
|
|
39
39
|
data: SubagentReturn
|
|
40
|
-
): ProcessResult {
|
|
41
|
-
|
|
42
|
-
// Prevents race conditions in parallel tasks (pattern storage, knowledge dedup)
|
|
43
|
-
return runInTransaction(() => processSubagentReturnInner(specId, taskId, taskNumber, data));
|
|
40
|
+
): Promise<ProcessResult> {
|
|
41
|
+
return await runInTransaction(async (tx) => await processSubagentReturnInner(specId, taskId, taskNumber, data));
|
|
44
42
|
}
|
|
45
43
|
|
|
46
|
-
function processSubagentReturnInner(
|
|
44
|
+
async function processSubagentReturnInner(
|
|
47
45
|
specId: string,
|
|
48
46
|
taskId: number,
|
|
49
47
|
taskNumber: number,
|
|
50
48
|
data: SubagentReturn
|
|
51
|
-
): ProcessResult {
|
|
52
|
-
const db = getDb();
|
|
49
|
+
): Promise<ProcessResult> {
|
|
53
50
|
const now = new Date().toISOString();
|
|
54
51
|
const result: ProcessResult = {
|
|
55
52
|
success: true,
|
|
@@ -68,14 +65,14 @@ function processSubagentReturnInner(
|
|
|
68
65
|
if (data.knowledge_to_broadcast && data.knowledge_to_broadcast.length > 0) {
|
|
69
66
|
for (const k of data.knowledge_to_broadcast) {
|
|
70
67
|
try {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
)
|
|
68
|
+
const existing = await dbGet<any>(
|
|
69
|
+
`SELECT id FROM knowledge WHERE spec_id = ? AND category = ? AND content = ? LIMIT 1`,
|
|
70
|
+
[specId, k.category, k.content]
|
|
71
|
+
);
|
|
75
72
|
|
|
76
|
-
if (existing) continue;
|
|
73
|
+
if (existing) continue;
|
|
77
74
|
|
|
78
|
-
|
|
75
|
+
await dbRun(
|
|
79
76
|
`INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
|
|
80
77
|
VALUES (?, ?, ?, ?, ?, 'all', ?)`,
|
|
81
78
|
[specId, taskId, k.category, k.content, k.severity, now]
|
|
@@ -96,7 +93,7 @@ function processSubagentReturnInner(
|
|
|
96
93
|
while (retries > 0 && !inserted) {
|
|
97
94
|
try {
|
|
98
95
|
const decisionId = getNextDecisionId(specId);
|
|
99
|
-
|
|
96
|
+
await dbRun(
|
|
100
97
|
`INSERT INTO decisions (id, spec_id, task_ref, title, decision, rationale, status, created_at)
|
|
101
98
|
VALUES (?, ?, ?, ?, ?, ?, 'active', ?)`,
|
|
102
99
|
[decisionId, specId, taskNumber, dec.title, dec.decision, dec.rationale || null, now]
|
|
@@ -119,9 +116,10 @@ function processSubagentReturnInner(
|
|
|
119
116
|
// 2b. v9.5: Conflict check (WARNING, nao bloqueia subagent)
|
|
120
117
|
if (savedDecisionIds.length > 0) {
|
|
121
118
|
try {
|
|
122
|
-
const existingDecisions =
|
|
123
|
-
|
|
124
|
-
|
|
119
|
+
const existingDecisions = await dbAll<any>(
|
|
120
|
+
"SELECT * FROM decisions WHERE spec_id = ? AND status = 'active'",
|
|
121
|
+
[specId]
|
|
122
|
+
);
|
|
125
123
|
|
|
126
124
|
for (const dec of data.decisions_made!) {
|
|
127
125
|
const othersOnly = existingDecisions.filter(
|
|
@@ -132,7 +130,7 @@ function processSubagentReturnInner(
|
|
|
132
130
|
const analysis = detectConflicts(dec.title, dec.decision, othersOnly);
|
|
133
131
|
if (analysis.hasConflict) {
|
|
134
132
|
for (const conflict of analysis.conflictingDecisions) {
|
|
135
|
-
|
|
133
|
+
await dbRun(
|
|
136
134
|
`INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
|
|
137
135
|
VALUES (?, ?, 'decision', ?, 'warning', 'all', ?)`,
|
|
138
136
|
[specId, taskId,
|
|
@@ -157,7 +155,7 @@ function processSubagentReturnInner(
|
|
|
157
155
|
|
|
158
156
|
for (const file of allFiles) {
|
|
159
157
|
try {
|
|
160
|
-
|
|
158
|
+
await dbRun(
|
|
161
159
|
`INSERT OR REPLACE INTO artifacts (spec_id, task_ref, path, action, created_at)
|
|
162
160
|
VALUES (?, ?, ?, ?, ?)`,
|
|
163
161
|
[specId, taskNumber, file.path, file.action, now]
|
|
@@ -171,11 +169,10 @@ function processSubagentReturnInner(
|
|
|
171
169
|
// 4. Registrar Patterns descobertos no contexto
|
|
172
170
|
if (data.patterns_discovered && data.patterns_discovered.length > 0) {
|
|
173
171
|
try {
|
|
174
|
-
const context =
|
|
172
|
+
const context = await dbGet<any>("SELECT * FROM context WHERE spec_id = ?", [specId]);
|
|
175
173
|
const currentPatterns = context?.patterns ? JSON.parse(context.patterns) : [];
|
|
176
174
|
|
|
177
175
|
for (const pattern of data.patterns_discovered) {
|
|
178
|
-
// v10.0: Dedup — skip if pattern text already exists
|
|
179
176
|
if (currentPatterns.some((p: any) => p.pattern === pattern)) {
|
|
180
177
|
continue;
|
|
181
178
|
}
|
|
@@ -187,12 +184,11 @@ function processSubagentReturnInner(
|
|
|
187
184
|
result.patternsAdded++;
|
|
188
185
|
}
|
|
189
186
|
|
|
190
|
-
// v10.0: Cap at 50 patterns (keep most recent)
|
|
191
187
|
if (currentPatterns.length > 50) {
|
|
192
188
|
currentPatterns.splice(0, currentPatterns.length - 50);
|
|
193
189
|
}
|
|
194
190
|
|
|
195
|
-
|
|
191
|
+
await dbRun(
|
|
196
192
|
"UPDATE context SET patterns = ?, updated_at = ? WHERE spec_id = ?",
|
|
197
193
|
[JSON.stringify(currentPatterns), now, specId]
|
|
198
194
|
);
|
|
@@ -206,12 +202,13 @@ function processSubagentReturnInner(
|
|
|
206
202
|
for (const pattern of data.patterns_discovered) {
|
|
207
203
|
try {
|
|
208
204
|
const content = `Pattern descoberto: ${pattern}`;
|
|
209
|
-
const existing =
|
|
210
|
-
`SELECT id FROM knowledge WHERE spec_id = ? AND category = 'pattern' AND content = ? LIMIT 1
|
|
211
|
-
|
|
205
|
+
const existing = await dbGet<any>(
|
|
206
|
+
`SELECT id FROM knowledge WHERE spec_id = ? AND category = 'pattern' AND content = ? LIMIT 1`,
|
|
207
|
+
[specId, content]
|
|
208
|
+
);
|
|
212
209
|
|
|
213
210
|
if (!existing) {
|
|
214
|
-
|
|
211
|
+
await dbRun(
|
|
215
212
|
`INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
|
|
216
213
|
VALUES (?, ?, 'pattern', ?, 'info', 'all', ?)`,
|
|
217
214
|
[specId, taskId, content, now]
|
|
@@ -225,7 +222,7 @@ function processSubagentReturnInner(
|
|
|
225
222
|
if (data.status === "blocked" && data.blockers) {
|
|
226
223
|
for (const blocker of data.blockers) {
|
|
227
224
|
try {
|
|
228
|
-
|
|
225
|
+
await dbRun(
|
|
229
226
|
`INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
|
|
230
227
|
VALUES (?, ?, 'blocker', ?, 'critical', 'all', ?)`,
|
|
231
228
|
[specId, taskId, blocker, now]
|
|
@@ -240,9 +237,8 @@ function processSubagentReturnInner(
|
|
|
240
237
|
// 6. v8.0: Registrar Reasoning no reasoning_log
|
|
241
238
|
if (data.reasoning) {
|
|
242
239
|
try {
|
|
243
|
-
// Abordagem principal
|
|
244
240
|
if (data.reasoning.approach) {
|
|
245
|
-
addReasoning(specId, taskId, {
|
|
241
|
+
await addReasoning(specId, taskId, {
|
|
246
242
|
category: "decision",
|
|
247
243
|
thought: `Abordagem: ${data.reasoning.approach}`,
|
|
248
244
|
relatedFiles: [...data.files_created, ...data.files_modified],
|
|
@@ -251,10 +247,9 @@ function processSubagentReturnInner(
|
|
|
251
247
|
result.reasoningAdded++;
|
|
252
248
|
}
|
|
253
249
|
|
|
254
|
-
// Desafios encontrados
|
|
255
250
|
if (data.reasoning.challenges && data.reasoning.challenges.length > 0) {
|
|
256
251
|
for (const challenge of data.reasoning.challenges) {
|
|
257
|
-
addReasoning(specId, taskId, {
|
|
252
|
+
await addReasoning(specId, taskId, {
|
|
258
253
|
category: "challenge",
|
|
259
254
|
thought: challenge,
|
|
260
255
|
relatedFiles: [...data.files_created, ...data.files_modified],
|
|
@@ -264,10 +259,9 @@ function processSubagentReturnInner(
|
|
|
264
259
|
}
|
|
265
260
|
}
|
|
266
261
|
|
|
267
|
-
// Alternativas consideradas
|
|
268
262
|
if (data.reasoning.alternatives && data.reasoning.alternatives.length > 0) {
|
|
269
263
|
for (const alt of data.reasoning.alternatives) {
|
|
270
|
-
addReasoning(specId, taskId, {
|
|
264
|
+
await addReasoning(specId, taskId, {
|
|
271
265
|
category: "decision",
|
|
272
266
|
thought: `Alternativa considerada: ${alt}`,
|
|
273
267
|
relatedFiles: [...data.files_created, ...data.files_modified],
|
|
@@ -277,9 +271,8 @@ function processSubagentReturnInner(
|
|
|
277
271
|
}
|
|
278
272
|
}
|
|
279
273
|
|
|
280
|
-
// Recomendações para próximas tasks
|
|
281
274
|
if (data.reasoning.recommendations) {
|
|
282
|
-
addReasoning(specId, taskId, {
|
|
275
|
+
await addReasoning(specId, taskId, {
|
|
283
276
|
category: "recommendation",
|
|
284
277
|
thought: data.reasoning.recommendations,
|
|
285
278
|
relatedFiles: [...data.files_created, ...data.files_modified],
|
|
@@ -294,9 +287,8 @@ function processSubagentReturnInner(
|
|
|
294
287
|
|
|
295
288
|
// 7. v8.0: Criar relações no knowledge graph
|
|
296
289
|
try {
|
|
297
|
-
// Relação: task -> arquivos criados
|
|
298
290
|
for (const file of data.files_created) {
|
|
299
|
-
addGraphRelation(specId, {
|
|
291
|
+
await addGraphRelation(specId, {
|
|
300
292
|
sourceType: "task",
|
|
301
293
|
sourceId: String(taskId),
|
|
302
294
|
targetType: "file",
|
|
@@ -306,9 +298,8 @@ function processSubagentReturnInner(
|
|
|
306
298
|
result.relationsAdded++;
|
|
307
299
|
}
|
|
308
300
|
|
|
309
|
-
// Relação: task -> arquivos modificados
|
|
310
301
|
for (const file of data.files_modified) {
|
|
311
|
-
addGraphRelation(specId, {
|
|
302
|
+
await addGraphRelation(specId, {
|
|
312
303
|
sourceType: "task",
|
|
313
304
|
sourceId: String(taskId),
|
|
314
305
|
targetType: "file",
|
|
@@ -318,11 +309,10 @@ function processSubagentReturnInner(
|
|
|
318
309
|
result.relationsAdded++;
|
|
319
310
|
}
|
|
320
311
|
|
|
321
|
-
// Relação: decision -> arquivos (usa IDs salvos na seção 2, não gera novos)
|
|
322
312
|
if (savedDecisionIds.length > 0) {
|
|
323
313
|
for (const decisionId of savedDecisionIds) {
|
|
324
314
|
for (const file of [...data.files_created, ...data.files_modified]) {
|
|
325
|
-
addGraphRelation(specId, {
|
|
315
|
+
await addGraphRelation(specId, {
|
|
326
316
|
sourceType: "decision",
|
|
327
317
|
sourceId: decisionId,
|
|
328
318
|
targetType: "file",
|
|
@@ -334,11 +324,10 @@ function processSubagentReturnInner(
|
|
|
334
324
|
}
|
|
335
325
|
}
|
|
336
326
|
|
|
337
|
-
// Relação: pattern -> arquivos (se patterns descobertos)
|
|
338
327
|
if (data.patterns_discovered) {
|
|
339
328
|
for (const pattern of data.patterns_discovered) {
|
|
340
329
|
for (const file of data.files_created) {
|
|
341
|
-
addGraphRelation(specId, {
|
|
330
|
+
await addGraphRelation(specId, {
|
|
342
331
|
sourceType: "pattern",
|
|
343
332
|
sourceId: pattern,
|
|
344
333
|
targetType: "file",
|
|
@@ -355,10 +344,9 @@ function processSubagentReturnInner(
|
|
|
355
344
|
|
|
356
345
|
// 8. v8.5: Registrar utilities de arquivos criados/modificados (DRY map)
|
|
357
346
|
try {
|
|
358
|
-
// 8a. Usar utilities declaradas pelo subagent (metadata mais rica)
|
|
359
347
|
if (data.utilities_created && data.utilities_created.length > 0) {
|
|
360
348
|
for (const util of data.utilities_created) {
|
|
361
|
-
upsertUtility({
|
|
349
|
+
await upsertUtility({
|
|
362
350
|
filePath: util.file,
|
|
363
351
|
utilityName: util.name,
|
|
364
352
|
utilityType: util.type || "function",
|
|
@@ -370,12 +358,11 @@ function processSubagentReturnInner(
|
|
|
370
358
|
}
|
|
371
359
|
}
|
|
372
360
|
|
|
373
|
-
// 8b. Fallback: escanear exports de arquivos criados/modificados
|
|
374
361
|
for (const file of allFiles.map(f => f.path)) {
|
|
375
362
|
const utilities = extractUtilitiesFromFile(file);
|
|
376
363
|
const scope = inferScopeFromPath(file);
|
|
377
364
|
for (const util of utilities) {
|
|
378
|
-
upsertUtility({
|
|
365
|
+
await upsertUtility({
|
|
379
366
|
filePath: file,
|
|
380
367
|
utilityName: util.name,
|
|
381
368
|
utilityType: util.type,
|
|
@@ -386,7 +373,6 @@ function processSubagentReturnInner(
|
|
|
386
373
|
}
|
|
387
374
|
}
|
|
388
375
|
} catch (e) {
|
|
389
|
-
// Nao-critico: nao falhar processamento por extracao de utilities
|
|
390
376
|
result.errors.push(`Aviso utilities: ${(e as Error).message}`);
|
|
391
377
|
}
|
|
392
378
|
|
|
@@ -415,14 +401,12 @@ export function formatProcessResult(result: ProcessResult): string {
|
|
|
415
401
|
if (result.patternsAdded > 0) {
|
|
416
402
|
output += ` + ${result.patternsAdded} pattern(s) adicionado(s) ao contexto\n`;
|
|
417
403
|
}
|
|
418
|
-
// v8.0: Novas métricas
|
|
419
404
|
if (result.reasoningAdded > 0) {
|
|
420
405
|
output += ` + ${result.reasoningAdded} raciocinio(s) registrado(s) no historico\n`;
|
|
421
406
|
}
|
|
422
407
|
if (result.relationsAdded > 0) {
|
|
423
408
|
output += ` + ${result.relationsAdded} relacao(oes) criada(s) no grafo de conhecimento\n`;
|
|
424
409
|
}
|
|
425
|
-
// v8.5: Utilities registradas
|
|
426
410
|
if (result.utilitiesRegistered > 0) {
|
|
427
411
|
output += ` + ${result.utilitiesRegistered} utilidade(s) registrada(s) no mapa DRY\n`;
|
|
428
412
|
}
|
|
@@ -435,4 +419,4 @@ export function formatProcessResult(result: ProcessResult): string {
|
|
|
435
419
|
}
|
|
436
420
|
|
|
437
421
|
return output;
|
|
438
|
-
}
|
|
422
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach } from "bun:test";
|
|
2
|
-
import {
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
|
2
|
+
import { createClient } from "@libsql/client";
|
|
3
|
+
import { setClient, resetClient, dbRun } from "../db/connection";
|
|
3
4
|
import { initSchema, runMigrations } from "../db/schema";
|
|
4
5
|
import {
|
|
5
6
|
buildSimplifyPrompt,
|
|
@@ -29,7 +30,7 @@ function createTmpFile(name: string, content: string): string {
|
|
|
29
30
|
return path;
|
|
30
31
|
}
|
|
31
32
|
|
|
32
|
-
function insertStandard(opts: {
|
|
33
|
+
async function insertStandard(opts: {
|
|
33
34
|
category: string;
|
|
34
35
|
scope: string;
|
|
35
36
|
rule: string;
|
|
@@ -38,8 +39,7 @@ function insertStandard(opts: {
|
|
|
38
39
|
anti_examples?: string;
|
|
39
40
|
semantic_query?: string | null;
|
|
40
41
|
}) {
|
|
41
|
-
|
|
42
|
-
db.run(
|
|
42
|
+
await dbRun(
|
|
43
43
|
`INSERT INTO standards (category, scope, rule, examples, anti_examples, enforcement, semantic_query, expect, source, created_at)
|
|
44
44
|
VALUES (?, ?, ?, ?, ?, ?, ?, 'no_match', 'test', datetime('now'))`,
|
|
45
45
|
[
|
|
@@ -59,51 +59,55 @@ function insertStandard(opts: {
|
|
|
59
59
|
// ═══════════════════════════════════════════════════════════════
|
|
60
60
|
|
|
61
61
|
describe("simplify prompt-builder", () => {
|
|
62
|
-
beforeEach(() => {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
beforeEach(async () => {
|
|
63
|
+
const client = createClient({ url: ":memory:" });
|
|
64
|
+
setClient(client);
|
|
65
|
+
await initSchema();
|
|
66
|
+
await runMigrations();
|
|
67
|
+
await dbRun("DELETE FROM standards");
|
|
68
68
|
if (existsSync(TMP_DIR)) {
|
|
69
69
|
rmSync(TMP_DIR, { recursive: true, force: true });
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
72
|
|
|
73
|
+
afterEach(() => {
|
|
74
|
+
resetClient();
|
|
75
|
+
});
|
|
76
|
+
|
|
73
77
|
// ── getStandardsForSimplify ─────────────────────────────────
|
|
74
78
|
|
|
75
79
|
describe("getStandardsForSimplify", () => {
|
|
76
|
-
it("returns standards filtered by scope", () => {
|
|
77
|
-
insertStandard({ category: "code", scope: "all", rule: "Global rule" });
|
|
78
|
-
insertStandard({ category: "code", scope: "backend", rule: "Backend rule" });
|
|
79
|
-
insertStandard({ category: "code", scope: "frontend", rule: "Frontend rule" });
|
|
80
|
+
it("returns standards filtered by scope", async () => {
|
|
81
|
+
await insertStandard({ category: "code", scope: "all", rule: "Global rule" });
|
|
82
|
+
await insertStandard({ category: "code", scope: "backend", rule: "Backend rule" });
|
|
83
|
+
await insertStandard({ category: "code", scope: "frontend", rule: "Frontend rule" });
|
|
80
84
|
|
|
81
|
-
const backendStds = getStandardsForSimplify("backend");
|
|
85
|
+
const backendStds = await getStandardsForSimplify("backend");
|
|
82
86
|
expect(backendStds.length).toBe(2);
|
|
83
87
|
expect(backendStds.map(s => s.rule)).toContain("Global rule");
|
|
84
88
|
expect(backendStds.map(s => s.rule)).toContain("Backend rule");
|
|
85
89
|
expect(backendStds.map(s => s.rule)).not.toContain("Frontend rule");
|
|
86
90
|
});
|
|
87
91
|
|
|
88
|
-
it("returns all standards when scope is 'all'", () => {
|
|
89
|
-
insertStandard({ category: "code", scope: "all", rule: "Global rule" });
|
|
90
|
-
insertStandard({ category: "code", scope: "backend", rule: "Backend rule" });
|
|
92
|
+
it("returns all standards when scope is 'all'", async () => {
|
|
93
|
+
await insertStandard({ category: "code", scope: "all", rule: "Global rule" });
|
|
94
|
+
await insertStandard({ category: "code", scope: "backend", rule: "Backend rule" });
|
|
91
95
|
|
|
92
|
-
const allStds = getStandardsForSimplify("all");
|
|
96
|
+
const allStds = await getStandardsForSimplify("all");
|
|
93
97
|
expect(allStds.length).toBe(1);
|
|
94
98
|
expect(allStds[0].rule).toBe("Global rule");
|
|
95
99
|
});
|
|
96
100
|
|
|
97
|
-
it("returns empty array when no standards exist", () => {
|
|
98
|
-
const stds = getStandardsForSimplify("backend");
|
|
101
|
+
it("returns empty array when no standards exist", async () => {
|
|
102
|
+
const stds = await getStandardsForSimplify("backend");
|
|
99
103
|
expect(stds.length).toBe(0);
|
|
100
104
|
});
|
|
101
105
|
|
|
102
|
-
it("orders required before recommended", () => {
|
|
103
|
-
insertStandard({ category: "code", scope: "all", rule: "Recommended", enforcement: "recommended" });
|
|
104
|
-
insertStandard({ category: "code", scope: "all", rule: "Required", enforcement: "required" });
|
|
106
|
+
it("orders required before recommended", async () => {
|
|
107
|
+
await insertStandard({ category: "code", scope: "all", rule: "Recommended", enforcement: "recommended" });
|
|
108
|
+
await insertStandard({ category: "code", scope: "all", rule: "Required", enforcement: "required" });
|
|
105
109
|
|
|
106
|
-
const stds = getStandardsForSimplify("all");
|
|
110
|
+
const stds = await getStandardsForSimplify("all");
|
|
107
111
|
expect(stds[0].rule).toBe("Required");
|
|
108
112
|
expect(stds[1].rule).toBe("Recommended");
|
|
109
113
|
});
|
|
@@ -179,8 +183,8 @@ describe("simplify prompt-builder", () => {
|
|
|
179
183
|
// ── buildSimplifyPrompt ─────────────────────────────────────
|
|
180
184
|
|
|
181
185
|
describe("buildSimplifyPrompt", () => {
|
|
182
|
-
it("builds refactor prompt with standards and files", () => {
|
|
183
|
-
insertStandard({
|
|
186
|
+
it("builds refactor prompt with standards and files", async () => {
|
|
187
|
+
await insertStandard({
|
|
184
188
|
category: "code",
|
|
185
189
|
scope: "all",
|
|
186
190
|
rule: "Use function declarations",
|
|
@@ -191,7 +195,7 @@ describe("simplify prompt-builder", () => {
|
|
|
191
195
|
const filePath = createTmpFile("test.ts", "export function hello() {}");
|
|
192
196
|
const files: SimplifyFile[] = [{ path: filePath, action: "created" }];
|
|
193
197
|
|
|
194
|
-
const prompt = buildSimplifyPrompt(files, "all", "refactor");
|
|
198
|
+
const prompt = await buildSimplifyPrompt(files, "all", "refactor");
|
|
195
199
|
|
|
196
200
|
expect(prompt).toContain("## Standards do Projeto");
|
|
197
201
|
expect(prompt).toContain("Use function declarations");
|
|
@@ -201,53 +205,51 @@ describe("simplify prompt-builder", () => {
|
|
|
201
205
|
expect(prompt).toContain("Preservem funcionalidade");
|
|
202
206
|
});
|
|
203
207
|
|
|
204
|
-
it("builds audit prompt with analysis-only instructions", () => {
|
|
205
|
-
insertStandard({ category: "code", scope: "all", rule: "Some rule" });
|
|
208
|
+
it("builds audit prompt with analysis-only instructions", async () => {
|
|
209
|
+
await insertStandard({ category: "code", scope: "all", rule: "Some rule" });
|
|
206
210
|
|
|
207
211
|
const filePath = createTmpFile("test2.ts", "export const x = 1;");
|
|
208
212
|
const files: SimplifyFile[] = [{ path: filePath, action: "modified" }];
|
|
209
213
|
|
|
210
|
-
const prompt = buildSimplifyPrompt(files, "all", "audit");
|
|
214
|
+
const prompt = await buildSimplifyPrompt(files, "all", "audit");
|
|
211
215
|
|
|
212
216
|
expect(prompt).toContain("NAO aplique mudancas");
|
|
213
217
|
expect(prompt).toContain("reporte oportunidades");
|
|
214
218
|
expect(prompt).toContain("(modificado)");
|
|
215
219
|
});
|
|
216
220
|
|
|
217
|
-
it("handles no standards gracefully", () => {
|
|
221
|
+
it("handles no standards gracefully", async () => {
|
|
218
222
|
const filePath = createTmpFile("test3.ts", "export const y = 2;");
|
|
219
223
|
const files: SimplifyFile[] = [{ path: filePath, action: "created" }];
|
|
220
224
|
|
|
221
|
-
const prompt = buildSimplifyPrompt(files, "all", "refactor");
|
|
225
|
+
const prompt = await buildSimplifyPrompt(files, "all", "refactor");
|
|
222
226
|
|
|
223
|
-
// Should still have files and instructions sections
|
|
224
227
|
expect(prompt).toContain("## Arquivos para Simplificar");
|
|
225
228
|
expect(prompt).toContain("## Instrucoes");
|
|
226
|
-
// Should NOT have standards section
|
|
227
229
|
expect(prompt).not.toContain("## Standards do Projeto");
|
|
228
230
|
});
|
|
229
231
|
|
|
230
|
-
it("filters out non-existing files", () => {
|
|
232
|
+
it("filters out non-existing files", async () => {
|
|
231
233
|
const filePath = createTmpFile("exists.ts", "export const z = 3;");
|
|
232
234
|
const files: SimplifyFile[] = [
|
|
233
235
|
{ path: filePath, action: "created" },
|
|
234
236
|
{ path: "/nonexistent/file.ts", action: "modified" },
|
|
235
237
|
];
|
|
236
238
|
|
|
237
|
-
const prompt = buildSimplifyPrompt(files, "all", "refactor");
|
|
239
|
+
const prompt = await buildSimplifyPrompt(files, "all", "refactor");
|
|
238
240
|
|
|
239
241
|
expect(prompt).toContain("exists.ts");
|
|
240
242
|
expect(prompt).not.toContain("nonexistent");
|
|
241
243
|
});
|
|
242
244
|
|
|
243
|
-
it("filters standards by scope", () => {
|
|
244
|
-
insertStandard({ category: "code", scope: "backend", rule: "Backend only rule" });
|
|
245
|
-
insertStandard({ category: "code", scope: "frontend", rule: "Frontend only rule" });
|
|
245
|
+
it("filters standards by scope", async () => {
|
|
246
|
+
await insertStandard({ category: "code", scope: "backend", rule: "Backend only rule" });
|
|
247
|
+
await insertStandard({ category: "code", scope: "frontend", rule: "Frontend only rule" });
|
|
246
248
|
|
|
247
249
|
const filePath = createTmpFile("test4.ts", "export const a = 1;");
|
|
248
250
|
const files: SimplifyFile[] = [{ path: filePath, action: "created" }];
|
|
249
251
|
|
|
250
|
-
const prompt = buildSimplifyPrompt(files, "backend", "refactor");
|
|
252
|
+
const prompt = await buildSimplifyPrompt(files, "backend", "refactor");
|
|
251
253
|
|
|
252
254
|
expect(prompt).toContain("Backend only rule");
|
|
253
255
|
expect(prompt).not.toContain("Frontend only rule");
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// from SQLite. Generates context files for the simplifier agent.
|
|
5
5
|
// ═══════════════════════════════════════════════════════════════
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { dbAll } from "../db/connection";
|
|
8
8
|
import { existsSync, mkdirSync } from "fs";
|
|
9
9
|
import { resolve, join } from "path";
|
|
10
10
|
|
|
@@ -42,16 +42,14 @@ export type SimplifyMode = "refactor" | "audit";
|
|
|
42
42
|
// STANDARDS RETRIEVAL
|
|
43
43
|
// ═══════════════════════════════════════════════════════════════
|
|
44
44
|
|
|
45
|
-
export function getStandardsForSimplify(agentDomain: string): StandardRow[] {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
)
|
|
54
|
-
.all(agentDomain) as StandardRow[];
|
|
45
|
+
export async function getStandardsForSimplify(agentDomain: string): Promise<StandardRow[]> {
|
|
46
|
+
return dbAll<StandardRow>(
|
|
47
|
+
`SELECT id, category, scope, rule, examples, anti_examples, enforcement, semantic_query
|
|
48
|
+
FROM standards
|
|
49
|
+
WHERE (scope = 'all' OR scope = ?)
|
|
50
|
+
ORDER BY enforcement DESC, category`,
|
|
51
|
+
[agentDomain]
|
|
52
|
+
);
|
|
55
53
|
}
|
|
56
54
|
|
|
57
55
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -124,12 +122,12 @@ function parseJsonArray(json: string | null): string[] {
|
|
|
124
122
|
// PROMPT BUILDER
|
|
125
123
|
// ═══════════════════════════════════════════════════════════════
|
|
126
124
|
|
|
127
|
-
export function buildSimplifyPrompt(
|
|
125
|
+
export async function buildSimplifyPrompt(
|
|
128
126
|
files: SimplifyFile[],
|
|
129
127
|
agentDomain: string,
|
|
130
128
|
mode: SimplifyMode
|
|
131
|
-
): string {
|
|
132
|
-
const standards = getStandardsForSimplify(agentDomain);
|
|
129
|
+
): Promise<string> {
|
|
130
|
+
const standards = await getStandardsForSimplify(agentDomain);
|
|
133
131
|
const existingFiles = files.filter(f => existsSync(f.path));
|
|
134
132
|
|
|
135
133
|
let prompt = "";
|