@codexa/cli 9.0.7 → 9.0.9
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/decide.ts +120 -3
- package/commands/discover.ts +18 -9
- package/commands/integration.test.ts +754 -0
- package/commands/knowledge.test.ts +2 -6
- package/commands/knowledge.ts +20 -4
- package/commands/patterns.ts +8 -644
- package/commands/product.ts +41 -104
- package/commands/spec-resolver.test.ts +2 -13
- package/commands/standards.ts +33 -3
- package/commands/task.ts +21 -4
- package/commands/utils.test.ts +25 -87
- package/commands/utils.ts +20 -82
- package/context/assembly.ts +11 -12
- package/context/domains.test.ts +300 -0
- package/context/domains.ts +157 -0
- package/context/generator.ts +14 -13
- package/context/index.ts +6 -1
- package/context/references.test.ts +159 -0
- package/context/references.ts +159 -0
- package/context/sections.ts +18 -1
- package/db/schema.ts +40 -5
- package/db/test-helpers.ts +33 -0
- package/gates/standards-validator.test.ts +447 -0
- package/gates/standards-validator.ts +164 -125
- package/gates/typecheck-validator.ts +296 -92
- package/gates/validator.ts +93 -8
- package/package.json +1 -1
- package/protocol/process-return.ts +39 -4
- package/workflow.ts +54 -84
package/commands/decide.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { initSchema, getNextDecisionId } from "../db/schema";
|
|
|
3
3
|
import { resolveSpec } from "./spec-resolver";
|
|
4
4
|
import { CodexaError } from "../errors";
|
|
5
5
|
|
|
6
|
-
interface ConflictAnalysis {
|
|
6
|
+
export interface ConflictAnalysis {
|
|
7
7
|
hasConflict: boolean;
|
|
8
8
|
conflictingDecisions: Array<{
|
|
9
9
|
id: string;
|
|
@@ -14,7 +14,7 @@ interface ConflictAnalysis {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
// Extrair keywords relevantes de um texto
|
|
17
|
-
function extractKeywords(text: string): string[] {
|
|
17
|
+
export function extractKeywords(text: string): string[] {
|
|
18
18
|
const stopWords = new Set([
|
|
19
19
|
"a", "o", "e", "de", "da", "do", "para", "com", "em", "que", "usar",
|
|
20
20
|
"the", "and", "or", "to", "for", "with", "in", "on", "is", "are", "be",
|
|
@@ -29,7 +29,7 @@ function extractKeywords(text: string): string[] {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// Detectar conflitos semanticos entre decisoes
|
|
32
|
-
function detectConflicts(
|
|
32
|
+
export function detectConflicts(
|
|
33
33
|
newTitle: string,
|
|
34
34
|
newDecision: string,
|
|
35
35
|
existingDecisions: any[]
|
|
@@ -235,3 +235,120 @@ export function listDecisions(json: boolean = false, specId?: string): void {
|
|
|
235
235
|
console.log();
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
|
+
|
|
239
|
+
export function revokeDecision(decisionId: string, reason?: string, specId?: string): void {
|
|
240
|
+
initSchema();
|
|
241
|
+
const db = getDb();
|
|
242
|
+
const spec = resolveSpec(specId);
|
|
243
|
+
|
|
244
|
+
const decision = db
|
|
245
|
+
.query("SELECT * FROM decisions WHERE id = ? AND spec_id = ?")
|
|
246
|
+
.get(decisionId, spec.id) as any;
|
|
247
|
+
|
|
248
|
+
if (!decision) {
|
|
249
|
+
throw new CodexaError(`Decisao '${decisionId}' nao encontrada no spec '${spec.id}'.`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (decision.status !== "active") {
|
|
253
|
+
throw new CodexaError(`Decisao '${decisionId}' ja esta com status '${decision.status}'. Apenas decisoes ativas podem ser revogadas.`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const now = new Date().toISOString();
|
|
257
|
+
|
|
258
|
+
db.run("UPDATE decisions SET status = 'revoked' WHERE id = ?", [decisionId]);
|
|
259
|
+
|
|
260
|
+
// Registrar como knowledge para subagentes saberem da revogacao
|
|
261
|
+
const currentTask = db
|
|
262
|
+
.query("SELECT * FROM tasks WHERE spec_id = ? AND status = 'running' LIMIT 1")
|
|
263
|
+
.get(spec.id) as any;
|
|
264
|
+
|
|
265
|
+
db.run(
|
|
266
|
+
`INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
|
|
267
|
+
VALUES (?, ?, 'decision', ?, 'warning', 'all', ?)`,
|
|
268
|
+
[
|
|
269
|
+
spec.id,
|
|
270
|
+
currentTask?.id || 0,
|
|
271
|
+
`Decisao REVOGADA: "${decision.title}" - ${decision.decision}${reason ? ` (Motivo: ${reason})` : ""}`,
|
|
272
|
+
now,
|
|
273
|
+
]
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
console.log(`\nDecisao revogada: ${decisionId}`);
|
|
277
|
+
console.log(`Titulo: ${decision.title}`);
|
|
278
|
+
if (reason) console.log(`Motivo: ${reason}`);
|
|
279
|
+
console.log();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export function supersedeDecision(
|
|
283
|
+
oldDecisionId: string,
|
|
284
|
+
newTitle: string,
|
|
285
|
+
newDecision: string,
|
|
286
|
+
options: { rationale?: string; specId?: string }
|
|
287
|
+
): void {
|
|
288
|
+
initSchema();
|
|
289
|
+
const db = getDb();
|
|
290
|
+
const spec = resolveSpec(options.specId);
|
|
291
|
+
|
|
292
|
+
const oldDec = db
|
|
293
|
+
.query("SELECT * FROM decisions WHERE id = ? AND spec_id = ?")
|
|
294
|
+
.get(oldDecisionId, spec.id) as any;
|
|
295
|
+
|
|
296
|
+
if (!oldDec) {
|
|
297
|
+
throw new CodexaError(`Decisao '${oldDecisionId}' nao encontrada no spec '${spec.id}'.`);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (oldDec.status !== "active") {
|
|
301
|
+
throw new CodexaError(`Decisao '${oldDecisionId}' ja esta com status '${oldDec.status}'. Apenas decisoes ativas podem ser substituidas.`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const now = new Date().toISOString();
|
|
305
|
+
const currentTask = db
|
|
306
|
+
.query("SELECT * FROM tasks WHERE spec_id = ? AND status = 'running' LIMIT 1")
|
|
307
|
+
.get(spec.id) as any;
|
|
308
|
+
|
|
309
|
+
// Criar nova decisao (com retry para colisao de ID)
|
|
310
|
+
let newDecisionId = "";
|
|
311
|
+
let retries = 3;
|
|
312
|
+
while (retries > 0) {
|
|
313
|
+
newDecisionId = getNextDecisionId(spec.id);
|
|
314
|
+
try {
|
|
315
|
+
db.run(
|
|
316
|
+
`INSERT INTO decisions (id, spec_id, task_ref, title, decision, rationale, status, created_at)
|
|
317
|
+
VALUES (?, ?, ?, ?, ?, ?, 'active', ?)`,
|
|
318
|
+
[newDecisionId, spec.id, currentTask?.number || null, newTitle, newDecision, options.rationale || null, now]
|
|
319
|
+
);
|
|
320
|
+
break;
|
|
321
|
+
} catch (e: any) {
|
|
322
|
+
if (e.message?.includes("UNIQUE constraint") && retries > 1) {
|
|
323
|
+
retries--;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
throw e;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Marcar antiga como superseded e linkar
|
|
331
|
+
db.run(
|
|
332
|
+
"UPDATE decisions SET status = 'superseded', superseded_by = ? WHERE id = ?",
|
|
333
|
+
[newDecisionId, oldDecisionId]
|
|
334
|
+
);
|
|
335
|
+
|
|
336
|
+
// Registrar como knowledge
|
|
337
|
+
db.run(
|
|
338
|
+
`INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
|
|
339
|
+
VALUES (?, ?, 'decision', ?, 'warning', 'all', ?)`,
|
|
340
|
+
[
|
|
341
|
+
spec.id,
|
|
342
|
+
currentTask?.id || 0,
|
|
343
|
+
`Decisao SUBSTITUIDA: "${oldDec.title}" -> "${newTitle}" (${newDecisionId} substitui ${oldDecisionId})`,
|
|
344
|
+
now,
|
|
345
|
+
]
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
console.log(`\nDecisao substituida!`);
|
|
349
|
+
console.log(`Antiga: ${oldDecisionId} - "${oldDec.title}" (agora: superseded)`);
|
|
350
|
+
console.log(`Nova: ${newDecisionId} - "${newTitle}"`);
|
|
351
|
+
console.log(`Decisao: ${newDecision}`);
|
|
352
|
+
if (options.rationale) console.log(`Racional: ${options.rationale}`);
|
|
353
|
+
console.log();
|
|
354
|
+
}
|
package/commands/discover.ts
CHANGED
|
@@ -192,11 +192,12 @@ export function discoverConfirm(): void {
|
|
|
192
192
|
|
|
193
193
|
if (structure.components) {
|
|
194
194
|
db.run(
|
|
195
|
-
`INSERT INTO standards (category, scope, rule, examples, source, created_at)
|
|
196
|
-
VALUES ('structure', 'frontend', ?, ?, 'detected', ?)`,
|
|
195
|
+
`INSERT INTO standards (category, scope, rule, examples, semantic_query, expect, source, created_at)
|
|
196
|
+
VALUES ('structure', 'frontend', ?, ?, ?, 'no_match', 'detected', ?)`,
|
|
197
197
|
[
|
|
198
198
|
`Componentes devem estar em ${structure.components}/`,
|
|
199
199
|
JSON.stringify([`${structure.components}/Button.tsx`]),
|
|
200
|
+
`component defined outside of ${structure.components} directory`,
|
|
200
201
|
now,
|
|
201
202
|
]
|
|
202
203
|
);
|
|
@@ -204,11 +205,12 @@ export function discoverConfirm(): void {
|
|
|
204
205
|
|
|
205
206
|
if (structure.services) {
|
|
206
207
|
db.run(
|
|
207
|
-
`INSERT INTO standards (category, scope, rule, examples, source, created_at)
|
|
208
|
-
VALUES ('structure', 'backend', ?, ?, 'detected', ?)`,
|
|
208
|
+
`INSERT INTO standards (category, scope, rule, examples, semantic_query, expect, source, created_at)
|
|
209
|
+
VALUES ('structure', 'backend', ?, ?, ?, 'no_match', 'detected', ?)`,
|
|
209
210
|
[
|
|
210
211
|
`Services devem estar em ${structure.services}/`,
|
|
211
212
|
JSON.stringify([`${structure.services}/authService.ts`]),
|
|
213
|
+
`service class or module defined outside of ${structure.services} directory`,
|
|
212
214
|
now,
|
|
213
215
|
]
|
|
214
216
|
);
|
|
@@ -216,11 +218,12 @@ export function discoverConfirm(): void {
|
|
|
216
218
|
|
|
217
219
|
if (structure.schema) {
|
|
218
220
|
db.run(
|
|
219
|
-
`INSERT INTO standards (category, scope, rule, examples, source, created_at)
|
|
220
|
-
VALUES ('structure', 'database', ?, ?, 'detected', ?)`,
|
|
221
|
+
`INSERT INTO standards (category, scope, rule, examples, semantic_query, expect, source, created_at)
|
|
222
|
+
VALUES ('structure', 'database', ?, ?, ?, 'no_match', 'detected', ?)`,
|
|
221
223
|
[
|
|
222
224
|
`Schema do banco deve estar em ${structure.schema}/`,
|
|
223
225
|
JSON.stringify([`${structure.schema}/users.ts`]),
|
|
226
|
+
`database schema definition outside of ${structure.schema} directory`,
|
|
224
227
|
now,
|
|
225
228
|
]
|
|
226
229
|
);
|
|
@@ -228,17 +231,18 @@ export function discoverConfirm(): void {
|
|
|
228
231
|
|
|
229
232
|
if (structure.types) {
|
|
230
233
|
db.run(
|
|
231
|
-
`INSERT INTO standards (category, scope, rule, examples, source, created_at)
|
|
232
|
-
VALUES ('structure', 'all', ?, ?, 'detected', ?)`,
|
|
234
|
+
`INSERT INTO standards (category, scope, rule, examples, semantic_query, expect, source, created_at)
|
|
235
|
+
VALUES ('structure', 'all', ?, ?, ?, 'no_match', 'detected', ?)`,
|
|
233
236
|
[
|
|
234
237
|
`Tipos devem estar em ${structure.types}/`,
|
|
235
238
|
JSON.stringify([`${structure.types}/user.ts`]),
|
|
239
|
+
`type definition outside of ${structure.types} directory`,
|
|
236
240
|
now,
|
|
237
241
|
]
|
|
238
242
|
);
|
|
239
243
|
}
|
|
240
244
|
|
|
241
|
-
// Standards de nomenclatura padrao
|
|
245
|
+
// Standards de nomenclatura padrao (sem semantic_query — grepai busca conteudo, nao nomes de arquivo)
|
|
242
246
|
db.run(
|
|
243
247
|
`INSERT INTO standards (category, scope, rule, examples, anti_examples, source, created_at)
|
|
244
248
|
VALUES ('naming', 'frontend', ?, ?, ?, 'detected', ?)`,
|
|
@@ -632,6 +636,11 @@ function generateStandardsMarkdown(): void {
|
|
|
632
636
|
}
|
|
633
637
|
}
|
|
634
638
|
|
|
639
|
+
if (std.semantic_query) {
|
|
640
|
+
md += `- **Semantic Query:** \`${std.semantic_query}\`\n`;
|
|
641
|
+
md += `- **Expect:** ${std.expect || "no_match"}\n`;
|
|
642
|
+
}
|
|
643
|
+
|
|
635
644
|
md += "\n";
|
|
636
645
|
}
|
|
637
646
|
}
|