@codexa/cli 9.0.7 → 9.0.8

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,53 +27,6 @@ interface ProductFeature {
27
27
  priority: "high" | "medium" | "low";
28
28
  }
29
29
 
30
- // ═══════════════════════════════════════════════════════════════
31
- // PRODUCT GUIDE - Inicia o processo guiado
32
- // ═══════════════════════════════════════════════════════════════
33
-
34
- export function productGuide(json: boolean = false): void {
35
- initSchema();
36
- const db = getDb();
37
-
38
- // Verificar se ja existe
39
- const existing = db.query("SELECT * FROM product_context WHERE id = 'default'").get();
40
- if (existing) {
41
- console.log("\nContexto de produto ja definido.");
42
- console.log("Use: product show para ver detalhes");
43
- console.log("Ou: product reset para refazer\n");
44
- return;
45
- }
46
-
47
- if (json) {
48
- console.log(JSON.stringify({ status: "ready_for_guide", message: "Use AskUserQuestion para guiar o usuario" }, null, 2));
49
- return;
50
- }
51
-
52
- console.log("\n" + "═".repeat(60));
53
- console.log("PRODUCT DISCOVERY - Modo Guiado");
54
- console.log("═".repeat(60));
55
- console.log(`
56
- Este modo vai ajudar a definir o contexto do produto atraves de perguntas.
57
-
58
- O agente vai usar AskUserQuestion para coletar:
59
- 1. Nome do produto
60
- 2. Problema que resolve
61
- 3. Solucao proposta
62
- 4. Usuarios alvo
63
- 5. Proposta de valor
64
- 6. Metricas de sucesso
65
- 7. O que esta fora do escopo
66
- 8. Restricoes conhecidas
67
-
68
- Apos coletar as respostas, use:
69
- product set --name "..." --problem "..." ...
70
-
71
- Para confirmar e salvar:
72
- product confirm
73
- `);
74
- console.log("─".repeat(60) + "\n");
75
- }
76
-
77
30
  // ═══════════════════════════════════════════════════════════════
78
31
  // PRODUCT IMPORT - Importa PRD existente
79
32
  // ═══════════════════════════════════════════════════════════════
@@ -142,6 +95,8 @@ export function productSet(options: {
142
95
  successMetrics?: string;
143
96
  outOfScope?: string;
144
97
  constraints?: string;
98
+ goal?: string;
99
+ feature?: string;
145
100
  }): void {
146
101
  initSchema();
147
102
  const db = getDb();
@@ -208,6 +163,42 @@ export function productSet(options: {
208
163
  );
209
164
  }
210
165
 
166
+ // Adicionar objetivo (formato: "category:text:priority")
167
+ if (options.goal) {
168
+ const parts = options.goal.split(":");
169
+ const category = parts[0] || "geral";
170
+ const goal = parts[1] || options.goal;
171
+ const priority = parts[2] || "medium";
172
+
173
+ db.run(
174
+ `INSERT INTO product_goals (product_id, category, goal, priority, created_at)
175
+ VALUES ('pending', ?, ?, ?, ?)`,
176
+ [category, goal, priority, now]
177
+ );
178
+
179
+ const count = db.query("SELECT COUNT(*) as c FROM product_goals WHERE product_id = 'pending'").get() as any;
180
+ console.log(`\nObjetivo adicionado (${count.c} total)`);
181
+ console.log(` [${category}] ${goal} (${priority})`);
182
+ }
183
+
184
+ // Adicionar feature (formato: "name:description:priority")
185
+ if (options.feature) {
186
+ const parts = options.feature.split(":");
187
+ const name = parts[0];
188
+ const description = parts[1] || "";
189
+ const priority = parts[2] || "medium";
190
+
191
+ db.run(
192
+ `INSERT INTO product_features (product_id, name, description, priority, created_at)
193
+ VALUES ('pending', ?, ?, ?, ?)`,
194
+ [name, description, priority, now]
195
+ );
196
+
197
+ const count = db.query("SELECT COUNT(*) as c FROM product_features WHERE product_id = 'pending'").get() as any;
198
+ console.log(`\nFeature adicionada (${count.c} total)`);
199
+ console.log(` ${name} (${priority})`);
200
+ }
201
+
211
202
  // Mostrar estado atual
212
203
  const current = db.query("SELECT * FROM product_context WHERE id = 'pending'").get() as any;
213
204
 
@@ -221,60 +212,6 @@ export function productSet(options: {
221
212
  console.log("\nPara confirmar: product confirm\n");
222
213
  }
223
214
 
224
- // ═══════════════════════════════════════════════════════════════
225
- // PRODUCT GOAL ADD - Adiciona objetivo
226
- // ═══════════════════════════════════════════════════════════════
227
-
228
- export function productGoalAdd(options: {
229
- category: string;
230
- goal: string;
231
- priority?: string;
232
- }): void {
233
- initSchema();
234
- const db = getDb();
235
-
236
- const now = new Date().toISOString();
237
- const priority = options.priority || "medium";
238
-
239
- db.run(
240
- `INSERT INTO product_goals (product_id, category, goal, priority, created_at)
241
- VALUES ('pending', ?, ?, ?, ?)`,
242
- [options.category, options.goal, priority, now]
243
- );
244
-
245
- const count = db.query("SELECT COUNT(*) as c FROM product_goals WHERE product_id = 'pending'").get() as any;
246
-
247
- console.log(`\nObjetivo adicionado (${count.c} total)`);
248
- console.log(` [${options.category}] ${options.goal} (${priority})\n`);
249
- }
250
-
251
- // ═══════════════════════════════════════════════════════════════
252
- // PRODUCT FEATURE ADD - Adiciona feature core
253
- // ═══════════════════════════════════════════════════════════════
254
-
255
- export function productFeatureAdd(options: {
256
- name: string;
257
- description?: string;
258
- priority?: string;
259
- }): void {
260
- initSchema();
261
- const db = getDb();
262
-
263
- const now = new Date().toISOString();
264
- const priority = options.priority || "medium";
265
-
266
- db.run(
267
- `INSERT INTO product_features (product_id, name, description, priority, created_at)
268
- VALUES ('pending', ?, ?, ?, ?)`,
269
- [options.name, options.description || "", priority, now]
270
- );
271
-
272
- const count = db.query("SELECT COUNT(*) as c FROM product_features WHERE product_id = 'pending'").get() as any;
273
-
274
- console.log(`\nFeature adicionada (${count.c} total)`);
275
- console.log(` ${options.name} (${priority})\n`);
276
- }
277
-
278
215
  // ═══════════════════════════════════════════════════════════════
279
216
  // PRODUCT CONFIRM - Confirma e salva
280
217
  // ═══════════════════════════════════════════════════════════════
@@ -285,7 +222,7 @@ export function productConfirm(): void {
285
222
 
286
223
  const pending = db.query("SELECT * FROM product_context WHERE id = 'pending'").get() as any;
287
224
  if (!pending) {
288
- throw new CodexaError("Nenhum contexto de produto pendente.\nExecute: product guide ou product import primeiro");
225
+ throw new CodexaError("Nenhum contexto de produto pendente.\nExecute: product import primeiro");
289
226
  }
290
227
 
291
228
  // Validar campos obrigatorios
@@ -353,7 +290,7 @@ export function productShow(options: { json?: boolean; pending?: boolean } = {})
353
290
  if (options.pending) {
354
291
  throw new CodexaError("Nenhum contexto pendente.");
355
292
  } else {
356
- throw new CodexaError("Contexto de produto nao definido.\nExecute: product guide ou product import");
293
+ throw new CodexaError("Contexto de produto nao definido.\nExecute: product import para definir");
357
294
  }
358
295
  }
359
296
 
@@ -481,7 +418,7 @@ export function productReset(): void {
481
418
  db.run("DELETE FROM product_context");
482
419
 
483
420
  console.log("\nContexto de produto resetado.");
484
- console.log("Execute: product guide ou product import para refazer\n");
421
+ console.log("Execute: product import para refazer\n");
485
422
  }
486
423
 
487
424
  // ═══════════════════════════════════════════════════════════════
@@ -2,22 +2,11 @@ import { describe, it, expect, beforeEach } from "bun:test";
2
2
  import { resolveSpec, getAllActiveSpecs } from "./spec-resolver";
3
3
  import { getDb } from "../db/connection";
4
4
  import { initSchema } from "../db/schema";
5
+ import { cleanDb } from "../db/test-helpers";
5
6
 
6
7
  beforeEach(() => {
7
8
  initSchema();
8
- const db = getDb();
9
- // Limpar em ordem que respeita foreign keys
10
- db.run("DELETE FROM reasoning_log");
11
- db.run("DELETE FROM knowledge_graph");
12
- db.run("DELETE FROM knowledge");
13
- db.run("DELETE FROM gate_bypasses");
14
- db.run("DELETE FROM snapshots");
15
- db.run("DELETE FROM review");
16
- db.run("DELETE FROM artifacts");
17
- db.run("DELETE FROM decisions");
18
- db.run("DELETE FROM tasks");
19
- db.run("DELETE FROM context");
20
- db.run("DELETE FROM specs");
9
+ cleanDb();
21
10
  });
22
11
 
23
12
  function createSpec(id: string, name: string, phase: string, createdAt?: string) {
@@ -48,6 +48,9 @@ export function standardsList(options: { category?: string; scope?: string; json
48
48
 
49
49
  const enforcement = std.enforcement === "required" ? "[REQ]" : "[REC]";
50
50
  console.log(` #${std.id} ${enforcement} (${std.scope}) ${std.rule}`);
51
+ if (std.semantic_query) {
52
+ console.log(` Semantic: "${std.semantic_query}" (expect: ${std.expect || "no_match"})`);
53
+ }
51
54
  }
52
55
 
53
56
  console.log("\n" + "─".repeat(60));
@@ -61,6 +64,8 @@ export function standardsAdd(options: {
61
64
  examples?: string;
62
65
  antiExamples?: string;
63
66
  enforcement?: string;
67
+ semanticQuery?: string;
68
+ expect?: string;
64
69
  }): void {
65
70
  initSchema();
66
71
  const db = getDb();
@@ -82,6 +87,12 @@ export function standardsAdd(options: {
82
87
  throw new ValidationError(`Enforcement invalido: ${enforcement}\nValidos: ${validEnforcement.join(", ")}`);
83
88
  }
84
89
 
90
+ const expectVal = options.expect || "no_match";
91
+ const validExpects = ["no_match", "must_match"];
92
+ if (!validExpects.includes(expectVal)) {
93
+ throw new ValidationError(`Expect invalido: ${expectVal}\nValidos: ${validExpects.join(", ")}`);
94
+ }
95
+
85
96
  let examples = null;
86
97
  let antiExamples = null;
87
98
 
@@ -94,9 +105,9 @@ export function standardsAdd(options: {
94
105
  }
95
106
 
96
107
  const result = db.run(
97
- `INSERT INTO standards (category, scope, rule, examples, anti_examples, enforcement, source, created_at)
98
- VALUES (?, ?, ?, ?, ?, ?, 'user', datetime('now'))`,
99
- [options.category, options.scope, options.rule, examples, antiExamples, enforcement]
108
+ `INSERT INTO standards (category, scope, rule, examples, anti_examples, enforcement, semantic_query, expect, source, created_at)
109
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, 'user', datetime('now'))`,
110
+ [options.category, options.scope, options.rule, examples, antiExamples, enforcement, options.semanticQuery || null, expectVal]
100
111
  );
101
112
 
102
113
  // Regenerar markdown
@@ -107,6 +118,9 @@ export function standardsAdd(options: {
107
118
  console.log(` Escopo: ${options.scope}`);
108
119
  console.log(` Regra: ${options.rule}`);
109
120
  console.log(` Enforcement: ${enforcement}`);
121
+ if (options.semanticQuery) {
122
+ console.log(` Semantic Query: "${options.semanticQuery}" (expect: ${expectVal})`);
123
+ }
110
124
  console.log("\nArquivo atualizado: .codexa/standards.md\n");
111
125
  }
112
126
 
@@ -117,6 +131,8 @@ export function standardsEdit(
117
131
  examples?: string;
118
132
  antiExamples?: string;
119
133
  enforcement?: string;
134
+ semanticQuery?: string;
135
+ expect?: string;
120
136
  }
121
137
  ): void {
122
138
  initSchema();
@@ -152,6 +168,20 @@ export function standardsEdit(
152
168
  params.push(options.enforcement);
153
169
  }
154
170
 
171
+ if (options.semanticQuery !== undefined) {
172
+ updates.push("semantic_query = ?");
173
+ params.push(options.semanticQuery || null);
174
+ }
175
+
176
+ if (options.expect) {
177
+ const validExpects = ["no_match", "must_match"];
178
+ if (!validExpects.includes(options.expect)) {
179
+ throw new ValidationError(`Expect invalido: ${options.expect}\nValidos: ${validExpects.join(", ")}`);
180
+ }
181
+ updates.push("expect = ?");
182
+ params.push(options.expect);
183
+ }
184
+
155
185
  if (updates.length === 0) {
156
186
  throw new ValidationError("Nenhuma alteracao fornecida.");
157
187
  }
package/commands/task.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { getDb } from "../db/connection";
2
- import { initSchema, getPatternsForFiles, getPatternsByScope, getRecentReasoning, claimTask, recordAgentPerformance } from "../db/schema";
2
+ import { initSchema, getPatternsForFiles, getPatternsByScope, getRecentReasoning, claimTask, recordAgentPerformance, detectStuckTasks } from "../db/schema";
3
3
  import { enforceGate } from "../gates/validator";
4
4
  import { parseSubagentReturn, formatValidationErrors } from "../protocol/subagent-protocol";
5
5
  import { processSubagentReturn, formatProcessResult } from "../protocol/process-return";
@@ -8,6 +8,7 @@ import { getUnreadKnowledgeForTask } from "./knowledge";
8
8
  import { loadTemplate } from "../templates/loader";
9
9
  import { TaskStateError, ValidationError, KnowledgeBlockError } from "../errors";
10
10
  import { resolveSpec, resolveSpecOrNull } from "./spec-resolver";
11
+ import { getAgentDomain, domainToScope } from "../context/domains";
11
12
 
12
13
  export function taskNext(json: boolean = false, specId?: string): void {
13
14
  initSchema();
@@ -59,7 +60,12 @@ export function taskNext(json: boolean = false, specId?: string): void {
59
60
  } else {
60
61
  console.log("\nNenhuma task disponivel no momento.");
61
62
  console.log(`Tasks em execucao ou bloqueadas: ${pending.length}`);
62
- console.log("Aguarde as tasks em andamento serem concluidas.\n");
63
+ const stuck = detectStuckTasks(spec.id);
64
+ if (stuck.length > 0) {
65
+ showStuckWarning(stuck);
66
+ } else {
67
+ console.log("Aguarde as tasks em andamento serem concluidas.\n");
68
+ }
63
69
  }
64
70
  return;
65
71
  }
@@ -100,6 +106,17 @@ export function taskNext(json: boolean = false, specId?: string): void {
100
106
  console.log(`Use: task start <id> ou task start <id1>,<id2>,...\n`);
101
107
  }
102
108
 
109
+ function showStuckWarning(stuck: any[]): void {
110
+ console.log(`\n[!] AVISO: ${stuck.length} task(s) parada(s) ha mais de 30 minutos:`);
111
+ for (const t of stuck) {
112
+ const elapsed = t.started_at
113
+ ? Math.round((Date.now() - new Date(t.started_at).getTime()) / 60000)
114
+ : "?";
115
+ console.log(` #${t.number}: ${t.name} (${elapsed} min)`);
116
+ }
117
+ console.log(` Use: task done <id> --force --force-reason "timeout" para liberar\n`);
118
+ }
119
+
103
120
  export function taskStart(ids: string, json: boolean = false, fullContext: boolean = false, specId?: string): void {
104
121
  initSchema();
105
122
  enforceGate("task-start");
@@ -171,7 +188,7 @@ export function taskStart(ids: string, json: boolean = false, fullContext: boole
171
188
 
172
189
  // NOVO v7.4: Buscar implementation patterns relevantes
173
190
  const taskFiles = task.files ? JSON.parse(task.files) : [];
174
- const agentScope = task.agent?.split("-")[0] || "all"; // frontend-next -> frontend
191
+ const agentScope = domainToScope(getAgentDomain(task.agent));
175
192
 
176
193
  // Buscar patterns por escopo do agente E por arquivos esperados
177
194
  let relevantPatterns: any[] = [];
@@ -416,7 +433,7 @@ export function taskDone(id: string, options: { checkpoint: string; files?: stri
416
433
  const bypassCount = (db.query(
417
434
  "SELECT COUNT(*) as c FROM gate_bypasses WHERE task_id = ?"
418
435
  ).get(taskId) as any)?.c || 0;
419
- const totalGates = 7;
436
+ const totalGates = 8;
420
437
 
421
438
  recordAgentPerformance({
422
439
  agentType,
@@ -1,100 +1,38 @@
1
1
  /**
2
- * v9.3: Tests for AGENT_SECTIONS filtering (P3.1 — Context Intelligence)
2
+ * v9.4: Tests for domain-based context intelligence (replaces AGENT_SECTIONS)
3
3
  */
4
4
  import { describe, it, expect } from "bun:test";
5
- import { AGENT_SECTIONS } from "./utils";
6
-
7
- describe("AGENT_SECTIONS (P3.1 Context Intelligence)", () => {
8
- it("testing-unit gets focused sections without PRODUTO or STACK", () => {
9
- const sections = AGENT_SECTIONS["testing-unit"];
10
- expect(sections).toBeDefined();
11
- expect(sections).toContain("STANDARDS");
12
- expect(sections).toContain("DECISOES");
13
- expect(sections).toContain("ALERTAS");
14
- expect(sections).toContain("PATTERNS");
15
- expect(sections).toContain("UTILITIES");
16
- expect(sections).toContain("HINTS");
17
- expect(sections).not.toContain("PRODUTO");
18
- expect(sections).not.toContain("STACK");
19
- expect(sections).not.toContain("ARQUITETURA");
20
- });
21
-
22
- it("deep-explore gets minimal sections", () => {
23
- const sections = AGENT_SECTIONS["deep-explore"];
24
- expect(sections).toBeDefined();
25
- expect(sections).toHaveLength(2);
26
- expect(sections).toContain("STACK");
27
- expect(sections).toContain("ARQUITETURA");
28
- expect(sections).not.toContain("HINTS");
5
+ import { AGENT_DOMAIN, getAgentDomain, domainToScope } from "./utils";
6
+
7
+ describe("Domain exports from utils (backward compatibility)", () => {
8
+ it("AGENT_DOMAIN is re-exported and maps known agents", () => {
9
+ expect(AGENT_DOMAIN).toBeDefined();
10
+ expect(AGENT_DOMAIN["backend-javascript"]).toBe("backend");
11
+ expect(AGENT_DOMAIN["expert-postgres-developer"]).toBe("database");
12
+ expect(AGENT_DOMAIN["testing-unit"]).toBe("testing");
29
13
  });
30
14
 
31
- it("security-specialist gets security-relevant sections", () => {
32
- const sections = AGENT_SECTIONS["security-specialist"];
33
- expect(sections).toBeDefined();
34
- expect(sections).toContain("STANDARDS");
35
- expect(sections).toContain("DECISOES");
36
- expect(sections).toContain("ALERTAS");
37
- expect(sections).toContain("STACK");
38
- expect(sections).toContain("HINTS");
39
- expect(sections).not.toContain("PATTERNS");
40
- expect(sections).not.toContain("UTILITIES");
15
+ it("getAgentDomain is re-exported and works", () => {
16
+ expect(getAgentDomain("deep-explore")).toBe("explore");
17
+ expect(getAgentDomain(null)).toBeNull();
41
18
  });
42
19
 
43
- it("expert-code-reviewer gets review-relevant sections", () => {
44
- const sections = AGENT_SECTIONS["expert-code-reviewer"];
45
- expect(sections).toBeDefined();
46
- expect(sections).toContain("STANDARDS");
47
- expect(sections).toContain("DECISOES");
48
- expect(sections).toContain("ARQUITETURA");
49
- expect(sections).toContain("UTILITIES");
50
- expect(sections).toContain("ALERTAS");
51
- expect(sections).toContain("HINTS");
52
- expect(sections).not.toContain("PRODUTO");
20
+ it("domainToScope is re-exported and works", () => {
21
+ expect(domainToScope("backend")).toBe("backend");
22
+ expect(domainToScope(null)).toBe("all");
53
23
  });
54
24
 
55
- it("unknown agent types are not in the map (get all sections)", () => {
56
- expect(AGENT_SECTIONS["frontend-next"]).toBeUndefined();
57
- expect(AGENT_SECTIONS["database-postgres"]).toBeUndefined();
58
- expect(AGENT_SECTIONS["backend-javascript"]).toBeUndefined();
59
- expect(AGENT_SECTIONS["general-purpose"]).toBeUndefined();
25
+ // Regression: old split("-")[0] bug
26
+ it("expert-csharp-developer maps to 'backend' not 'expert'", () => {
27
+ const domain = getAgentDomain("expert-csharp-developer");
28
+ expect(domain).toBe("backend");
29
+ expect(domainToScope(domain)).toBe("backend");
60
30
  });
61
31
 
62
- it("filtering logic: known agent filters sections", () => {
63
- const allSections = [
64
- { name: "PRODUTO", content: "...", priority: 7 },
65
- { name: "STANDARDS", content: "...", priority: 1 },
66
- { name: "ALERTAS", content: "...", priority: 2 },
67
- { name: "STACK", content: "...", priority: 11 },
68
- { name: "ARQUITETURA", content: "...", priority: 3 },
69
- { name: "DECISOES", content: "...", priority: 4 },
70
- { name: "PATTERNS", content: "...", priority: 9 },
71
- ];
72
-
73
- const agentType = "deep-explore";
74
- const allowed = AGENT_SECTIONS[agentType];
75
- const filtered = allowed
76
- ? allSections.filter(s => allowed.includes(s.name))
77
- : allSections;
78
-
79
- expect(filtered).toHaveLength(2);
80
- expect(filtered.map(s => s.name)).toContain("STACK");
81
- expect(filtered.map(s => s.name)).toContain("ARQUITETURA");
82
- expect(filtered.map(s => s.name)).not.toContain("PRODUTO");
83
- expect(filtered.map(s => s.name)).not.toContain("STANDARDS");
84
- });
85
-
86
- it("filtering logic: unknown agent keeps all sections", () => {
87
- const allSections = [
88
- { name: "PRODUTO", content: "...", priority: 7 },
89
- { name: "STANDARDS", content: "...", priority: 1 },
90
- ];
91
-
92
- const agentType = "frontend-next"; // not in AGENT_SECTIONS
93
- const allowed = AGENT_SECTIONS[agentType];
94
- const filtered = allowed
95
- ? allSections.filter(s => allowed.includes(s.name))
96
- : allSections;
97
-
98
- expect(filtered).toHaveLength(2); // All kept
32
+ it("unknown agents return null domain and 'all' scope", () => {
33
+ expect(getAgentDomain("frontend-next")).toBeNull();
34
+ expect(getAgentDomain("database-postgres")).toBeNull();
35
+ expect(getAgentDomain("general-purpose")).toBeNull();
36
+ expect(domainToScope(getAgentDomain("general-purpose"))).toBe("all");
99
37
  });
100
38
  });
package/commands/utils.ts CHANGED
@@ -1,92 +1,16 @@
1
1
  import { getDb } from "../db/connection";
2
- import { initSchema, getArchitecturalAnalysisForSpec } from "../db/schema";
2
+ import { initSchema, getArchitecturalAnalysisForSpec, detectStuckTasks } from "../db/schema";
3
3
  import { getKnowledgeForTask } from "./knowledge";
4
4
  import { getLibContextsForAgent } from "./research";
5
5
  import { resolveSpec, resolveSpecOrNull, getAllActiveSpecs } from "./spec-resolver";
6
6
  import { CodexaError } from "../errors";
7
+ import { getAgentDomain, domainToScope } from "../context/domains";
7
8
  import pkg from "../package.json";
8
9
 
9
10
  // Re-export context modules for backward compatibility
10
11
  export { getContextForSubagent, getMinimalContextForSubagent } from "../context/generator";
11
- export { AGENT_SECTIONS, MAX_CONTEXT_SIZE } from "../context/assembly";
12
-
13
- export function contextUpdate(options: {
14
- approach?: string;
15
- pattern?: string;
16
- constraint?: string;
17
- specId?: string;
18
- }): void {
19
- initSchema();
20
- const db = getDb();
21
-
22
- const spec = resolveSpec(options.specId, ["implementing"]);
23
-
24
- const context = db.query("SELECT * FROM context WHERE spec_id = ?").get(spec.id) as any;
25
- const now = new Date().toISOString();
26
-
27
- let updated = false;
28
-
29
- if (options.approach) {
30
- // Append ao approach existente, não substitui
31
- const currentApproach = context?.approach || "";
32
- const newApproach = currentApproach
33
- ? `${currentApproach}\n- ${options.approach}`
34
- : options.approach;
35
-
36
- db.run("UPDATE context SET approach = ?, updated_at = ? WHERE spec_id = ?", [
37
- newApproach,
38
- now,
39
- spec.id,
40
- ]);
41
- console.log(`\n✓ Approach atualizado`);
42
- updated = true;
43
- }
44
-
45
- if (options.pattern) {
46
- // Adiciona ao array de patterns
47
- const currentPatterns = context?.patterns ? JSON.parse(context.patterns) : [];
48
- currentPatterns.push({
49
- pattern: options.pattern,
50
- detected_at: now,
51
- });
52
-
53
- db.run("UPDATE context SET patterns = ?, updated_at = ? WHERE spec_id = ?", [
54
- JSON.stringify(currentPatterns),
55
- now,
56
- spec.id,
57
- ]);
58
- console.log(`✓ Pattern adicionado (total: ${currentPatterns.length})`);
59
- updated = true;
60
- }
61
-
62
- if (options.constraint) {
63
- // Adiciona ao array de constraints
64
- const currentConstraints = context?.constraints ? JSON.parse(context.constraints) : [];
65
- currentConstraints.push({
66
- constraint: options.constraint,
67
- added_at: now,
68
- });
69
-
70
- db.run("UPDATE context SET constraints = ?, updated_at = ? WHERE spec_id = ?", [
71
- JSON.stringify(currentConstraints),
72
- now,
73
- spec.id,
74
- ]);
75
- console.log(`✓ Constraint adicionado (total: ${currentConstraints.length})`);
76
- updated = true;
77
- }
78
-
79
- if (!updated) {
80
- console.log("\nNenhuma opcao fornecida.");
81
- console.log("Use:");
82
- console.log(" --approach 'Nova abordagem descoberta'");
83
- console.log(" --pattern 'Padrao identificado no codigo'");
84
- console.log(" --constraint 'Nova limitacao encontrada'\n");
85
- return;
86
- }
87
-
88
- console.log(`\nContexto atualizado para: ${spec.name}\n`);
89
- }
12
+ export { MAX_CONTEXT_SIZE } from "../context/assembly";
13
+ export { AGENT_DOMAIN, getAgentDomain, domainToScope } from "../context/domains";
90
14
 
91
15
  export function status(json: boolean = false, specId?: string): void {
92
16
  initSchema();
@@ -168,6 +92,20 @@ function showSingleSpecStatus(db: any, spec: any, json: boolean): void {
168
92
  console.log(` Em execucao: ${tasksRunning}`);
169
93
  console.log(` Pendentes: ${tasksPending}`);
170
94
 
95
+ if (tasksRunning > 0) {
96
+ const stuck = detectStuckTasks(spec.id);
97
+ if (stuck.length > 0) {
98
+ console.log(`\n[!] AVISO: ${stuck.length} task(s) parada(s) ha mais de 30 minutos:`);
99
+ for (const t of stuck) {
100
+ const elapsed = t.started_at
101
+ ? Math.round((Date.now() - new Date(t.started_at).getTime()) / 60000)
102
+ : "?";
103
+ console.log(` #${t.number}: ${t.name} (${elapsed} min)`);
104
+ }
105
+ console.log(` Use: task done <id> --force --force-reason "timeout" para liberar`);
106
+ }
107
+ }
108
+
171
109
  if (context?.last_checkpoint) {
172
110
  console.log(`\nUltimo checkpoint:`);
173
111
  console.log(` ${context.last_checkpoint}`);
@@ -266,7 +204,7 @@ export function contextExport(options: { task?: string; json?: boolean; specId?:
266
204
  };
267
205
 
268
206
  // Obter standards para o agente da task
269
- const domain = task.agent?.split("-")[0] || "all";
207
+ const domain = domainToScope(getAgentDomain(task.agent));
270
208
  standards = db
271
209
  .query(
272
210
  `SELECT * FROM standards
@@ -363,7 +301,7 @@ export function contextDetail(section: string, json: boolean = false, specId?: s
363
301
  "SELECT * FROM tasks WHERE spec_id = ? AND status = 'running' LIMIT 1"
364
302
  ).get(spec.id) as any;
365
303
 
366
- const domain = runningTask?.agent?.split("-")[0] || "all";
304
+ const domain = domainToScope(getAgentDomain(runningTask?.agent));
367
305
 
368
306
  let output = "";
369
307
 
@@ -52,18 +52,26 @@ export function assembleSections(header: string, sections: ContextSection[]): st
52
52
  if (output.length > MAX_CONTEXT_SIZE) {
53
53
  const parts = output.split('\n### ');
54
54
  let trimmed = parts[0]; // Sempre manter header
55
+ let breakIndex = parts.length;
55
56
 
56
57
  for (let i = 1; i < parts.length; i++) {
57
58
  const candidate = trimmed + '\n### ' + parts[i];
58
59
  if (candidate.length > MAX_CONTEXT_SIZE - 200) {
60
+ breakIndex = i;
59
61
  break;
60
62
  }
61
63
  trimmed = candidate;
62
64
  }
63
65
 
64
- const omittedSections = parts.length - trimmed.split('\n### ').length;
65
- if (omittedSections > 0) {
66
- trimmed += `\n\n[CONTEXTO TRUNCADO: ${omittedSections} secao(oes) omitida(s) por limite de ${MAX_CONTEXT_SIZE} chars. Use: context-export para contexto completo]`;
66
+ // Coletar nomes das secoes omitidas
67
+ const omittedNames: string[] = [];
68
+ for (let i = breakIndex; i < parts.length; i++) {
69
+ const name = parts[i].split('\n')[0].trim();
70
+ if (name) omittedNames.push(name);
71
+ }
72
+
73
+ if (omittedNames.length > 0) {
74
+ trimmed += `\n\n[CONTEXTO TRUNCADO: ${omittedNames.length} secao(oes) omitida(s) (${omittedNames.join(', ')}). Use: codexa context detail <secao>]`;
67
75
  }
68
76
 
69
77
  output = trimmed;
@@ -71,12 +79,3 @@ export function assembleSections(header: string, sections: ContextSection[]): st
71
79
 
72
80
  return output;
73
81
  }
74
-
75
- // v9.3: Secoes relevantes por tipo de agente.
76
- // Agentes nao listados recebem TODAS as secoes (comportamento atual).
77
- export const AGENT_SECTIONS: Record<string, string[]> = {
78
- "testing-unit": ["STANDARDS", "DECISOES", "ALERTAS", "PATTERNS", "UTILITIES", "HINTS"],
79
- "deep-explore": ["STACK", "ARQUITETURA"],
80
- "security-specialist": ["STANDARDS", "DECISOES", "ALERTAS", "STACK", "HINTS"],
81
- "expert-code-reviewer": ["STANDARDS", "DECISOES", "ARQUITETURA", "UTILITIES", "ALERTAS", "HINTS"],
82
- };