@codexa/cli 9.0.1 → 9.0.3

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.
@@ -27,6 +27,8 @@ export interface Reasoning {
27
27
  }
28
28
 
29
29
  export interface SubagentReturn {
30
+ // v9.1: Versao do protocolo (opcional por compatibilidade)
31
+ protocol_version?: string;
30
32
  status: "completed" | "blocked" | "needs_decision";
31
33
  summary: string;
32
34
  files_created: string[];
@@ -58,11 +60,14 @@ const VALID_STATUSES = ["completed", "blocked", "needs_decision"];
58
60
  const VALID_KNOWLEDGE_CATEGORIES = ["discovery", "decision", "blocker", "pattern", "constraint"];
59
61
  const VALID_SEVERITIES = ["info", "warning", "critical"];
60
62
 
63
+ // v9.1: Versao atual do protocolo de subagentes
64
+ export const CURRENT_PROTOCOL_VERSION = "9.1";
65
+
61
66
  /**
62
67
  * Extrai JSON de uma string que pode conter texto misto
63
68
  * Subagents podem retornar texto com JSON embutido
64
69
  */
65
- function extractJsonFromText(text: string): string | null {
70
+ export function extractJsonFromText(text: string): string | null {
66
71
  // Tenta encontrar JSON em bloco de codigo
67
72
  const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
68
73
  if (codeBlockMatch) {
@@ -222,6 +227,8 @@ export function parseSubagentReturn(input: string): ParseResult {
222
227
  errors: [
223
228
  `JSON invalido: ${(e as Error).message}`,
224
229
  "Verifique se o JSON esta bem formatado.",
230
+ "Dica: Caracteres como ! $ ` em double-quotes causam problemas no shell.",
231
+ "Use aspas simples ou --output-file /tmp/result.json para evitar escaping.",
225
232
  ],
226
233
  rawInput: input,
227
234
  };
@@ -238,6 +245,10 @@ export function parseSubagentReturn(input: string): ParseResult {
238
245
  const summaryError = validateString(parsed.summary, "summary", 10, 500);
239
246
  if (summaryError) errors.push(summaryError);
240
247
 
248
+ // files_created / files_modified: default para [] se ausentes
249
+ if (parsed.files_created === undefined) parsed.files_created = [];
250
+ if (parsed.files_modified === undefined) parsed.files_modified = [];
251
+
241
252
  // files_created
242
253
  const filesCreatedError = validateStringArray(parsed.files_created, "files_created");
243
254
  if (filesCreatedError) errors.push(filesCreatedError);
@@ -344,6 +355,17 @@ export function parseSubagentReturn(input: string): ParseResult {
344
355
  }
345
356
  }
346
357
 
358
+ // v9.1: protocol_version (opcional, warn on mismatch)
359
+ if (parsed.protocol_version !== undefined) {
360
+ if (typeof parsed.protocol_version !== "string") {
361
+ errors.push("Campo 'protocol_version' deve ser string");
362
+ } else if (parsed.protocol_version !== CURRENT_PROTOCOL_VERSION) {
363
+ console.warn(
364
+ `[protocol] Versao do protocolo: esperado ${CURRENT_PROTOCOL_VERSION}, recebido ${parsed.protocol_version}`
365
+ );
366
+ }
367
+ }
368
+
347
369
  // 5. Validacoes semanticas
348
370
 
349
371
  // Se blocked, deve ter blockers
@@ -370,6 +392,7 @@ export function parseSubagentReturn(input: string): ParseResult {
370
392
 
371
393
  // Cast para tipo correto
372
394
  const data: SubagentReturn = {
395
+ protocol_version: parsed.protocol_version as string | undefined,
373
396
  status: parsed.status as SubagentReturn["status"],
374
397
  summary: parsed.summary as string,
375
398
  files_created: parsed.files_created as string[],
@@ -406,6 +429,7 @@ export function formatValidationErrors(result: ParseResult): string {
406
429
  output += "\n" + "─".repeat(50) + "\n";
407
430
  output += "O retorno deve seguir o formato:\n\n";
408
431
  output += `{
432
+ "protocol_version": "9.1",
409
433
  "status": "completed | blocked | needs_decision",
410
434
  "summary": "Resumo do que foi feito (10-500 chars)",
411
435
  "files_created": ["path/to/file.ts"],
package/workflow.ts CHANGED
@@ -22,12 +22,14 @@ import {
22
22
  discoverPatternRemove,
23
23
  discoverRefreshPatterns,
24
24
  discoverExportPatterns,
25
+ discoverIncremental,
25
26
  } from "./commands/discover";
26
27
  import { clearTasks, clearShow } from "./commands/clear";
27
28
  import { standardsList, standardsAdd, standardsEdit, standardsRemove, standardsExport } from "./commands/standards";
28
29
  import { productGuide, productImport, productSet, productGoalAdd, productFeatureAdd, productConfirm, productShow, productReset } from "./commands/product";
29
30
  import { researchStart, researchShow, researchFill, researchMapAgent, researchReset } from "./commands/research";
30
31
  import { patternsExtract, patternsAnalyze } from "./commands/patterns";
32
+ import { syncAgents } from "./commands/sync";
31
33
  import {
32
34
  architectStart,
33
35
  architectShow,
@@ -43,6 +45,7 @@ import { execSync } from "child_process";
43
45
  import { existsSync, readFileSync } from "fs";
44
46
  import { join } from "path";
45
47
  import pkg from "./package.json";
48
+ import { CodexaError, ValidationError } from "./errors";
46
49
 
47
50
  function checkVersionSync(): void {
48
51
  // 1. Check CLI vs Plugin (dev repo only)
@@ -52,10 +55,11 @@ function checkVersionSync(): void {
52
55
  if (existsSync(pluginJson)) {
53
56
  const plugin = JSON.parse(readFileSync(pluginJson, "utf-8"));
54
57
  if (plugin.version && plugin.version !== pkg.version) {
55
- console.error(`\n✘ Versao incompativel: CLI=${pkg.version} Plugin=${plugin.version}`);
56
- console.error(` O CLI e o plugin DEVEM ter a mesma versao.`);
57
- console.error(` Atualize o CLI: bun add -g @codexa/cli@${plugin.version}\n`);
58
- process.exit(1);
58
+ throw new CodexaError(
59
+ `✘ Versao incompativel: CLI=${pkg.version} Plugin=${plugin.version}\n` +
60
+ ` O CLI e o plugin DEVEM ter a mesma versao.\n` +
61
+ ` Atualize o CLI: bun add -g @codexa/cli@${plugin.version}`
62
+ );
59
63
  }
60
64
  }
61
65
  } catch {
@@ -83,25 +87,59 @@ function checkVersionSync(): void {
83
87
  }
84
88
 
85
89
  if (project.cli_version !== pkg.version) {
86
- console.error(`\n✘ Versao incompativel: CLI=${pkg.version} Projeto=${project.cli_version}`);
87
- console.error(` O projeto foi configurado com CLI v${project.cli_version} mas voce esta usando v${pkg.version}.`);
88
- console.error(` Opcoes:`);
89
- console.error(` 1. Atualize o CLI: bun add -g @codexa/cli@${project.cli_version}`);
90
- console.error(` 2. Reconfigure o projeto: codexa discover reset && codexa discover start\n`);
91
- process.exit(1);
90
+ throw new CodexaError(
91
+ `✘ Versao incompativel: CLI=${pkg.version} Projeto=${project.cli_version}\n` +
92
+ ` O projeto foi configurado com CLI v${project.cli_version} mas voce esta usando v${pkg.version}.\n` +
93
+ ` Opcoes:\n` +
94
+ ` 1. Atualize o CLI: bun add -g @codexa/cli@${project.cli_version}\n` +
95
+ ` 2. Reconfigure o projeto: codexa discover reset && codexa discover start`
96
+ );
92
97
  }
93
98
  } catch {
94
99
  // DB not available or column doesn't exist yet — skip
95
100
  }
96
101
  }
97
102
 
103
+ function handleError(e: unknown): never {
104
+ if (e instanceof CodexaError) {
105
+ console.error(`\n${e.message}\n`);
106
+ process.exit(e.exitCode);
107
+ }
108
+ console.error(`\n[ERRO INESPERADO] ${(e as Error).message}\n`);
109
+ process.exit(1);
110
+ }
111
+
112
+ function wrapAction<T extends (...args: any[]) => any>(fn: T): T {
113
+ return ((...args: any[]) => {
114
+ try {
115
+ const result = fn(...args);
116
+ if (result instanceof Promise) {
117
+ return result.catch(handleError);
118
+ }
119
+ return result;
120
+ } catch (e) {
121
+ handleError(e);
122
+ }
123
+ }) as T;
124
+ }
125
+
98
126
  const program = new Command();
99
127
 
100
128
  program
101
129
  .name("codexa")
102
130
  .description(`Codexa Workflow v${pkg.version} - Sistema de workflow para Claude Code`)
103
131
  .version(pkg.version)
104
- .hook("preAction", () => checkVersionSync());
132
+ .hook("preAction", () => {
133
+ try {
134
+ checkVersionSync();
135
+ } catch (e) {
136
+ if (e instanceof CodexaError) {
137
+ console.error(`\n${e.message}\n`);
138
+ process.exit(e.exitCode);
139
+ }
140
+ throw e;
141
+ }
142
+ });
105
143
 
106
144
  // ═══════════════════════════════════════════════════════════════
107
145
  // FASE: PLAN
@@ -182,33 +220,56 @@ taskCmd
182
220
  .command("next")
183
221
  .description("Mostra proximas tasks disponiveis")
184
222
  .option("--json", "Saida em JSON")
185
- .action((options) => {
223
+ .action(wrapAction((options) => {
186
224
  taskNext(options.json);
187
- });
225
+ }));
188
226
 
189
227
  taskCmd
190
228
  .command("start <ids>")
191
229
  .description("Inicia task(s) - pode ser multiplas separadas por virgula")
192
230
  .option("--json", "Saida em JSON")
193
231
  .option("--full-context", "Incluir contexto completo (modo legado)")
194
- .action((ids: string, options) => {
232
+ .action(wrapAction((ids: string, options) => {
195
233
  taskStart(ids, options.json, options.fullContext);
196
- });
234
+ }));
197
235
 
198
236
  taskCmd
199
237
  .command("done <id>")
200
238
  .description("Completa uma task")
201
239
  .option("--checkpoint <text>", "Resumo do que foi feito (extraido automaticamente de --output)")
202
240
  .option("--output <json>", "Retorno JSON do subagent (processa automaticamente)")
241
+ .option("--output-file <path>", "Ler retorno JSON de arquivo (evita problemas de shell escaping)")
203
242
  .option("--files <files>", "Arquivos criados/modificados (extraido automaticamente de --output)")
204
243
  .option("--force", "Ignorar validacao de standards (sera registrado)")
205
244
  .option("--force-reason <reason>", "Motivo do bypass de validacao")
206
- .action((id: string, options) => {
245
+ .action(wrapAction((id: string, options) => {
246
+ // Resolver --output-file: ler conteudo do arquivo
247
+ if (options.outputFile && !options.output) {
248
+ try {
249
+ options.output = readFileSync(options.outputFile, "utf-8");
250
+ } catch (e) {
251
+ throw new ValidationError(
252
+ `[ERRO] Nao foi possivel ler arquivo: ${options.outputFile}\n ${(e as Error).message}`
253
+ );
254
+ }
255
+ }
256
+
257
+ // Resolver stdin: --output - (le de stdin)
258
+ if (options.output === "-") {
259
+ try {
260
+ options.output = readFileSync("/dev/stdin", "utf-8");
261
+ } catch (e) {
262
+ throw new ValidationError(
263
+ `[ERRO] Nao foi possivel ler de stdin.\n ${(e as Error).message}`
264
+ );
265
+ }
266
+ }
267
+
207
268
  // Checkpoint obrigatorio se nao houver --output
208
269
  if (!options.output && !options.checkpoint) {
209
- console.error("\n[ERRO] Checkpoint obrigatorio.");
210
- console.error("Use: --checkpoint 'resumo' ou --output '{json do subagent}'\n");
211
- process.exit(1);
270
+ throw new ValidationError(
271
+ "[ERRO] Checkpoint obrigatorio.\nUse: --checkpoint 'resumo' ou --output '{json do subagent}'"
272
+ );
212
273
  }
213
274
 
214
275
  taskDone(id, {
@@ -218,7 +279,7 @@ taskCmd
218
279
  forceReason: options.forceReason,
219
280
  output: options.output,
220
281
  });
221
- });
282
+ }));
222
283
 
223
284
  // ═══════════════════════════════════════════════════════════════
224
285
  // DECISOES
@@ -294,7 +355,6 @@ knowledgeCmd
294
355
  .description("Consulta o knowledge graph (relacoes entre arquivos, decisoes, patterns)")
295
356
  .option("--file <path>", "Buscar relacoes de um arquivo")
296
357
  .option("--decision <id>", "Buscar arquivos afetados por uma decisao")
297
- .option("--contradictions", "Detectar contradicoes entre decisoes")
298
358
  .option("--json", "Saida em JSON")
299
359
  .action((options) => {
300
360
  queryGraph(options);
@@ -454,6 +514,13 @@ discoverCmd
454
514
  await discoverRefresh(options);
455
515
  });
456
516
 
517
+ discoverCmd
518
+ .command("incremental")
519
+ .description("Atualiza discover incrementalmente (arquivos modificados desde ultimo discover)")
520
+ .action(async () => {
521
+ await discoverIncremental();
522
+ });
523
+
457
524
  // ─────────────────────────────────────────────────────────────────
458
525
  // PATTERNS (v7.4)
459
526
  // ─────────────────────────────────────────────────────────────────
@@ -872,5 +939,19 @@ architectCmd
872
939
  architectCancel(options);
873
940
  });
874
941
 
942
+ // ═══════════════════════════════════════════════════════════════
943
+ // PLUGIN
944
+ // ═══════════════════════════════════════════════════════════════
945
+
946
+ const pluginCmd = program.command("plugin").description("Comandos de gestao do plugin");
947
+
948
+ pluginCmd
949
+ .command("sync-agents")
950
+ .description("Copia agents do plugin para .claude/agents/ do projeto (resolve limitacao de subagent_type)")
951
+ .option("--force", "Sobrescrever agents existentes")
952
+ .action((options) => {
953
+ syncAgents(options);
954
+ });
955
+
875
956
  // Parse e executa
876
957
  program.parse();