@codexa/cli 9.0.7 → 9.0.9
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/decide.ts +120 -3
- package/commands/discover.ts +18 -9
- package/commands/integration.test.ts +754 -0
- package/commands/knowledge.test.ts +2 -6
- package/commands/knowledge.ts +20 -4
- package/commands/patterns.ts +8 -644
- package/commands/product.ts +41 -104
- package/commands/spec-resolver.test.ts +2 -13
- package/commands/standards.ts +33 -3
- package/commands/task.ts +21 -4
- package/commands/utils.test.ts +25 -87
- package/commands/utils.ts +20 -82
- package/context/assembly.ts +11 -12
- package/context/domains.test.ts +300 -0
- package/context/domains.ts +157 -0
- package/context/generator.ts +14 -13
- package/context/index.ts +6 -1
- package/context/references.test.ts +159 -0
- package/context/references.ts +159 -0
- package/context/sections.ts +18 -1
- package/db/schema.ts +40 -5
- package/db/test-helpers.ts +33 -0
- package/gates/standards-validator.test.ts +447 -0
- package/gates/standards-validator.ts +164 -125
- package/gates/typecheck-validator.ts +296 -92
- package/gates/validator.ts +93 -8
- package/package.json +1 -1
- package/protocol/process-return.ts +39 -4
- package/workflow.ts +54 -84
package/commands/product.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
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) {
|
package/commands/standards.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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 =
|
|
436
|
+
const totalGates = 8;
|
|
420
437
|
|
|
421
438
|
recordAgentPerformance({
|
|
422
439
|
agentType,
|
package/commands/utils.test.ts
CHANGED
|
@@ -1,100 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* v9.
|
|
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 {
|
|
6
|
-
|
|
7
|
-
describe("
|
|
8
|
-
it("
|
|
9
|
-
|
|
10
|
-
expect(
|
|
11
|
-
expect(
|
|
12
|
-
expect(
|
|
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("
|
|
32
|
-
|
|
33
|
-
expect(
|
|
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("
|
|
44
|
-
|
|
45
|
-
expect(
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
expect(
|
|
59
|
-
expect(
|
|
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("
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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 {
|
|
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
|
|
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
|
|
304
|
+
const domain = domainToScope(getAgentDomain(runningTask?.agent));
|
|
367
305
|
|
|
368
306
|
let output = "";
|
|
369
307
|
|
package/context/assembly.ts
CHANGED
|
@@ -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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
};
|