@codexa/cli 8.5.0

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.
@@ -0,0 +1,131 @@
1
+ import { getDb } from "../db/connection";
2
+ import { initSchema } from "../db/schema";
3
+ import { enforceGate } from "../gates/validator";
4
+
5
+ export function checkRequest(): void {
6
+ initSchema();
7
+ enforceGate("check-request");
8
+
9
+ const db = getDb();
10
+ const spec = db
11
+ .query("SELECT * FROM specs WHERE phase NOT IN ('completed', 'cancelled') ORDER BY created_at DESC LIMIT 1")
12
+ .get() as any;
13
+
14
+ const tasks = db
15
+ .query("SELECT * FROM tasks WHERE spec_id = ? ORDER BY number")
16
+ .all(spec.id) as any[];
17
+
18
+ const context = db
19
+ .query("SELECT * FROM context WHERE spec_id = ?")
20
+ .get(spec.id) as any;
21
+
22
+ // Atualizar fase para checking
23
+ db.run("UPDATE specs SET phase = 'checking', updated_at = ? WHERE id = ?", [
24
+ new Date().toISOString(),
25
+ spec.id,
26
+ ]);
27
+
28
+ // Exibir plano para aprovacao
29
+ console.log(`\n${"=".repeat(60)}`);
30
+ console.log(`SOLICITACAO DE APROVACAO`);
31
+ console.log(`${"=".repeat(60)}`);
32
+ console.log(`\nFeature: ${spec.name}`);
33
+ console.log(`Objetivo: ${context?.objective || "-"}`);
34
+ console.log(`\nTasks planejadas (${tasks.length}):`);
35
+ console.log(`${"─".repeat(60)}`);
36
+
37
+ // Identificar grupos paralelos
38
+ const groups: { parallel: any[]; sequential: any[] } = { parallel: [], sequential: [] };
39
+
40
+ for (const task of tasks) {
41
+ const deps = task.depends_on ? JSON.parse(task.depends_on) : [];
42
+ if (task.can_parallel && deps.length === 0) {
43
+ groups.parallel.push(task);
44
+ } else {
45
+ groups.sequential.push(task);
46
+ }
47
+ }
48
+
49
+ if (groups.parallel.length > 0) {
50
+ console.log(`\nGrupo paralelo (podem executar simultaneamente):`);
51
+ for (const task of groups.parallel) {
52
+ console.log(` #${task.number}: ${task.name} (${task.agent || "geral"})`);
53
+ }
54
+ }
55
+
56
+ if (groups.sequential.length > 0) {
57
+ console.log(`\nTasks sequenciais:`);
58
+ for (const task of groups.sequential) {
59
+ const deps = task.depends_on ? JSON.parse(task.depends_on) : [];
60
+ const depsStr = deps.length > 0 ? ` [apos: ${deps.join(",")}]` : "";
61
+ console.log(` #${task.number}: ${task.name} (${task.agent || "geral"})${depsStr}`);
62
+ }
63
+ }
64
+
65
+ console.log(`\n${"─".repeat(60)}`);
66
+ console.log(`\nPara aprovar: check approve`);
67
+ console.log(`Para rejeitar: check reject "motivo"`);
68
+ console.log(`Para ajustar: plan task-add (adiciona mais tasks)\n`);
69
+ }
70
+
71
+ export function checkApprove(): void {
72
+ initSchema();
73
+ enforceGate("check-approve");
74
+
75
+ const db = getDb();
76
+ const now = new Date().toISOString();
77
+
78
+ const spec = db
79
+ .query("SELECT * FROM specs WHERE phase = 'checking' ORDER BY created_at DESC LIMIT 1")
80
+ .get() as any;
81
+
82
+ // Atualizar para aprovado
83
+ db.run(
84
+ "UPDATE specs SET phase = 'implementing', approved_at = ?, updated_at = ? WHERE id = ?",
85
+ [now, now, spec.id]
86
+ );
87
+
88
+ // Atualizar contexto
89
+ db.run("UPDATE context SET current_task = 0, updated_at = ? WHERE spec_id = ?", [now, spec.id]);
90
+
91
+ const taskCount = db.query("SELECT COUNT(*) as c FROM tasks WHERE spec_id = ?").get(spec.id) as any;
92
+
93
+ console.log(`\nPlano APROVADO!`);
94
+ console.log(`Feature: ${spec.name}`);
95
+ console.log(`Tasks: ${taskCount.c}`);
96
+ console.log(`Fase: implementing`);
97
+ console.log(`\nProximos passos:`);
98
+ console.log(`1. Veja proximas tasks com: task next`);
99
+ console.log(`2. Inicie uma task com: task start <ids>`);
100
+ console.log(`3. Complete uma task com: task done <id> --checkpoint "..."\n`);
101
+ }
102
+
103
+ export function checkReject(reason: string): void {
104
+ initSchema();
105
+
106
+ const db = getDb();
107
+ const now = new Date().toISOString();
108
+
109
+ const spec = db
110
+ .query("SELECT * FROM specs WHERE phase = 'checking' ORDER BY created_at DESC LIMIT 1")
111
+ .get() as any;
112
+
113
+ if (!spec) {
114
+ console.error("\nNenhum plano aguardando aprovacao.\n");
115
+ process.exit(1);
116
+ }
117
+
118
+ // Voltar para planning
119
+ db.run("UPDATE specs SET phase = 'planning', updated_at = ? WHERE id = ?", [now, spec.id]);
120
+
121
+ // Registrar motivo no contexto
122
+ db.run(
123
+ "UPDATE context SET last_checkpoint = ?, updated_at = ? WHERE spec_id = ?",
124
+ [`REJEITADO: ${reason}`, now, spec.id]
125
+ );
126
+
127
+ console.log(`\nPlano REJEITADO.`);
128
+ console.log(`Motivo: ${reason}`);
129
+ console.log(`\nFase retornada para: planning`);
130
+ console.log(`Ajuste o plano e solicite nova aprovacao com: check request\n`);
131
+ }
@@ -0,0 +1,174 @@
1
+ import { getDbPath, getDb } from "../db/connection";
2
+ import { initSchema } from "../db/schema";
3
+ import { existsSync } from "fs";
4
+ import { join, dirname } from "path";
5
+
6
+ interface ClearOptions {
7
+ force?: boolean;
8
+ }
9
+
10
+ /**
11
+ * Limpa apenas as tasks/features, mantendo configurações do projeto
12
+ * Esta é a ÚNICA forma de limpeza disponível (v8.0)
13
+ * A limpeza completa foi removida por ser muito destrutiva
14
+ */
15
+ export function clearTasks(options: ClearOptions = {}): void {
16
+ // Garantir que o schema existe
17
+ initSchema();
18
+ const db = getDb();
19
+
20
+ // Contar o que existe
21
+ const specsCount = (db.query("SELECT COUNT(*) as c FROM specs").get() as any).c;
22
+ const tasksCount = (db.query("SELECT COUNT(*) as c FROM tasks").get() as any).c;
23
+ const decisionsCount = (db.query("SELECT COUNT(*) as c FROM decisions").get() as any).c;
24
+ const knowledgeCount = (db.query("SELECT COUNT(*) as c FROM knowledge").get() as any).c;
25
+ const reasoningCount = (db.query("SELECT COUNT(*) as c FROM reasoning_log").get() as any).c;
26
+
27
+ if (specsCount === 0 && tasksCount === 0) {
28
+ console.log("\nNenhuma feature/task para limpar.\n");
29
+ return;
30
+ }
31
+
32
+ if (!options.force) {
33
+ console.log("\n" + "═".repeat(60));
34
+ console.log("CODEXA WORKFLOW - LIMPEZA DE TASKS");
35
+ console.log("═".repeat(60));
36
+
37
+ console.log("\nO seguinte sera PERMANENTEMENTE removido:\n");
38
+ console.log(` - ${specsCount} feature(s)`);
39
+ console.log(` - ${tasksCount} task(s)`);
40
+ console.log(` - ${decisionsCount} decisao(oes)`);
41
+ console.log(` - ${knowledgeCount} knowledge(s)`);
42
+ console.log(` - ${reasoningCount} raciocinio(s)`);
43
+ console.log(" - Artefatos relacionados");
44
+ console.log(" - Reviews pendentes");
45
+ console.log(" - Snapshots de recovery");
46
+ console.log(" - Knowledge graph (relacoes)");
47
+ console.log(" - Session summaries");
48
+ console.log(" - Gate bypasses");
49
+
50
+ console.log("\n" + "─".repeat(60));
51
+ console.log("\nO seguinte sera MANTIDO:\n");
52
+ console.log(" - Configuracao do projeto (stack)");
53
+ console.log(" - Standards");
54
+ console.log(" - Contexto de produto (PRD)");
55
+ console.log(" - Goals e features do produto");
56
+ console.log(" - Contexto de bibliotecas");
57
+ console.log(" - Implementation patterns");
58
+ console.log(" - Arquivos gerados (standards.md, product-context.md, patterns.md)");
59
+
60
+ console.log("\n" + "─".repeat(60));
61
+ console.log("\nPara confirmar a limpeza, execute:");
62
+ console.log(" bun run .claude/cli/workflow.ts clear --force\n");
63
+
64
+ return;
65
+ }
66
+
67
+ console.log("\n" + "═".repeat(60));
68
+ console.log("LIMPANDO TASKS...");
69
+ console.log("═".repeat(60) + "\n");
70
+
71
+ // Ordem de deleção respeita foreign keys
72
+ // v8.0: Incluindo novas tabelas
73
+ const tablesToClear = [
74
+ "session_summaries",
75
+ "reasoning_log",
76
+ "knowledge_graph",
77
+ "knowledge",
78
+ "gate_bypasses",
79
+ "snapshots",
80
+ "review",
81
+ "artifacts",
82
+ "decisions",
83
+ "tasks",
84
+ "context",
85
+ "specs",
86
+ ];
87
+
88
+ for (const table of tablesToClear) {
89
+ try {
90
+ const result = db.run(`DELETE FROM ${table}`);
91
+ console.log(` Limpo: ${table} (${result.changes} registros)`);
92
+ } catch (err: any) {
93
+ // Tabela pode não existir em bancos antigos
94
+ if (!err.message.includes("no such table")) {
95
+ console.error(` Erro ao limpar ${table}: ${err.message}`);
96
+ }
97
+ }
98
+ }
99
+
100
+ console.log("\n" + "═".repeat(60));
101
+ console.log("TASKS LIMPAS COM SUCESSO");
102
+ console.log("═".repeat(60));
103
+
104
+ console.log("\nConfiguracoes do projeto mantidas.");
105
+ console.log("\nProximo passo:");
106
+ console.log(" /codexa:feature - Iniciar uma nova feature\n");
107
+ }
108
+
109
+ /**
110
+ * Mostra o estado atual do workflow
111
+ */
112
+ export function clearShow(): void {
113
+ const dbPath = getDbPath();
114
+ const codexaDir = dirname(dirname(dbPath));
115
+
116
+ const dbFiles = [
117
+ dbPath,
118
+ `${dbPath}-shm`,
119
+ `${dbPath}-wal`,
120
+ ];
121
+
122
+ const markdownFiles = [
123
+ join(codexaDir, "standards.md"),
124
+ join(codexaDir, "product-context.md"),
125
+ join(codexaDir, "patterns.md"),
126
+ ];
127
+
128
+ console.log("\n" + "═".repeat(60));
129
+ console.log("ESTADO DO WORKFLOW");
130
+ console.log("═".repeat(60) + "\n");
131
+
132
+ console.log("Banco de dados:");
133
+ for (const file of dbFiles) {
134
+ const status = existsSync(file) ? "existe" : "nao existe";
135
+ console.log(` ${file}: ${status}`);
136
+ }
137
+
138
+ console.log("\nArquivos gerados:");
139
+ for (const file of markdownFiles) {
140
+ const status = existsSync(file) ? "existe" : "nao existe";
141
+ console.log(` ${file}: ${status}`);
142
+ }
143
+
144
+ // Mostrar estatísticas do banco se existir
145
+ if (existsSync(dbPath)) {
146
+ try {
147
+ initSchema();
148
+ const db = getDb();
149
+
150
+ console.log("\nEstatisticas:");
151
+
152
+ const specs = (db.query("SELECT COUNT(*) as c FROM specs").get() as any).c;
153
+ const tasks = (db.query("SELECT COUNT(*) as c FROM tasks").get() as any).c;
154
+ const decisions = (db.query("SELECT COUNT(*) as c FROM decisions").get() as any).c;
155
+ const standards = (db.query("SELECT COUNT(*) as c FROM standards").get() as any).c;
156
+ const patterns = (db.query("SELECT COUNT(*) as c FROM implementation_patterns").get() as any).c;
157
+
158
+ console.log(` Features: ${specs}`);
159
+ console.log(` Tasks: ${tasks}`);
160
+ console.log(` Decisions: ${decisions}`);
161
+ console.log(` Standards: ${standards}`);
162
+ console.log(` Patterns: ${patterns}`);
163
+ } catch (err: any) {
164
+ console.log(`\n Erro ao ler estatisticas: ${err.message}`);
165
+ }
166
+ }
167
+
168
+ console.log("\n" + "─".repeat(60));
169
+ console.log("Para limpar tasks (mantendo configs): clear --force\n");
170
+ }
171
+
172
+ // Exportar aliases para compatibilidade
173
+ export { clearTasks as clearWorkflow };
174
+ export { clearTasks as clearTasksOnly };
@@ -0,0 +1,249 @@
1
+ import { getDb } from "../db/connection";
2
+ import { initSchema } from "../db/schema";
3
+
4
+ function getNextDecisionId(specId: string): string {
5
+ const db = getDb();
6
+ const last = db
7
+ .query("SELECT id FROM decisions WHERE spec_id = ? ORDER BY created_at DESC LIMIT 1")
8
+ .get(specId) as any;
9
+
10
+ if (!last) return "DEC-001";
11
+
12
+ const num = parseInt(last.id.replace("DEC-", "")) + 1;
13
+ return `DEC-${num.toString().padStart(3, "0")}`;
14
+ }
15
+
16
+ interface ConflictAnalysis {
17
+ hasConflict: boolean;
18
+ conflictingDecisions: Array<{
19
+ id: string;
20
+ title: string;
21
+ decision: string;
22
+ reason: string;
23
+ }>;
24
+ }
25
+
26
+ // Extrair keywords relevantes de um texto
27
+ function extractKeywords(text: string): string[] {
28
+ const stopWords = new Set([
29
+ "a", "o", "e", "de", "da", "do", "para", "com", "em", "que", "usar",
30
+ "the", "and", "or", "to", "for", "with", "in", "on", "is", "are", "be",
31
+ "will", "should", "must", "can", "use", "using", "como", "ser", "sera"
32
+ ]);
33
+
34
+ return text
35
+ .toLowerCase()
36
+ .replace(/[^\w\s-]/g, " ")
37
+ .split(/\s+/)
38
+ .filter((word) => word.length > 2 && !stopWords.has(word));
39
+ }
40
+
41
+ // Detectar conflitos semanticos entre decisoes
42
+ function detectConflicts(
43
+ newTitle: string,
44
+ newDecision: string,
45
+ existingDecisions: any[]
46
+ ): ConflictAnalysis {
47
+ const conflicts: ConflictAnalysis["conflictingDecisions"] = [];
48
+
49
+ const newKeywords = new Set([
50
+ ...extractKeywords(newTitle),
51
+ ...extractKeywords(newDecision),
52
+ ]);
53
+
54
+ // Padroes de conflito conhecidos
55
+ const conflictPatterns: Array<{ patterns: string[][]; reason: string }> = [
56
+ {
57
+ patterns: [["rest", "api"], ["graphql"]],
58
+ reason: "Conflito de paradigma de API (REST vs GraphQL)",
59
+ },
60
+ {
61
+ patterns: [["jwt", "token"], ["session", "cookie"]],
62
+ reason: "Conflito de estrategia de autenticacao",
63
+ },
64
+ {
65
+ patterns: [["zustand"], ["redux"]],
66
+ reason: "Conflito de biblioteca de estado",
67
+ },
68
+ {
69
+ patterns: [["prisma"], ["drizzle"]],
70
+ reason: "Conflito de ORM",
71
+ },
72
+ {
73
+ patterns: [["mysql"], ["postgres", "postgresql"]],
74
+ reason: "Conflito de banco de dados",
75
+ },
76
+ {
77
+ patterns: [["server", "component"], ["client", "component"]],
78
+ reason: "Conflito de tipo de componente padrao",
79
+ },
80
+ {
81
+ patterns: [["tailwind"], ["styled", "component"]],
82
+ reason: "Conflito de abordagem de styling",
83
+ },
84
+ {
85
+ patterns: [["vitest"], ["jest"]],
86
+ reason: "Conflito de framework de testes",
87
+ },
88
+ ];
89
+
90
+ for (const existing of existingDecisions) {
91
+ const existingKeywords = new Set([
92
+ ...extractKeywords(existing.title),
93
+ ...extractKeywords(existing.decision),
94
+ ]);
95
+
96
+ // Verificar padroes de conflito conhecidos
97
+ for (const { patterns, reason } of conflictPatterns) {
98
+ const [patternA, patternB] = patterns;
99
+
100
+ const newHasA = patternA.some((p) => newKeywords.has(p));
101
+ const newHasB = patternB.some((p) => newKeywords.has(p));
102
+ const existingHasA = patternA.some((p) => existingKeywords.has(p));
103
+ const existingHasB = patternB.some((p) => existingKeywords.has(p));
104
+
105
+ // Conflito: nova decisao menciona A, existente menciona B (ou vice-versa)
106
+ if ((newHasA && existingHasB) || (newHasB && existingHasA)) {
107
+ conflicts.push({
108
+ id: existing.id,
109
+ title: existing.title,
110
+ decision: existing.decision,
111
+ reason,
112
+ });
113
+ break; // Apenas um conflito por decisao existente
114
+ }
115
+ }
116
+
117
+ // Verificar sobreposicao alta de keywords (mesmo topico, decisoes diferentes?)
118
+ if (conflicts.every((c) => c.id !== existing.id)) {
119
+ const intersection = [...newKeywords].filter((k) => existingKeywords.has(k));
120
+ const similarity = intersection.length / Math.max(newKeywords.size, existingKeywords.size);
121
+
122
+ if (similarity > 0.4 && intersection.length >= 3) {
123
+ conflicts.push({
124
+ id: existing.id,
125
+ title: existing.title,
126
+ decision: existing.decision,
127
+ reason: `Alta sobreposicao de topico (${Math.round(similarity * 100)}% similar) - verificar se sao decisoes conflitantes`,
128
+ });
129
+ }
130
+ }
131
+ }
132
+
133
+ return {
134
+ hasConflict: conflicts.length > 0,
135
+ conflictingDecisions: conflicts,
136
+ };
137
+ }
138
+
139
+ export function decide(title: string, decision: string, options: { rationale?: string; force?: boolean }): void {
140
+ initSchema();
141
+
142
+ const db = getDb();
143
+ const now = new Date().toISOString();
144
+
145
+ const spec = db
146
+ .query("SELECT * FROM specs WHERE phase NOT IN ('completed', 'cancelled') ORDER BY created_at DESC LIMIT 1")
147
+ .get() as any;
148
+
149
+ if (!spec) {
150
+ console.error("\nNenhuma feature ativa.\n");
151
+ process.exit(1);
152
+ }
153
+
154
+ // Verificar conflitos com decisoes existentes
155
+ const existingDecisions = db
156
+ .query("SELECT * FROM decisions WHERE spec_id = ? AND status = 'active'")
157
+ .all(spec.id) as any[];
158
+
159
+ if (existingDecisions.length > 0 && !options.force) {
160
+ const analysis = detectConflicts(title, decision, existingDecisions);
161
+
162
+ if (analysis.hasConflict) {
163
+ console.warn("\n⚠ POTENCIAL CONFLITO DETECTADO:\n");
164
+ console.warn(`Nova decisao: "${title}"`);
165
+ console.warn(` ${decision}\n`);
166
+
167
+ for (const conflict of analysis.conflictingDecisions) {
168
+ console.warn(`Pode conflitar com: ${conflict.id} - "${conflict.title}"`);
169
+ console.warn(` ${conflict.decision}`);
170
+ console.warn(` Motivo: ${conflict.reason}\n`);
171
+ }
172
+
173
+ console.warn(`${"─".repeat(50)}`);
174
+ console.warn(`Para registrar mesmo assim, use: decide "${title}" "${decision}" --force`);
175
+ console.warn(`Ou revise as decisoes existentes com: decisions\n`);
176
+ process.exit(1);
177
+ }
178
+ }
179
+
180
+ // Pegar task atual em execucao
181
+ const currentTask = db
182
+ .query("SELECT * FROM tasks WHERE spec_id = ? AND status = 'running' LIMIT 1")
183
+ .get(spec.id) as any;
184
+
185
+ const decisionId = getNextDecisionId(spec.id);
186
+
187
+ db.run(
188
+ `INSERT INTO decisions (id, spec_id, task_ref, title, decision, rationale, status, created_at)
189
+ VALUES (?, ?, ?, ?, ?, ?, 'active', ?)`,
190
+ [
191
+ decisionId,
192
+ spec.id,
193
+ currentTask?.number || null,
194
+ title,
195
+ decision,
196
+ options.rationale || null,
197
+ now,
198
+ ]
199
+ );
200
+
201
+ console.log(`\nDecisao registrada: ${decisionId}`);
202
+ console.log(`Titulo: ${title}`);
203
+ console.log(`Decisao: ${decision}`);
204
+ if (options.rationale) console.log(`Racional: ${options.rationale}`);
205
+ if (currentTask) console.log(`Task: #${currentTask.number}`);
206
+ if (options.force) console.log(`[!] Registrada com --force (conflito ignorado)`);
207
+ console.log();
208
+ }
209
+
210
+ export function listDecisions(json: boolean = false): void {
211
+ initSchema();
212
+
213
+ const db = getDb();
214
+
215
+ const spec = db
216
+ .query("SELECT * FROM specs WHERE phase NOT IN ('completed', 'cancelled') ORDER BY created_at DESC LIMIT 1")
217
+ .get() as any;
218
+
219
+ if (!spec) {
220
+ console.error("\nNenhuma feature ativa.\n");
221
+ process.exit(1);
222
+ }
223
+
224
+ const decisions = db
225
+ .query("SELECT * FROM decisions WHERE spec_id = ? ORDER BY created_at")
226
+ .all(spec.id) as any[];
227
+
228
+ if (json) {
229
+ console.log(JSON.stringify({ decisions }));
230
+ return;
231
+ }
232
+
233
+ if (decisions.length === 0) {
234
+ console.log("\nNenhuma decisao registrada.\n");
235
+ return;
236
+ }
237
+
238
+ console.log(`\nDecisoes (${decisions.length}):`);
239
+ console.log(`${"─".repeat(60)}`);
240
+
241
+ for (const dec of decisions) {
242
+ const taskRef = dec.task_ref ? ` [Task #${dec.task_ref}]` : "";
243
+ const status = dec.status === "active" ? "" : ` (${dec.status})`;
244
+ console.log(`${dec.id}: ${dec.title}${taskRef}${status}`);
245
+ console.log(` ${dec.decision}`);
246
+ if (dec.rationale) console.log(` Racional: ${dec.rationale}`);
247
+ console.log();
248
+ }
249
+ }