@codexa/cli 9.0.2 → 9.0.4
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 +75 -17
- package/commands/check.ts +7 -17
- package/commands/clear.ts +40 -1
- package/commands/decide.ts +37 -49
- package/commands/discover.ts +136 -28
- package/commands/knowledge.test.ts +160 -0
- package/commands/knowledge.ts +192 -102
- package/commands/patterns.test.ts +169 -0
- package/commands/patterns.ts +6 -13
- package/commands/plan.test.ts +73 -0
- package/commands/plan.ts +18 -66
- package/commands/product.ts +8 -17
- package/commands/research.ts +4 -3
- package/commands/review.ts +190 -28
- package/commands/spec-resolver.test.ts +119 -0
- package/commands/spec-resolver.ts +90 -0
- package/commands/standards.ts +7 -15
- package/commands/sync.ts +89 -0
- package/commands/task.ts +72 -167
- package/commands/utils.test.ts +100 -0
- package/commands/utils.ts +78 -706
- package/db/schema.test.ts +760 -0
- package/db/schema.ts +284 -130
- package/gates/validator.test.ts +675 -0
- package/gates/validator.ts +112 -27
- 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 +19 -1
- package/workflow.ts +176 -67
package/commands/architect.ts
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { getDb } from "../db/connection";
|
|
2
2
|
import { initSchema } from "../db/schema";
|
|
3
3
|
import { existsSync, mkdirSync, writeFileSync, readFileSync } from "fs";
|
|
4
|
+
import { CodexaError } from "../errors";
|
|
4
5
|
import { join } from "path";
|
|
5
6
|
|
|
6
7
|
// ═══════════════════════════════════════════════════════════════
|
|
7
8
|
// TYPES
|
|
8
9
|
// ═══════════════════════════════════════════════════════════════
|
|
9
10
|
|
|
10
|
-
interface BabyStep {
|
|
11
|
+
export interface BabyStep {
|
|
11
12
|
number: number;
|
|
12
13
|
name: string;
|
|
13
14
|
what: string;
|
|
@@ -18,7 +19,7 @@ interface BabyStep {
|
|
|
18
19
|
dependsOn?: number[];
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
interface Risk {
|
|
22
|
+
export interface Risk {
|
|
22
23
|
id: string;
|
|
23
24
|
description: string;
|
|
24
25
|
probability: "low" | "medium" | "high";
|
|
@@ -35,7 +36,7 @@ interface Alternative {
|
|
|
35
36
|
whyDiscarded?: string;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
interface ArchitecturalDecision {
|
|
39
|
+
export interface ArchitecturalDecision {
|
|
39
40
|
decision: string;
|
|
40
41
|
rationale: string;
|
|
41
42
|
}
|
|
@@ -103,23 +104,80 @@ function formatDate(date: Date): string {
|
|
|
103
104
|
}
|
|
104
105
|
|
|
105
106
|
// ═══════════════════════════════════════════════════════════════
|
|
106
|
-
// MARKDOWN PARSER (v8.4)
|
|
107
|
+
// MARKDOWN PARSER (v8.4 + v9.0 tolerant matching)
|
|
107
108
|
// ═══════════════════════════════════════════════════════════════
|
|
108
109
|
|
|
109
|
-
|
|
110
|
-
|
|
110
|
+
const HEADER_ALIASES: Record<string, string[]> = {
|
|
111
|
+
"Contexto e Entendimento": [
|
|
112
|
+
"contexto e entendimento", "context and understanding", "contexto",
|
|
113
|
+
"entendimento do problema", "understanding", "background",
|
|
114
|
+
],
|
|
115
|
+
"Stack e Arquitetura Atual": [
|
|
116
|
+
"stack e arquitetura atual", "stack e arquitetura", "current architecture",
|
|
117
|
+
"arquitetura atual", "stack atual", "tech stack",
|
|
118
|
+
],
|
|
119
|
+
"Solucao Proposta": [
|
|
120
|
+
"solucao proposta", "proposed solution", "abordagem proposta",
|
|
121
|
+
"proposta", "solution", "approach",
|
|
122
|
+
],
|
|
123
|
+
"Diagramas": [
|
|
124
|
+
"diagramas", "diagrams", "diagramas mermaid", "architecture diagrams",
|
|
125
|
+
],
|
|
126
|
+
"Baby Steps": [
|
|
127
|
+
"baby steps", "passos", "steps", "plano de implementacao",
|
|
128
|
+
"implementation plan", "implementation steps", "etapas",
|
|
129
|
+
],
|
|
130
|
+
"Riscos e Mitigacoes": [
|
|
131
|
+
"riscos e mitigacoes", "risks and mitigations", "riscos",
|
|
132
|
+
"risks", "analise de riscos", "risk analysis",
|
|
133
|
+
],
|
|
134
|
+
"Alternativas Descartadas": [
|
|
135
|
+
"alternativas descartadas", "alternativas", "alternatives",
|
|
136
|
+
"discarded alternatives", "alternativas consideradas",
|
|
137
|
+
],
|
|
138
|
+
"Decisoes Arquiteturais": [
|
|
139
|
+
"decisoes arquiteturais", "architectural decisions",
|
|
140
|
+
"decisoes", "decisions",
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export function extractSection(content: string, header: string): string {
|
|
111
145
|
const sections = content.split(/^## /m);
|
|
146
|
+
|
|
147
|
+
// Tier 1: Exact startsWith (fast path)
|
|
112
148
|
for (const section of sections) {
|
|
113
149
|
if (section.startsWith(header)) {
|
|
114
|
-
// Remover o header e retornar o conteudo
|
|
115
150
|
const lines = section.split("\n");
|
|
116
151
|
return lines.slice(1).join("\n").trim();
|
|
117
152
|
}
|
|
118
153
|
}
|
|
154
|
+
|
|
155
|
+
// Tier 2: Case-insensitive startsWith
|
|
156
|
+
const headerLower = header.toLowerCase();
|
|
157
|
+
for (const section of sections) {
|
|
158
|
+
if (section.toLowerCase().startsWith(headerLower)) {
|
|
159
|
+
const lines = section.split("\n");
|
|
160
|
+
return lines.slice(1).join("\n").trim();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Tier 3: Alias-based includes matching (case-insensitive)
|
|
165
|
+
const aliases = HEADER_ALIASES[header] || [];
|
|
166
|
+
for (const section of sections) {
|
|
167
|
+
const sectionHeader = section.split("\n")[0]?.toLowerCase().trim() || "";
|
|
168
|
+
for (const alias of aliases) {
|
|
169
|
+
if (sectionHeader.includes(alias)) {
|
|
170
|
+
const lines = section.split("\n");
|
|
171
|
+
return lines.slice(1).join("\n").trim();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
console.warn(`[architect] Secao "${header}" nao encontrada no .md`);
|
|
119
177
|
return "";
|
|
120
178
|
}
|
|
121
179
|
|
|
122
|
-
function parseBabySteps(section: string): BabyStep[] {
|
|
180
|
+
export function parseBabySteps(section: string): BabyStep[] {
|
|
123
181
|
if (!section) return [];
|
|
124
182
|
const steps: BabyStep[] = [];
|
|
125
183
|
// Match "### N. Name" or "### Step N: Name"
|
|
@@ -152,7 +210,7 @@ function parseBabySteps(section: string): BabyStep[] {
|
|
|
152
210
|
return steps;
|
|
153
211
|
}
|
|
154
212
|
|
|
155
|
-
function parseRisks(section: string): Risk[] {
|
|
213
|
+
export function parseRisks(section: string): Risk[] {
|
|
156
214
|
if (!section) return [];
|
|
157
215
|
const risks: Risk[] = [];
|
|
158
216
|
const riskBlocks = section.split(/^###\s+/m).filter(Boolean);
|
|
@@ -173,7 +231,7 @@ function parseRisks(section: string): Risk[] {
|
|
|
173
231
|
return risks;
|
|
174
232
|
}
|
|
175
233
|
|
|
176
|
-
function parseDiagrams(section: string): { name: string; type: string; content: string }[] {
|
|
234
|
+
export function parseDiagrams(section: string): { name: string; type: string; content: string }[] {
|
|
177
235
|
if (!section) return [];
|
|
178
236
|
const diagrams: { name: string; type: string; content: string }[] = [];
|
|
179
237
|
const diagramRegex = /###\s+(.+)\n[\s\S]*?```mermaid\n([\s\S]*?)```/g;
|
|
@@ -192,7 +250,7 @@ function parseDiagrams(section: string): { name: string; type: string; content:
|
|
|
192
250
|
return diagrams;
|
|
193
251
|
}
|
|
194
252
|
|
|
195
|
-
function parseDecisionsTable(section: string): ArchitecturalDecision[] {
|
|
253
|
+
export function parseDecisionsTable(section: string): ArchitecturalDecision[] {
|
|
196
254
|
if (!section) return [];
|
|
197
255
|
const decisions: ArchitecturalDecision[] = [];
|
|
198
256
|
const lines = section.split("\n");
|
|
@@ -307,7 +365,7 @@ export function architectStart(description: string, options: { json?: boolean }
|
|
|
307
365
|
console.log(` Criada: ${pending.created_at}`);
|
|
308
366
|
console.log("\nUse 'architect show' para ver detalhes ou 'architect cancel' para cancelar.\n");
|
|
309
367
|
}
|
|
310
|
-
|
|
368
|
+
throw new CodexaError("Ja existe uma analise pendente. Use 'architect show' ou 'architect cancel'.");
|
|
311
369
|
}
|
|
312
370
|
|
|
313
371
|
// Criar nova analise
|
|
@@ -509,7 +567,7 @@ export function architectSave(options: { file?: string; json?: boolean }): void
|
|
|
509
567
|
} else {
|
|
510
568
|
console.log("\n[ERRO] Nenhuma analise pendente encontrada.\n");
|
|
511
569
|
}
|
|
512
|
-
|
|
570
|
+
throw new CodexaError("Nenhuma analise pendente encontrada.");
|
|
513
571
|
}
|
|
514
572
|
|
|
515
573
|
// v8.4: Resolver caminho do arquivo .md
|
|
@@ -530,7 +588,7 @@ export function architectSave(options: { file?: string; json?: boolean }): void
|
|
|
530
588
|
console.log(`\n[ERRO] Arquivo nao encontrado: ${filePath}`);
|
|
531
589
|
console.log("O agente architect deve escrever o .md antes de chamar 'architect save'.\n");
|
|
532
590
|
}
|
|
533
|
-
|
|
591
|
+
throw new CodexaError(`Arquivo nao encontrado: ${filePath}`);
|
|
534
592
|
}
|
|
535
593
|
|
|
536
594
|
const content = readFileSync(filePath, "utf-8");
|
|
@@ -640,7 +698,7 @@ export function architectApprove(options: { id?: string; json?: boolean }): void
|
|
|
640
698
|
} else {
|
|
641
699
|
console.log("\n[ERRO] Analise nao encontrada.\n");
|
|
642
700
|
}
|
|
643
|
-
|
|
701
|
+
throw new CodexaError("Analise nao encontrada.");
|
|
644
702
|
}
|
|
645
703
|
|
|
646
704
|
if (analysis.status !== "pending") {
|
|
@@ -649,7 +707,7 @@ export function architectApprove(options: { id?: string; json?: boolean }): void
|
|
|
649
707
|
} else {
|
|
650
708
|
console.log(`\n[ERRO] Analise ja esta com status '${analysis.status}'.\n`);
|
|
651
709
|
}
|
|
652
|
-
|
|
710
|
+
throw new CodexaError(`Analise ja esta com status '${analysis.status}'.`);
|
|
653
711
|
}
|
|
654
712
|
|
|
655
713
|
db.run(
|
|
@@ -693,7 +751,7 @@ export function architectExport(options: { id?: string; json?: boolean }): void
|
|
|
693
751
|
console.log("\n[ERRO] Nenhuma analise aprovada encontrada.");
|
|
694
752
|
console.log("Use 'architect approve' primeiro.\n");
|
|
695
753
|
}
|
|
696
|
-
|
|
754
|
+
throw new CodexaError("Nenhuma analise aprovada encontrada. Use 'architect approve' primeiro.");
|
|
697
755
|
}
|
|
698
756
|
|
|
699
757
|
const babySteps: BabyStep[] = analysis.baby_steps ? JSON.parse(analysis.baby_steps) : [];
|
package/commands/check.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { getDb } from "../db/connection";
|
|
2
2
|
import { initSchema } from "../db/schema";
|
|
3
3
|
import { enforceGate } from "../gates/validator";
|
|
4
|
+
import { resolveSpec } from "./spec-resolver";
|
|
4
5
|
|
|
5
|
-
export function checkRequest(): void {
|
|
6
|
+
export function checkRequest(specId?: string): void {
|
|
6
7
|
initSchema();
|
|
7
8
|
enforceGate("check-request");
|
|
8
9
|
|
|
9
10
|
const db = getDb();
|
|
10
|
-
const spec =
|
|
11
|
-
.query("SELECT * FROM specs WHERE phase NOT IN ('completed', 'cancelled') ORDER BY created_at DESC LIMIT 1")
|
|
12
|
-
.get() as any;
|
|
11
|
+
const spec = resolveSpec(specId, ["planning"]);
|
|
13
12
|
|
|
14
13
|
const tasks = db
|
|
15
14
|
.query("SELECT * FROM tasks WHERE spec_id = ? ORDER BY number")
|
|
@@ -68,16 +67,14 @@ export function checkRequest(): void {
|
|
|
68
67
|
console.log(`Para ajustar: plan task-add (adiciona mais tasks)\n`);
|
|
69
68
|
}
|
|
70
69
|
|
|
71
|
-
export function checkApprove(): void {
|
|
70
|
+
export function checkApprove(specId?: string): void {
|
|
72
71
|
initSchema();
|
|
73
72
|
enforceGate("check-approve");
|
|
74
73
|
|
|
75
74
|
const db = getDb();
|
|
76
75
|
const now = new Date().toISOString();
|
|
77
76
|
|
|
78
|
-
const spec =
|
|
79
|
-
.query("SELECT * FROM specs WHERE phase = 'checking' ORDER BY created_at DESC LIMIT 1")
|
|
80
|
-
.get() as any;
|
|
77
|
+
const spec = resolveSpec(specId, ["checking"]);
|
|
81
78
|
|
|
82
79
|
// Atualizar para aprovado
|
|
83
80
|
db.run(
|
|
@@ -100,20 +97,13 @@ export function checkApprove(): void {
|
|
|
100
97
|
console.log(`3. Complete uma task com: task done <id> --checkpoint "..."\n`);
|
|
101
98
|
}
|
|
102
99
|
|
|
103
|
-
export function checkReject(reason: string): void {
|
|
100
|
+
export function checkReject(reason: string, specId?: string): void {
|
|
104
101
|
initSchema();
|
|
105
102
|
|
|
106
103
|
const db = getDb();
|
|
107
104
|
const now = new Date().toISOString();
|
|
108
105
|
|
|
109
|
-
const spec =
|
|
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
|
-
}
|
|
106
|
+
const spec = resolveSpec(specId, ["checking"]);
|
|
117
107
|
|
|
118
108
|
// Voltar para planning
|
|
119
109
|
db.run("UPDATE specs SET phase = 'planning', updated_at = ? WHERE id = ?", [now, spec.id]);
|
package/commands/clear.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { join, dirname } from "path";
|
|
|
5
5
|
|
|
6
6
|
interface ClearOptions {
|
|
7
7
|
force?: boolean;
|
|
8
|
+
specId?: string;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -64,6 +65,45 @@ export function clearTasks(options: ClearOptions = {}): void {
|
|
|
64
65
|
return;
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
// Per-spec clear or clear all
|
|
69
|
+
if (options.specId) {
|
|
70
|
+
console.log("\n" + "═".repeat(60));
|
|
71
|
+
console.log(`LIMPANDO SPEC: ${options.specId}`);
|
|
72
|
+
console.log("═".repeat(60) + "\n");
|
|
73
|
+
|
|
74
|
+
const tablesWithSpecId = [
|
|
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 tablesWithSpecId) {
|
|
89
|
+
try {
|
|
90
|
+
const result = db.run(`DELETE FROM ${table} WHERE spec_id = ?`, [options.specId]);
|
|
91
|
+
if (result.changes > 0) {
|
|
92
|
+
console.log(` Limpo: ${table} (${result.changes} registros)`);
|
|
93
|
+
}
|
|
94
|
+
} catch (err: any) {
|
|
95
|
+
if (!err.message.includes("no such table") && !err.message.includes("no such column")) {
|
|
96
|
+
console.error(` Erro ao limpar ${table}: ${err.message}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
console.log("\n" + "═".repeat(60));
|
|
102
|
+
console.log(`SPEC ${options.specId} LIMPO`);
|
|
103
|
+
console.log("═".repeat(60) + "\n");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
67
107
|
console.log("\n" + "═".repeat(60));
|
|
68
108
|
console.log("LIMPANDO TASKS...");
|
|
69
109
|
console.log("═".repeat(60) + "\n");
|
|
@@ -71,7 +111,6 @@ export function clearTasks(options: ClearOptions = {}): void {
|
|
|
71
111
|
// Ordem de deleção respeita foreign keys
|
|
72
112
|
// v8.0: Incluindo novas tabelas
|
|
73
113
|
const tablesToClear = [
|
|
74
|
-
"session_summaries",
|
|
75
114
|
"reasoning_log",
|
|
76
115
|
"knowledge_graph",
|
|
77
116
|
"knowledge",
|
package/commands/decide.ts
CHANGED
|
@@ -1,17 +1,7 @@
|
|
|
1
1
|
import { getDb } from "../db/connection";
|
|
2
|
-
import { initSchema } from "../db/schema";
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
}
|
|
2
|
+
import { initSchema, getNextDecisionId } from "../db/schema";
|
|
3
|
+
import { resolveSpec } from "./spec-resolver";
|
|
4
|
+
import { CodexaError } from "../errors";
|
|
15
5
|
|
|
16
6
|
interface ConflictAnalysis {
|
|
17
7
|
hasConflict: boolean;
|
|
@@ -136,20 +126,13 @@ function detectConflicts(
|
|
|
136
126
|
};
|
|
137
127
|
}
|
|
138
128
|
|
|
139
|
-
export function decide(title: string, decision: string, options: { rationale?: string; force?: boolean }): void {
|
|
129
|
+
export function decide(title: string, decision: string, options: { rationale?: string; force?: boolean; specId?: string }): void {
|
|
140
130
|
initSchema();
|
|
141
131
|
|
|
142
132
|
const db = getDb();
|
|
143
133
|
const now = new Date().toISOString();
|
|
144
134
|
|
|
145
|
-
const spec =
|
|
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
|
-
}
|
|
135
|
+
const spec = resolveSpec(options.specId);
|
|
153
136
|
|
|
154
137
|
// Verificar conflitos com decisoes existentes
|
|
155
138
|
const existingDecisions = db
|
|
@@ -171,9 +154,9 @@ export function decide(title: string, decision: string, options: { rationale?: s
|
|
|
171
154
|
}
|
|
172
155
|
|
|
173
156
|
console.warn(`${"─".repeat(50)}`);
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
157
|
+
throw new CodexaError(
|
|
158
|
+
`Potencial conflito detectado.\nPara registrar mesmo assim, use: decide "${title}" "${decision}" --force\nOu revise as decisoes existentes com: decisions`
|
|
159
|
+
);
|
|
177
160
|
}
|
|
178
161
|
}
|
|
179
162
|
|
|
@@ -182,21 +165,33 @@ export function decide(title: string, decision: string, options: { rationale?: s
|
|
|
182
165
|
.query("SELECT * FROM tasks WHERE spec_id = ? AND status = 'running' LIMIT 1")
|
|
183
166
|
.get(spec.id) as any;
|
|
184
167
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
168
|
+
let decisionId = "";
|
|
169
|
+
let retries = 3;
|
|
170
|
+
while (retries > 0) {
|
|
171
|
+
decisionId = getNextDecisionId(spec.id);
|
|
172
|
+
try {
|
|
173
|
+
db.run(
|
|
174
|
+
`INSERT INTO decisions (id, spec_id, task_ref, title, decision, rationale, status, created_at)
|
|
175
|
+
VALUES (?, ?, ?, ?, ?, ?, 'active', ?)`,
|
|
176
|
+
[
|
|
177
|
+
decisionId,
|
|
178
|
+
spec.id,
|
|
179
|
+
currentTask?.number || null,
|
|
180
|
+
title,
|
|
181
|
+
decision,
|
|
182
|
+
options.rationale || null,
|
|
183
|
+
now,
|
|
184
|
+
]
|
|
185
|
+
);
|
|
186
|
+
break;
|
|
187
|
+
} catch (e: any) {
|
|
188
|
+
if (e.message?.includes("UNIQUE constraint") && retries > 1) {
|
|
189
|
+
retries--;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
throw e;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
200
195
|
|
|
201
196
|
console.log(`\nDecisao registrada: ${decisionId}`);
|
|
202
197
|
console.log(`Titulo: ${title}`);
|
|
@@ -207,19 +202,12 @@ export function decide(title: string, decision: string, options: { rationale?: s
|
|
|
207
202
|
console.log();
|
|
208
203
|
}
|
|
209
204
|
|
|
210
|
-
export function listDecisions(json: boolean = false): void {
|
|
205
|
+
export function listDecisions(json: boolean = false, specId?: string): void {
|
|
211
206
|
initSchema();
|
|
212
207
|
|
|
213
208
|
const db = getDb();
|
|
214
209
|
|
|
215
|
-
const spec =
|
|
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
|
-
}
|
|
210
|
+
const spec = resolveSpec(specId);
|
|
223
211
|
|
|
224
212
|
const decisions = db
|
|
225
213
|
.query("SELECT * FROM decisions WHERE spec_id = ? ORDER BY created_at")
|