@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.
@@ -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
+ }
@@ -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
  }