@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.
- package/commands/architect.test.ts +531 -0
- package/commands/architect.ts +68 -11
- package/commands/clear.ts +0 -1
- package/commands/decide.ts +28 -28
- package/commands/discover.ts +128 -3
- package/commands/knowledge.ts +2 -27
- package/commands/patterns.test.ts +169 -0
- package/commands/plan.test.ts +73 -0
- package/commands/plan.ts +4 -2
- package/commands/sync.ts +90 -0
- package/commands/task.ts +43 -159
- package/commands/utils.ts +251 -249
- package/db/schema.test.ts +333 -0
- package/db/schema.ts +160 -130
- package/gates/validator.test.ts +617 -0
- package/gates/validator.ts +42 -10
- package/package.json +3 -1
- package/protocol/process-return.ts +25 -93
- package/protocol/subagent-protocol.test.ts +936 -0
- package/protocol/subagent-protocol.ts +25 -1
- package/workflow.ts +102 -21
|
@@ -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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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", () =>
|
|
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
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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();
|