@codexa/cli 9.0.21 → 9.0.23

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.
@@ -107,10 +107,20 @@ export function detectConflicts(
107
107
 
108
108
  // Verificar sobreposicao alta de keywords (mesmo topico, decisoes diferentes?)
109
109
  if (conflicts.every((c) => c.id !== existing.id)) {
110
+ // v10.1: Skip exact duplicates (same title+decision = not a conflict)
111
+ if (
112
+ newTitle.trim().toLowerCase() === existing.title?.trim().toLowerCase() &&
113
+ newDecision.trim().toLowerCase() === existing.decision?.trim().toLowerCase()
114
+ ) {
115
+ continue;
116
+ }
117
+
110
118
  const intersection = [...newKeywords].filter((k) => existingKeywords.has(k));
111
119
  const similarity = intersection.length / Math.max(newKeywords.size, existingKeywords.size);
112
120
 
113
- if (similarity > 0.4 && intersection.length >= 3) {
121
+ // v10.1: Raised threshold from 0.4 to 0.7 to reduce false positives
122
+ // Complementary decisions (e.g. "Use REST" + "Use JWT") were falsely flagged
123
+ if (similarity > 0.7 && intersection.length >= 3) {
114
124
  conflicts.push({
115
125
  id: existing.id,
116
126
  title: existing.title,
@@ -4,6 +4,7 @@ import { enforceGate } from "../gates/validator";
4
4
  import { resolveSpec } from "./spec-resolver";
5
5
  import { CodexaError, GateError } from "../errors";
6
6
  import { cleanupContextFiles } from "../context/file-writer";
7
+ import { simplifyAudit } from "./simplify";
7
8
 
8
9
  // ═══════════════════════════════════════════════════════════════
9
10
  // P1-2: Review Score — Threshold Automatico
@@ -264,6 +265,17 @@ export function reviewStart(json: boolean = false, specId?: string): void {
264
265
  console.log(`\nNenhum desvio encontrado.`);
265
266
  }
266
267
 
268
+ // v10.1: Simplify audit
269
+ try {
270
+ const simplifyContextPath = simplifyAudit(spec.id);
271
+ if (simplifyContextPath) {
272
+ console.log(`\nAuditoria de Simplificacao:`);
273
+ console.log(` Prompt gerado com standards do projeto`);
274
+ console.log(` Contexto: ${simplifyContextPath}`);
275
+ console.log(` Invoque o simplifier agent em modo audit para analise de qualidade`);
276
+ }
277
+ } catch { /* nao-critico */ }
278
+
267
279
  if (score.total < 50) {
268
280
  console.log(`\n[!] Score abaixo de 50. Aprovacao requer: review approve --force --force-reason "motivo"`);
269
281
  } else {
@@ -0,0 +1,142 @@
1
+ // ═══════════════════════════════════════════════════════════════
2
+ // SIMPLIFY COMMAND (v10.1)
3
+ // Generates simplification prompts enriched with Codexa standards.
4
+ // Used post-task (Option A) and pre-review (Option C).
5
+ // ═══════════════════════════════════════════════════════════════
6
+
7
+ import { getDb } from "../db/connection";
8
+ import { initSchema } from "../db/schema";
9
+ import { resolveSpec } from "./spec-resolver";
10
+ import { getAgentDomain, domainToScope } from "../context/domains";
11
+ import { CodexaError } from "../errors";
12
+ import {
13
+ buildSimplifyPrompt,
14
+ writeSimplifyContext,
15
+ type SimplifyFile,
16
+ type SimplifyMode,
17
+ } from "../simplify/prompt-builder";
18
+
19
+ // ═══════════════════════════════════════════════════════════════
20
+ // SIMPLIFY FOR TASK (Option A)
21
+ // ═══════════════════════════════════════════════════════════════
22
+
23
+ export function simplifyTask(
24
+ taskId: string,
25
+ options: { audit?: boolean; specId?: string } = {}
26
+ ): void {
27
+ initSchema();
28
+
29
+ const db = getDb();
30
+ const id = parseInt(taskId);
31
+ const task = db.query("SELECT * FROM tasks WHERE id = ?").get(id) as any;
32
+
33
+ if (!task) {
34
+ throw new CodexaError(`Task #${id} nao encontrada.`);
35
+ }
36
+
37
+ if (task.status !== "done") {
38
+ throw new CodexaError(
39
+ `Task #${id} nao esta concluida (status: ${task.status}). Complete com 'task done' primeiro.`
40
+ );
41
+ }
42
+
43
+ // Get files from artifacts
44
+ const artifacts = db
45
+ .query(
46
+ "SELECT path, action FROM artifacts WHERE spec_id = ? AND task_ref = ?"
47
+ )
48
+ .all(task.spec_id, task.number) as any[];
49
+
50
+ if (artifacts.length === 0) {
51
+ console.log(`\nTask #${task.number} nao tem arquivos registrados. Nada para simplificar.\n`);
52
+ return;
53
+ }
54
+
55
+ const files: SimplifyFile[] = artifacts.map((a: any) => ({
56
+ path: a.path,
57
+ action: a.action === "created" ? "created" as const : "modified" as const,
58
+ }));
59
+
60
+ // Determine agent domain for scope filtering
61
+ const domain = getAgentDomain(task.agent);
62
+ const scope = domainToScope(domain);
63
+ const mode: SimplifyMode = options.audit ? "audit" : "refactor";
64
+
65
+ // Build prompt with standards
66
+ const prompt = buildSimplifyPrompt(files, scope, mode);
67
+
68
+ // Write context file
69
+ const contextPath = writeSimplifyContext(`task-${task.number}`, prompt);
70
+
71
+ // Output
72
+ const modeLabel = mode === "audit" ? "AUDITORIA" : "REFATORACAO";
73
+ console.log(`\n${"=".repeat(55)}`);
74
+ console.log(`SIMPLIFY (${modeLabel}) — Task #${task.number}`);
75
+ console.log(`${"=".repeat(55)}`);
76
+ console.log(` Arquivos: ${files.length}`);
77
+ console.log(` Scope: ${scope}`);
78
+ console.log(` Modo: ${mode}`);
79
+ console.log(` Contexto: ${contextPath}`);
80
+ console.log(`\n O orchestrador deve invocar o simplifier agent com o prompt acima.`);
81
+ console.log(` Leia o contexto com: Read ${contextPath}`);
82
+ console.log(`${"=".repeat(55)}\n`);
83
+ }
84
+
85
+ // ═══════════════════════════════════════════════════════════════
86
+ // SIMPLIFY AUDIT FOR REVIEW (Option C)
87
+ // ═══════════════════════════════════════════════════════════════
88
+
89
+ export function simplifyAudit(specId?: string): string | null {
90
+ initSchema();
91
+
92
+ const db = getDb();
93
+ const spec = resolveSpec(specId, ["implementing", "reviewing"]);
94
+
95
+ // Get all feature artifacts
96
+ const artifacts = db
97
+ .query("SELECT DISTINCT path, action FROM artifacts WHERE spec_id = ?")
98
+ .all(spec.id) as any[];
99
+
100
+ if (artifacts.length === 0) {
101
+ return null;
102
+ }
103
+
104
+ const files: SimplifyFile[] = artifacts.map((a: any) => ({
105
+ path: a.path,
106
+ action: a.action === "created" ? "created" as const : "modified" as const,
107
+ }));
108
+
109
+ // Use "all" scope for review (covers all domains)
110
+ const prompt = buildSimplifyPrompt(files, "all", "audit");
111
+
112
+ // Write context file
113
+ const contextPath = writeSimplifyContext(`audit-${spec.id}`, prompt);
114
+
115
+ return contextPath;
116
+ }
117
+
118
+ // ═══════════════════════════════════════════════════════════════
119
+ // AUTO-SIMPLIFY CHECK
120
+ // ═══════════════════════════════════════════════════════════════
121
+
122
+ export function isAutoSimplifyEnabled(): boolean {
123
+ try {
124
+ const db = getDb();
125
+ const project = db
126
+ .query("SELECT auto_simplify FROM project WHERE id = 'default'")
127
+ .get() as any;
128
+ return project?.auto_simplify === 1;
129
+ } catch {
130
+ return false;
131
+ }
132
+ }
133
+
134
+ export function setAutoSimplify(enabled: boolean): void {
135
+ initSchema();
136
+ const db = getDb();
137
+ db.run(
138
+ "UPDATE project SET auto_simplify = ?, updated_at = ? WHERE id = 'default'",
139
+ [enabled ? 1 : 0, new Date().toISOString()]
140
+ );
141
+ console.log(`\nauto_simplify ${enabled ? "habilitado" : "desabilitado"}.\n`);
142
+ }
package/commands/task.ts CHANGED
@@ -16,6 +16,8 @@ import { getModelForTask } from "../context/model-profiles";
16
16
  import { getContextBudget, formatContextWarning, estimateTokens } from "../context/monitor";
17
17
  import { invalidateCache } from "../context/cache";
18
18
  import { cleanupContextFiles } from "../context/file-writer";
19
+ import { isAutoSimplifyEnabled } from "./simplify";
20
+ import { buildSimplifyPrompt, writeSimplifyContext, type SimplifyFile } from "../simplify/prompt-builder";
19
21
 
20
22
  export function taskNext(json: boolean = false, specId?: string): void {
21
23
  initSchema();
@@ -369,7 +371,7 @@ export function taskStart(ids: string, json: boolean = false, minimalContext: bo
369
371
  console.log(`\nAo concluir, use: task done <id> --checkpoint "resumo"\n`);
370
372
  }
371
373
 
372
- export function taskDone(id: string, options: { checkpoint: string; files?: string; force?: boolean; forceReason?: string; output?: string }): void {
374
+ export function taskDone(id: string, options: { checkpoint: string; files?: string; force?: boolean; forceReason?: string; output?: string; simplify?: boolean }): void {
373
375
  initSchema();
374
376
 
375
377
  const taskId = parseInt(id);
@@ -505,6 +507,26 @@ export function taskDone(id: string, options: { checkpoint: string; files?: stri
505
507
 
506
508
  // Nota: Validacao de standards agora e feita pelo Gate 4.2 (standards-follow) em enforceGate
507
509
 
510
+ // v10.1: Simplify pos-task (opt-in)
511
+ try {
512
+ const shouldSimplify = options.simplify || isAutoSimplifyEnabled();
513
+ if (shouldSimplify && expectedFiles.length > 0) {
514
+ const domain = getAgentDomain(task.agent);
515
+ const scope = domainToScope(domain);
516
+ const simplifyFiles: SimplifyFile[] = expectedFiles.map((f: string) => ({
517
+ path: f,
518
+ action: (subagentData?.files_created?.includes(f) ? "created" : "modified") as "created" | "modified",
519
+ }));
520
+
521
+ const prompt = buildSimplifyPrompt(simplifyFiles, scope, "refactor");
522
+ const contextPath = writeSimplifyContext(`task-${task.number}`, prompt);
523
+
524
+ console.log(`\n[simplify] Prompt gerado com ${simplifyFiles.length} arquivo(s) e standards do projeto`);
525
+ console.log(`[simplify] Contexto: ${contextPath}`);
526
+ console.log(`[simplify] Invoque o simplifier agent com o prompt acima`);
527
+ }
528
+ } catch { /* nao-critico: nao falhar task done por simplify */ }
529
+
508
530
  // v9.0: Atualizar patterns incrementalmente com arquivos da task
509
531
  try {
510
532
  if (subagentData && subagentData.status === "completed") {
package/db/schema.ts CHANGED
@@ -496,6 +496,13 @@ const MIGRATIONS: Migration[] = [
496
496
  db.exec(`ALTER TABLE context ADD COLUMN current_phase INTEGER DEFAULT 1`);
497
497
  },
498
498
  },
499
+ {
500
+ version: "10.1.0",
501
+ description: "Adicionar auto_simplify na tabela project para simplificacao automatica pos-task",
502
+ up: (db) => {
503
+ db.exec(`ALTER TABLE project ADD COLUMN auto_simplify INTEGER DEFAULT 0`);
504
+ },
505
+ },
499
506
  ];
500
507
 
501
508
  export function runMigrations(): void {
@@ -493,9 +493,17 @@ function executeCheck(check: string, context: any): { passed: boolean; details?:
493
493
  .all(dcTask.spec_id) as any[];
494
494
  if (existingDecisions.length === 0) return { passed: true };
495
495
 
496
+ // v10.1: Filter out decisions from the CURRENT task.
497
+ // processSubagentReturn() already saved them to DB before this gate runs,
498
+ // so without filtering, the new decisions would match against themselves.
499
+ const priorDecisions = existingDecisions.filter(
500
+ (d: any) => d.task_ref !== dcTask.number
501
+ );
502
+ if (priorDecisions.length === 0) return { passed: true };
503
+
496
504
  const allConflicts: string[] = [];
497
505
  for (const dec of context.subagentData.decisions_made) {
498
- const analysis = detectConflicts(dec.title, dec.decision, existingDecisions);
506
+ const analysis = detectConflicts(dec.title, dec.decision, priorDecisions);
499
507
  if (analysis.hasConflict) {
500
508
  for (const conflict of analysis.conflictingDecisions) {
501
509
  allConflicts.push(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codexa/cli",
3
- "version": "9.0.21",
3
+ "version": "9.0.23",
4
4
  "description": "Orchestrated workflow system for Claude Code - manages feature development through parallel subagents with structured phases, gates, and quality enforcement.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -316,20 +316,35 @@ export function parseSubagentReturn(input: string): ParseResult {
316
316
  }
317
317
  }
318
318
 
319
- // v9.0: Validacao de reasoning obrigatorio para status "completed" (Gate 4.4)
319
+ // v9.0: Validacao de reasoning para status "completed" (Gate 4.4)
320
+ // v10.1: Auto-derive reasoning from summary when missing (graceful degradation)
320
321
  if (parsed.status === "completed") {
321
322
  if (!parsed.reasoning || typeof parsed.reasoning !== "object") {
322
- errors.push(
323
- "Status 'completed' requer campo 'reasoning' com pelo menos 'approach'. " +
324
- "Isso preserva contexto entre tasks. Exemplo: {\"reasoning\": {\"approach\": \"Descreva como abordou o problema\"}}"
325
- );
323
+ // Auto-derive reasoning from summary instead of hard-failing
324
+ const summary = typeof parsed.summary === "string" ? parsed.summary : "";
325
+ if (summary.length >= 20) {
326
+ parsed.reasoning = { approach: summary };
327
+ console.warn("[protocol] reasoning ausente — auto-derivado do summary");
328
+ } else {
329
+ errors.push(
330
+ "Status 'completed' requer campo 'reasoning' com pelo menos 'approach'. " +
331
+ "Isso preserva contexto entre tasks. Exemplo: {\"reasoning\": {\"approach\": \"Descreva como abordou o problema\"}}"
332
+ );
333
+ }
326
334
  } else {
327
335
  const r = parsed.reasoning as Record<string, unknown>;
328
336
  if (!r.approach || typeof r.approach !== "string" || (r.approach as string).length < 20) {
329
- errors.push(
330
- "Campo 'reasoning.approach' obrigatorio com minimo 20 caracteres para status 'completed'. " +
331
- "Descreva COMO voce abordou o problema, nao apenas O QUE fez."
332
- );
337
+ // Auto-derive approach from summary if approach is too short
338
+ const summary = typeof parsed.summary === "string" ? parsed.summary : "";
339
+ if (summary.length >= 20) {
340
+ (parsed.reasoning as Record<string, unknown>).approach = summary;
341
+ console.warn("[protocol] reasoning.approach insuficiente — auto-derivado do summary");
342
+ } else {
343
+ errors.push(
344
+ "Campo 'reasoning.approach' obrigatorio com minimo 20 caracteres para status 'completed'. " +
345
+ "Descreva COMO voce abordou o problema, nao apenas O QUE fez."
346
+ );
347
+ }
333
348
  }
334
349
  }
335
350
  }
package/workflow.ts CHANGED
@@ -269,6 +269,7 @@ taskCmd
269
269
  .option("--files <files>", "Arquivos criados/modificados (extraido automaticamente de --output)")
270
270
  .option("--force", "Ignorar validacao de standards (sera registrado)")
271
271
  .option("--force-reason <reason>", "Motivo do bypass de validacao")
272
+ .option("--simplify", "Gerar prompt de simplificacao com standards do projeto")
272
273
  .action(wrapAction((id: string, options) => {
273
274
  // Resolver --output-file: ler conteudo do arquivo
274
275
  if (options.outputFile && !options.output) {
@@ -305,6 +306,7 @@ taskCmd
305
306
  force: options.force,
306
307
  forceReason: options.forceReason,
307
308
  output: options.output,
309
+ simplify: options.simplify,
308
310
  });
309
311
  }));
310
312
 
@@ -318,6 +320,20 @@ taskCmd
318
320
  taskPhaseAdvance({ noCompact: options.compact === false, json: options.json, spec: options.spec });
319
321
  }));
320
322
 
323
+ // ═══════════════════════════════════════════════════════════════
324
+ // SIMPLIFY (v10.1)
325
+ // ═══════════════════════════════════════════════════════════════
326
+
327
+ program
328
+ .command("simplify <taskId>")
329
+ .description("Gera prompt de simplificacao com standards do projeto para uma task")
330
+ .option("--audit", "Modo auditoria (apenas relatorio, sem aplicar mudancas)")
331
+ .option("--spec <id>", "ID do spec (padrao: mais recente)")
332
+ .action(wrapAction((taskId: string, options) => {
333
+ const { simplifyTask } = require("./commands/simplify");
334
+ simplifyTask(taskId, { audit: options.audit, specId: options.spec });
335
+ }));
336
+
321
337
  // ═══════════════════════════════════════════════════════════════
322
338
  // DECISOES
323
339
  // ═══════════════════════════════════════════════════════════════
@@ -610,6 +626,8 @@ discoverCmd
610
626
  .option("--testing <value>", "Testing (vitest, jest, playwright)")
611
627
  .option("--typecheck-command <cmd>", "Comando custom de typecheck (ex: 'mypy --strict', 'go vet ./...'). Vazio para usar preset automatico.")
612
628
  .option("--grepai-workspace <name>", "Nome do workspace grepai para busca semantica cross-project. Vazio para remover.")
629
+ .option("--auto-simplify", "Habilitar simplificacao automatica pos-task com standards do projeto")
630
+ .option("--no-auto-simplify", "Desabilitar simplificacao automatica pos-task")
613
631
  .action((options) => {
614
632
  // Tratar typecheck-command separadamente
615
633
  if (options.typecheckCommand !== undefined) {
@@ -648,10 +666,16 @@ discoverCmd
648
666
  // Auto-regenerate deep-explore agent with new workspace config
649
667
  ensureDeepExploreAgent();
650
668
  }
669
+ // Tratar auto-simplify
670
+ if (options.autoSimplify !== undefined) {
671
+ const { setAutoSimplify } = require("./commands/simplify");
672
+ setAutoSimplify(!!options.autoSimplify);
673
+ }
651
674
  // Tratar stack options normalmente
652
675
  const stackOptions = { ...options };
653
676
  delete stackOptions.typecheckCommand;
654
677
  delete stackOptions.grepaiWorkspace;
678
+ delete stackOptions.autoSimplify;
655
679
  if (Object.keys(stackOptions).length > 0) {
656
680
  discoverSetStack(stackOptions);
657
681
  }