@codexa/cli 8.5.0 → 8.6.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/architect.ts +1 -137
- package/commands/clear.ts +1 -5
- package/commands/discover.ts +1071 -999
- package/commands/product.ts +0 -2
- package/commands/research.ts +1 -1
- package/commands/standards.ts +0 -23
- package/commands/task.ts +623 -623
- package/db/schema.ts +0 -69
- package/detectors/README.md +109 -109
- package/detectors/dotnet.ts +357 -357
- package/detectors/flutter.ts +350 -350
- package/detectors/go.ts +324 -324
- package/detectors/index.ts +387 -387
- package/detectors/jvm.ts +433 -433
- package/detectors/loader.ts +128 -140
- package/detectors/node.ts +493 -493
- package/detectors/python.ts +423 -423
- package/detectors/rust.ts +348 -348
- package/package.json +5 -4
- package/protocol/subagent-protocol.ts +0 -10
- package/workflow.ts +783 -800
package/commands/task.ts
CHANGED
|
@@ -1,624 +1,624 @@
|
|
|
1
|
-
import { getDb } from "../db/connection";
|
|
2
|
-
import { initSchema, getPatternsForFiles, getPatternsByScope, addSessionSummary, getRecentReasoning } from "../db/schema";
|
|
3
|
-
import { enforceGate } from "../gates/validator";
|
|
4
|
-
import { parseSubagentReturn, formatValidationErrors } from "../protocol/subagent-protocol";
|
|
5
|
-
import { processSubagentReturn, formatProcessResult } from "../protocol/process-return";
|
|
6
|
-
import { getContextForSubagent } from "./utils";
|
|
7
|
-
import { getUnreadKnowledgeForTask } from "./knowledge";
|
|
8
|
-
|
|
9
|
-
export function taskNext(json: boolean = false): void {
|
|
10
|
-
initSchema();
|
|
11
|
-
|
|
12
|
-
const db = getDb();
|
|
13
|
-
const spec = db
|
|
14
|
-
.query("SELECT * FROM specs WHERE phase = 'implementing' ORDER BY created_at DESC LIMIT 1")
|
|
15
|
-
.get() as any;
|
|
16
|
-
|
|
17
|
-
if (!spec) {
|
|
18
|
-
if (json) {
|
|
19
|
-
console.log(JSON.stringify({ available: [], message: "Nenhuma feature em implementacao" }));
|
|
20
|
-
} else {
|
|
21
|
-
console.error("\nNenhuma feature em fase de implementacao.");
|
|
22
|
-
console.error("Aprove o plano com: check approve\n");
|
|
23
|
-
}
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Buscar tasks pendentes cujas dependencias estao todas concluidas
|
|
28
|
-
const allTasks = db
|
|
29
|
-
.query("SELECT * FROM tasks WHERE spec_id = ? ORDER BY number")
|
|
30
|
-
.all(spec.id) as any[];
|
|
31
|
-
|
|
32
|
-
const available: any[] = [];
|
|
33
|
-
|
|
34
|
-
for (const task of allTasks) {
|
|
35
|
-
if (task.status !== "pending") continue;
|
|
36
|
-
|
|
37
|
-
// Verificar dependencias
|
|
38
|
-
const deps = task.depends_on ? JSON.parse(task.depends_on) : [];
|
|
39
|
-
const allDepsDone = deps.every((depNum: number) => {
|
|
40
|
-
const depTask = allTasks.find((t) => t.number === depNum);
|
|
41
|
-
return depTask?.status === "done";
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
if (allDepsDone) {
|
|
45
|
-
available.push(task);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
if (json) {
|
|
50
|
-
console.log(JSON.stringify({ available }));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (available.length === 0) {
|
|
55
|
-
// Verificar se todas estao concluidas
|
|
56
|
-
const pending = allTasks.filter((t) => t.status !== "done");
|
|
57
|
-
if (pending.length === 0) {
|
|
58
|
-
console.log("\nTodas as tasks foram concluidas!");
|
|
59
|
-
console.log("Inicie o review com: review start\n");
|
|
60
|
-
} else {
|
|
61
|
-
console.log("\nNenhuma task disponivel no momento.");
|
|
62
|
-
console.log(`Tasks em execucao ou bloqueadas: ${pending.length}`);
|
|
63
|
-
console.log("Aguarde as tasks em andamento serem concluidas.\n");
|
|
64
|
-
}
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
console.log(`\nTasks disponiveis para execucao (${available.length}):`);
|
|
69
|
-
console.log(`${"─".repeat(50)}`);
|
|
70
|
-
|
|
71
|
-
const parallelizable = available.filter((t) => t.can_parallel);
|
|
72
|
-
const sequential = available.filter((t) => !t.can_parallel);
|
|
73
|
-
|
|
74
|
-
if (parallelizable.length > 0) {
|
|
75
|
-
if (parallelizable.length > 1) {
|
|
76
|
-
console.log(`\nPodem executar em PARALELO:`);
|
|
77
|
-
} else {
|
|
78
|
-
console.log(`\nDisponivel:`);
|
|
79
|
-
}
|
|
80
|
-
const ids = parallelizable.map((t) => t.id).join(",");
|
|
81
|
-
for (const task of parallelizable) {
|
|
82
|
-
const files = task.files ? JSON.parse(task.files) : [];
|
|
83
|
-
console.log(` #${task.number}: ${task.name} (${task.agent || "geral"}) [id: ${task.id}]`);
|
|
84
|
-
if (files.length > 0) console.log(` Arquivos: ${files.join(", ")}`);
|
|
85
|
-
}
|
|
86
|
-
if (parallelizable.length > 1) {
|
|
87
|
-
console.log(`\n Para iniciar todas: task start ${ids}`);
|
|
88
|
-
} else {
|
|
89
|
-
console.log(`\n Para iniciar: task start ${ids}`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (sequential.length > 0) {
|
|
94
|
-
console.log(`\nSequenciais (dependencias pendentes):`);
|
|
95
|
-
for (const task of sequential) {
|
|
96
|
-
console.log(` #${task.number}: ${task.name} (${task.agent || "geral"})`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
console.log(`\n${"─".repeat(50)}`);
|
|
101
|
-
console.log(`Use: task start <id> ou task start <id1>,<id2>,...\n`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export function taskStart(ids: string, json: boolean = false): void {
|
|
105
|
-
initSchema();
|
|
106
|
-
enforceGate("task-start");
|
|
107
|
-
|
|
108
|
-
const db = getDb();
|
|
109
|
-
const now = new Date().toISOString();
|
|
110
|
-
|
|
111
|
-
const spec = db
|
|
112
|
-
.query("SELECT * FROM specs WHERE phase = 'implementing' ORDER BY created_at DESC LIMIT 1")
|
|
113
|
-
.get() as any;
|
|
114
|
-
|
|
115
|
-
const taskIds = ids.split(",").map((s) => parseInt(s.trim()));
|
|
116
|
-
const startedTasks: any[] = [];
|
|
117
|
-
|
|
118
|
-
for (const taskId of taskIds) {
|
|
119
|
-
const task = db.query("SELECT * FROM tasks WHERE id = ?").get(taskId) as any;
|
|
120
|
-
|
|
121
|
-
if (!task) {
|
|
122
|
-
console.error(`\nTask #${taskId} nao encontrada.\n`);
|
|
123
|
-
process.exit(2);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (task.status !== "pending") {
|
|
127
|
-
console.error(`\nTask #${task.number} nao esta pendente (status: ${task.status}).\n`);
|
|
128
|
-
process.exit(2);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Validar dependencias para esta task especifica
|
|
132
|
-
enforceGate("task-start", { taskId });
|
|
133
|
-
|
|
134
|
-
// Marcar como running
|
|
135
|
-
db.run("UPDATE tasks SET status = 'running' WHERE id = ?", [taskId]);
|
|
136
|
-
|
|
137
|
-
startedTasks.push(task);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
// Atualizar contexto
|
|
141
|
-
db.run("UPDATE context SET current_task = ?, updated_at = ? WHERE spec_id = ?", [
|
|
142
|
-
startedTasks[0].number,
|
|
143
|
-
now,
|
|
144
|
-
spec.id,
|
|
145
|
-
]);
|
|
146
|
-
|
|
147
|
-
if (json) {
|
|
148
|
-
// NOVO: Incluir contexto COMPLETO para cada task
|
|
149
|
-
const contexts = startedTasks.map((task) => {
|
|
150
|
-
const contextText = getContextForSubagent(task.id);
|
|
151
|
-
const unreadKnowledge = getUnreadKnowledgeForTask(spec.id, task.id);
|
|
152
|
-
|
|
153
|
-
// NOVO v7.4: Buscar implementation patterns relevantes
|
|
154
|
-
const taskFiles = task.files ? JSON.parse(task.files) : [];
|
|
155
|
-
const agentScope = task.agent?.split("-")[0] || "all"; // frontend-next -> frontend
|
|
156
|
-
|
|
157
|
-
// Buscar patterns por escopo do agente E por arquivos esperados
|
|
158
|
-
let relevantPatterns: any[] = [];
|
|
159
|
-
|
|
160
|
-
// Primeiro: patterns que correspondem aos arquivos esperados
|
|
161
|
-
if (taskFiles.length > 0) {
|
|
162
|
-
const filePatterns = getPatternsForFiles(taskFiles);
|
|
163
|
-
relevantPatterns.push(...filePatterns);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Segundo: patterns do escopo do agente (se nao foram encontrados via arquivos)
|
|
167
|
-
if (relevantPatterns.length === 0) {
|
|
168
|
-
const scopePatterns = getPatternsByScope(agentScope);
|
|
169
|
-
relevantPatterns.push(...scopePatterns);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Formatar patterns para o contexto (sem duplicatas)
|
|
173
|
-
const patternNames = new Set<string>();
|
|
174
|
-
const formattedPatterns = relevantPatterns
|
|
175
|
-
.filter(p => {
|
|
176
|
-
if (patternNames.has(p.name)) return false;
|
|
177
|
-
patternNames.add(p.name);
|
|
178
|
-
return true;
|
|
179
|
-
})
|
|
180
|
-
.map(p => ({
|
|
181
|
-
name: p.name,
|
|
182
|
-
category: p.category,
|
|
183
|
-
applies_to: p.applies_to,
|
|
184
|
-
template: p.template,
|
|
185
|
-
structure: JSON.parse(p.structure || "{}"),
|
|
186
|
-
examples: JSON.parse(p.examples || "[]").slice(0, 3), // Top 3 exemplos
|
|
187
|
-
anti_patterns: JSON.parse(p.anti_patterns || "[]"),
|
|
188
|
-
confidence: p.confidence,
|
|
189
|
-
}));
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
taskId: task.id,
|
|
193
|
-
number: task.number,
|
|
194
|
-
name: task.name,
|
|
195
|
-
agent: task.agent,
|
|
196
|
-
files: taskFiles,
|
|
197
|
-
// AVISO PARA O ORQUESTRADOR - NAO EXECUTE, DELEGUE
|
|
198
|
-
_orchestratorWarning: "NAO execute esta task diretamente. Use Task tool com subagent_type='general-purpose' para delegar. O campo 'subagentContext' abaixo e o prompt para o SUBAGENT.",
|
|
199
|
-
// Contexto completo para o subagent (NAO para o orquestrador)
|
|
200
|
-
context: contextText,
|
|
201
|
-
// Knowledge nao lido (broadcast de outras tasks)
|
|
202
|
-
unreadKnowledge: unreadKnowledge.map((k: any) => ({
|
|
203
|
-
id: k.id,
|
|
204
|
-
category: k.category,
|
|
205
|
-
content: k.content,
|
|
206
|
-
severity: k.severity,
|
|
207
|
-
origin_task: k.task_origin,
|
|
208
|
-
})),
|
|
209
|
-
// NOVO v7.4: Implementation patterns extraidos do projeto
|
|
210
|
-
implementationPatterns: formattedPatterns,
|
|
211
|
-
// Contexto para o SUBAGENT (o orquestrador deve passar isso via Task tool)
|
|
212
|
-
subagentContext: `
|
|
213
|
-
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
214
|
-
║ DIRETIVA CRITICA: USE Write/Edit PARA CRIAR OS ARQUIVOS ║
|
|
215
|
-
║ NAO descreva. NAO planeje. NAO simule. EXECUTE AGORA. ║
|
|
216
|
-
║ Se retornar sem usar Write/Edit, a task FALHA. ║
|
|
217
|
-
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
218
|
-
|
|
219
|
-
ARQUIVOS QUE VOCE DEVE CRIAR (use Write para cada um):
|
|
220
|
-
${taskFiles.map(f => ` - ${f}`).join('\n') || ' (nenhum arquivo especificado - analise o contexto)'}
|
|
221
|
-
|
|
222
|
-
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
223
|
-
║ POLLING OBRIGATORIO: Verifique blockers a cada 3 arquivos modificados ║
|
|
224
|
-
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
225
|
-
|
|
226
|
-
ANTES de criar o 4o, 7o, 10o arquivo (a cada 3), execute:
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
Se retornar QUALQUER blocker:
|
|
230
|
-
1. PARE imediatamente
|
|
231
|
-
2. Retorne com status "blocked" e inclua o blocker encontrado
|
|
232
|
-
3. NAO continue criando arquivos apos encontrar blocker
|
|
233
|
-
|
|
234
|
-
CHECKLIST OBRIGATORIO (verifique ANTES de retornar):
|
|
235
|
-
- [ ] Usei Write ou Edit para criar/modificar arquivos?
|
|
236
|
-
- [ ] Verifiquei blockers a cada 3 arquivos?
|
|
237
|
-
- [ ] Os arquivos que vou listar em files_created EXISTEM no disco?
|
|
238
|
-
|
|
239
|
-
Se nao marcou todos os items, PARE e corrija AGORA.
|
|
240
|
-
`.trim(),
|
|
241
|
-
// Instrucoes de retorno para o SUBAGENT
|
|
242
|
-
subagentReturnProtocol: `
|
|
243
|
-
FORMATO DE RETORNO (apos criar os arquivos):
|
|
244
|
-
{
|
|
245
|
-
"status": "completed | blocked | needs_decision",
|
|
246
|
-
"summary": "Resumo do que foi feito (10-500 chars)",
|
|
247
|
-
"files_created": ["path/arquivo.ts"],
|
|
248
|
-
"files_modified": ["path/outro.ts"],
|
|
249
|
-
"patterns_discovered": ["Pattern identificado"],
|
|
250
|
-
"decisions_made": [{"title": "...", "decision": "..."}],
|
|
251
|
-
"blockers": ["Se status != completed"],
|
|
252
|
-
"knowledge_to_broadcast": [{"category": "discovery|pattern|constraint", "content": "...", "severity": "info|warning|critical"}],
|
|
253
|
-
"reasoning": {
|
|
254
|
-
"approach": "Como voce abordou o problema (RECOMENDADO)",
|
|
255
|
-
"challenges": ["Desafios encontrados"],
|
|
256
|
-
"recommendations": "Sugestoes para proximas tasks"
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
IMPORTANTE: O campo "reasoning" ajuda a preservar contexto entre sessoes.
|
|
261
|
-
Inclua pelo menos "approach" para explicar suas escolhas.
|
|
262
|
-
|
|
263
|
-
Se NAO conseguir criar arquivos (sem permissao, sem ferramentas), retorne:
|
|
264
|
-
{
|
|
265
|
-
"status": "blocked",
|
|
266
|
-
"blockers": ["Descreva por que nao conseguiu criar os arquivos"]
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
Veja .claude/agents/PROTOCOL.md para detalhes completos.
|
|
270
|
-
${formattedPatterns.length > 0 ? `
|
|
271
|
-
PATTERNS: Voce recebeu ${formattedPatterns.length} implementation patterns extraidos do projeto.
|
|
272
|
-
Use os TEMPLATES fornecidos para criar codigo CONSISTENTE com o projeto existente.
|
|
273
|
-
` : ''}
|
|
274
|
-
`.trim(),
|
|
275
|
-
};
|
|
276
|
-
});
|
|
277
|
-
console.log(JSON.stringify({ started: contexts }));
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
console.log(`\nTasks iniciadas:`);
|
|
282
|
-
for (const task of startedTasks) {
|
|
283
|
-
console.log(` #${task.number}: ${task.name} (${task.agent || "geral"})`);
|
|
284
|
-
}
|
|
285
|
-
console.log(`\nAo concluir, use: task done <id> --checkpoint "resumo"\n`);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
export function taskDone(id: string, options: { checkpoint: string; files?: string; force?: boolean; forceReason?: string; output?: string }): void {
|
|
289
|
-
initSchema();
|
|
290
|
-
|
|
291
|
-
const taskId = parseInt(id);
|
|
292
|
-
const db = getDb();
|
|
293
|
-
const task = db.query("SELECT * FROM tasks WHERE id = ?").get(taskId) as any;
|
|
294
|
-
|
|
295
|
-
if (!task) {
|
|
296
|
-
console.error(`\nTask #${taskId} nao encontrada.\n`);
|
|
297
|
-
process.exit(2);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
const spec = db.query("SELECT * FROM specs WHERE id = ?").get(task.spec_id) as any;
|
|
301
|
-
const now = new Date().toISOString();
|
|
302
|
-
|
|
303
|
-
// ============================================================
|
|
304
|
-
// NOVO: Processar retorno do subagent se fornecido via --output
|
|
305
|
-
// ============================================================
|
|
306
|
-
let checkpoint = options.checkpoint;
|
|
307
|
-
let expectedFiles: string[] = [];
|
|
308
|
-
let subagentData = null;
|
|
309
|
-
|
|
310
|
-
if (options.output) {
|
|
311
|
-
// Parse e valida o retorno do subagent
|
|
312
|
-
const parseResult = parseSubagentReturn(options.output);
|
|
313
|
-
|
|
314
|
-
if (!parseResult.success) {
|
|
315
|
-
console.error(formatValidationErrors(parseResult));
|
|
316
|
-
console.error("\nTask NAO pode ser completada sem retorno valido.");
|
|
317
|
-
console.error("Corrija o formato do retorno do subagent e tente novamente.\n");
|
|
318
|
-
process.exit(2);
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
subagentData = parseResult.data!;
|
|
322
|
-
|
|
323
|
-
// Extrair checkpoint do summary se nao fornecido
|
|
324
|
-
if (!checkpoint || checkpoint === "") {
|
|
325
|
-
checkpoint = subagentData.summary;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Combinar arquivos do retorno com --files
|
|
329
|
-
expectedFiles = [
|
|
330
|
-
...subagentData.files_created,
|
|
331
|
-
...subagentData.files_modified,
|
|
332
|
-
];
|
|
333
|
-
|
|
334
|
-
// Se --files tambem foi fornecido, adicionar
|
|
335
|
-
if (options.files) {
|
|
336
|
-
expectedFiles.push(...options.files.split(",").map((f) => f.trim()));
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Remover duplicatas
|
|
340
|
-
expectedFiles = [...new Set(expectedFiles)];
|
|
341
|
-
|
|
342
|
-
// Se status nao e completed, informar e sair
|
|
343
|
-
if (subagentData.status === "blocked") {
|
|
344
|
-
console.log("\n[!] Subagent retornou status 'blocked'");
|
|
345
|
-
console.log("\nBlockers encontrados:");
|
|
346
|
-
for (const blocker of subagentData.blockers || []) {
|
|
347
|
-
console.log(` - ${blocker}`);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// Registrar blockers como knowledge critico
|
|
351
|
-
const processResult = processSubagentReturn(spec.id, taskId, task.number, subagentData);
|
|
352
|
-
console.log(formatProcessResult(processResult));
|
|
353
|
-
|
|
354
|
-
console.log("\nTask permanece em execucao. Resolva os blockers e tente novamente.\n");
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
if (subagentData.status === "needs_decision") {
|
|
359
|
-
console.log("\n[?] Subagent retornou status 'needs_decision'");
|
|
360
|
-
console.log("\nDecisao necessaria:");
|
|
361
|
-
for (const item of subagentData.blockers || []) {
|
|
362
|
-
console.log(` - ${item}`);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// Registrar como knowledge
|
|
366
|
-
const processResult = processSubagentReturn(spec.id, taskId, task.number, subagentData);
|
|
367
|
-
console.log(formatProcessResult(processResult));
|
|
368
|
-
|
|
369
|
-
console.log("\nTask permanece em execucao. Tome a decisao e tente novamente.\n");
|
|
370
|
-
return;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Status completed - processar dados automaticamente
|
|
374
|
-
const processResult = processSubagentReturn(spec.id, taskId, task.number, subagentData);
|
|
375
|
-
console.log(formatProcessResult(processResult));
|
|
376
|
-
|
|
377
|
-
// v8.3: BLOCKING check para knowledge critico nao reconhecido (substitui warning v8.2)
|
|
378
|
-
const unackedCritical = getUnreadKnowledgeForTask(spec.id, taskId)
|
|
379
|
-
.filter((k: any) => k.severity === 'critical' && k.task_origin !== taskId);
|
|
380
|
-
if (unackedCritical.length > 0) {
|
|
381
|
-
if (!options.force) {
|
|
382
|
-
console.error(`\n[X] BLOQUEADO: ${unackedCritical.length} knowledge(s) critico(s) nao reconhecido(s):`);
|
|
383
|
-
for (const k of unackedCritical) {
|
|
384
|
-
console.error(` [X] ${k.content} (de Task #${k.task_origin})`);
|
|
385
|
-
}
|
|
386
|
-
console.error(`\n O subagent NAO verificou o polling obrigatorio.`);
|
|
387
|
-
console.error(` Reconheca com: knowledge ack <id>`);
|
|
388
|
-
console.error(` Ou force com: task done ${id} --checkpoint "..." --force --force-reason "motivo"\n`);
|
|
389
|
-
process.exit(1);
|
|
390
|
-
} else {
|
|
391
|
-
console.log(`\n[!] AVISO: ${unackedCritical.length} knowledge(s) critico(s) ignorado(s) (--force usado)`);
|
|
392
|
-
for (const k of unackedCritical) {
|
|
393
|
-
console.log(` [X] ${k.content}`);
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
} else {
|
|
398
|
-
// Modo antigo: arquivos via --files
|
|
399
|
-
expectedFiles = options.files
|
|
400
|
-
? options.files.split(",").map((f) => f.trim())
|
|
401
|
-
: [];
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Validar gates incluindo files-exist e standards-follow
|
|
405
|
-
enforceGate("task-done", {
|
|
406
|
-
taskId,
|
|
407
|
-
checkpoint,
|
|
408
|
-
files: expectedFiles,
|
|
409
|
-
force: options.force,
|
|
410
|
-
forceReason: options.forceReason,
|
|
411
|
-
});
|
|
412
|
-
|
|
413
|
-
// Nota: Validacao de standards agora e feita pelo Gate 4.2 (standards-follow) em enforceGate
|
|
414
|
-
|
|
415
|
-
// Marcar como done
|
|
416
|
-
db.run(
|
|
417
|
-
"UPDATE tasks SET status = 'done', checkpoint = ?, completed_at = ? WHERE id = ?",
|
|
418
|
-
[checkpoint, now, taskId]
|
|
419
|
-
);
|
|
420
|
-
|
|
421
|
-
// Registrar artefatos se NAO veio do subagent (ja foi processado acima)
|
|
422
|
-
if (!subagentData && options.files) {
|
|
423
|
-
const files = options.files.split(",").map((s) => s.trim());
|
|
424
|
-
for (const file of files) {
|
|
425
|
-
db.run(
|
|
426
|
-
`INSERT OR REPLACE INTO artifacts (spec_id, task_ref, path, action, created_at)
|
|
427
|
-
VALUES (?, ?, ?, 'created', ?)`,
|
|
428
|
-
[spec.id, task.number, file, now]
|
|
429
|
-
);
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Atualizar contexto
|
|
434
|
-
const doneCount = db
|
|
435
|
-
.query("SELECT COUNT(*) as c FROM tasks WHERE spec_id = ? AND status = 'done'")
|
|
436
|
-
.get(spec.id) as any;
|
|
437
|
-
const totalCount = db
|
|
438
|
-
.query("SELECT COUNT(*) as c FROM tasks WHERE spec_id = ?")
|
|
439
|
-
.get(spec.id) as any;
|
|
440
|
-
|
|
441
|
-
db.run(
|
|
442
|
-
"UPDATE context SET last_checkpoint = ?, updated_at = ? WHERE spec_id = ?",
|
|
443
|
-
[checkpoint, now, spec.id]
|
|
444
|
-
);
|
|
445
|
-
|
|
446
|
-
// Criar snapshot automatico COMPLETO
|
|
447
|
-
const allTasks = db.query("SELECT * FROM tasks WHERE spec_id = ?").all(spec.id) as any[];
|
|
448
|
-
const allDecisions = db.query("SELECT * FROM decisions WHERE spec_id = ?").all(spec.id) as any[];
|
|
449
|
-
const allArtifacts = db.query("SELECT * FROM artifacts WHERE spec_id = ?").all(spec.id) as any[];
|
|
450
|
-
const context = db.query("SELECT * FROM context WHERE spec_id = ?").get(spec.id);
|
|
451
|
-
|
|
452
|
-
const snapshotData = {
|
|
453
|
-
spec,
|
|
454
|
-
context,
|
|
455
|
-
tasks: allTasks,
|
|
456
|
-
decisions: allDecisions,
|
|
457
|
-
artifacts: allArtifacts,
|
|
458
|
-
checkpoint: options.checkpoint,
|
|
459
|
-
taskCompleted: task.number,
|
|
460
|
-
timestamp: now,
|
|
461
|
-
};
|
|
462
|
-
db.run("INSERT INTO snapshots (spec_id, data, trigger, created_at) VALUES (?, ?, 'auto', ?)", [
|
|
463
|
-
spec.id,
|
|
464
|
-
JSON.stringify(snapshotData),
|
|
465
|
-
now,
|
|
466
|
-
]);
|
|
467
|
-
|
|
468
|
-
// v8.1: Gerar session summary automaticamente
|
|
469
|
-
try {
|
|
470
|
-
const taskDecisions = allDecisions.filter((d) => d.task_ref === task.number);
|
|
471
|
-
const taskArtifacts = allArtifacts.filter((a) => a.task_ref === task.number);
|
|
472
|
-
const blockers = db.query(
|
|
473
|
-
"SELECT content FROM knowledge WHERE spec_id = ? AND task_origin = ? AND category = 'blocker'"
|
|
474
|
-
).all(spec.id, taskId) as any[];
|
|
475
|
-
const reasoning = getRecentReasoning(spec.id, 5);
|
|
476
|
-
const nextSteps: string[] = [];
|
|
477
|
-
|
|
478
|
-
// Extrair recommendations do reasoning
|
|
479
|
-
for (const r of reasoning) {
|
|
480
|
-
if (r.category === 'recommendation' && r.thought) {
|
|
481
|
-
nextSteps.push(r.thought);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
// Se nao completou tudo, sugerir proximo passo
|
|
486
|
-
if (doneCount.c < totalCount.c) {
|
|
487
|
-
nextSteps.push(`Continuar implementacao: ${totalCount.c - doneCount.c} tasks restantes`);
|
|
488
|
-
} else {
|
|
489
|
-
nextSteps.push("Todas tasks concluidas. Iniciar review.");
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
addSessionSummary(spec.id, {
|
|
493
|
-
startTime: task.completed_at || now, // approximation
|
|
494
|
-
endTime: now,
|
|
495
|
-
summary: `Task #${task.number} (${task.name}) concluida. ${checkpoint}`,
|
|
496
|
-
decisions: taskDecisions.map((d: any) => `${d.title}: ${d.decision}`),
|
|
497
|
-
blockers: blockers.map((b: any) => b.content),
|
|
498
|
-
nextSteps,
|
|
499
|
-
tasksCompleted: 1,
|
|
500
|
-
filesCreated: taskArtifacts.filter((a: any) => a.action === 'created').length,
|
|
501
|
-
filesModified: taskArtifacts.filter((a: any) => a.action === 'modified').length,
|
|
502
|
-
});
|
|
503
|
-
} catch (e) {
|
|
504
|
-
// Session summary e best-effort, nao deve bloquear o fluxo
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
console.log(`\nTask #${task.number} concluida!`);
|
|
508
|
-
console.log(`Checkpoint: ${options.checkpoint}`);
|
|
509
|
-
console.log(`Progresso: ${doneCount.c}/${totalCount.c} tasks`);
|
|
510
|
-
|
|
511
|
-
if (doneCount.c === totalCount.c) {
|
|
512
|
-
// Mostrar resumo completo da implementacao
|
|
513
|
-
showImplementationSummary(spec.id, allTasks, allArtifacts, allDecisions);
|
|
514
|
-
} else {
|
|
515
|
-
console.log(`\nProximas tasks: task next\n`);
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
/**
|
|
520
|
-
* Mostra resumo detalhado da implementacao apos todas as tasks concluidas
|
|
521
|
-
* Permite ao usuario decidir se quer fazer review ou nao
|
|
522
|
-
*/
|
|
523
|
-
|
|
524
|
-
specId: number,
|
|
525
|
-
tasks: any[],
|
|
526
|
-
artifacts: any[],
|
|
527
|
-
decisions: any[]
|
|
528
|
-
): void {
|
|
529
|
-
const db = getDb();
|
|
530
|
-
const spec = db.query("SELECT * FROM specs WHERE id = ?").get(specId) as any;
|
|
531
|
-
const knowledge = db.query("SELECT * FROM knowledge WHERE spec_id = ?").all(specId) as any[];
|
|
532
|
-
|
|
533
|
-
console.log(`\n${"=".repeat(60)}`);
|
|
534
|
-
console.log(`IMPLEMENTACAO CONCLUIDA: ${spec.name}`);
|
|
535
|
-
console.log(`${"=".repeat(60)}`);
|
|
536
|
-
|
|
537
|
-
// Resumo por subagente
|
|
538
|
-
console.log(`\n📋 RESUMO POR SUBAGENTE:`);
|
|
539
|
-
console.log(`${"─".repeat(50)}`);
|
|
540
|
-
|
|
541
|
-
// Agrupar tasks por agente
|
|
542
|
-
const tasksByAgent: Record<string, any[]> = {};
|
|
543
|
-
for (const task of tasks) {
|
|
544
|
-
const agent = task.agent || "geral";
|
|
545
|
-
if (!tasksByAgent[agent]) {
|
|
546
|
-
tasksByAgent[agent] = [];
|
|
547
|
-
}
|
|
548
|
-
tasksByAgent[agent].push(task);
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
for (const [agent, agentTasks] of Object.entries(tasksByAgent)) {
|
|
552
|
-
console.log(`\n [${agent.toUpperCase()}]`);
|
|
553
|
-
for (const task of agentTasks) {
|
|
554
|
-
console.log(` #${task.number}: ${task.name}`);
|
|
555
|
-
if (task.checkpoint) {
|
|
556
|
-
console.log(` └─ ${task.checkpoint}`);
|
|
557
|
-
}
|
|
558
|
-
// Mostrar arquivos criados por esta task
|
|
559
|
-
const taskArtifacts = artifacts.filter((a) => a.task_ref === task.number);
|
|
560
|
-
if (taskArtifacts.length > 0) {
|
|
561
|
-
console.log(` Arquivos: ${taskArtifacts.map((a) => a.path).join(", ")}`);
|
|
562
|
-
}
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
// Artefatos criados
|
|
567
|
-
console.log(`\n📁 ARTEFATOS CRIADOS (${artifacts.length}):`);
|
|
568
|
-
console.log(`${"─".repeat(50)}`);
|
|
569
|
-
for (const artifact of artifacts) {
|
|
570
|
-
const task = tasks.find((t) => t.number === artifact.task_ref);
|
|
571
|
-
const agentInfo = task?.agent ? ` [${task.agent}]` : "";
|
|
572
|
-
console.log(` - ${artifact.path}${agentInfo}`);
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
// Decisoes tomadas
|
|
576
|
-
if (decisions.length > 0) {
|
|
577
|
-
console.log(`\n🎯 DECISOES TOMADAS (${decisions.length}):`);
|
|
578
|
-
console.log(`${"─".repeat(50)}`);
|
|
579
|
-
for (const dec of decisions) {
|
|
580
|
-
const task = tasks.find((t) => t.number === dec.task_ref);
|
|
581
|
-
const agentInfo = task?.agent ? ` [${task.agent}]` : "";
|
|
582
|
-
console.log(` - ${dec.title}${agentInfo}`);
|
|
583
|
-
console.log(` └─ ${dec.decision}`);
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
// Knowledge descoberto
|
|
588
|
-
const discoveries = knowledge.filter((k) => k.category === "discovery" || k.category === "pattern");
|
|
589
|
-
if (discoveries.length > 0) {
|
|
590
|
-
console.log(`\n💡 DESCOBERTAS E PADROES (${discoveries.length}):`);
|
|
591
|
-
console.log(`${"─".repeat(50)}`);
|
|
592
|
-
for (const item of discoveries) {
|
|
593
|
-
const severityIcon = item.severity === "critical" ? "🚨" : item.severity === "warning" ? "⚠️" : "ℹ️";
|
|
594
|
-
console.log(` ${severityIcon} ${item.content}`);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
// Blockers encontrados
|
|
599
|
-
const blockers = knowledge.filter((k) => k.category === "blocker");
|
|
600
|
-
if (blockers.length > 0) {
|
|
601
|
-
console.log(`\n⛔ BLOCKERS ENCONTRADOS (${blockers.length}):`);
|
|
602
|
-
console.log(`${"─".repeat(50)}`);
|
|
603
|
-
for (const blocker of blockers) {
|
|
604
|
-
console.log(` - ${blocker.content}`);
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
// Estatisticas finais
|
|
609
|
-
console.log(`\n📊 ESTATISTICAS:`);
|
|
610
|
-
console.log(`${"─".repeat(50)}`);
|
|
611
|
-
console.log(` Tasks concluidas: ${tasks.length}`);
|
|
612
|
-
console.log(` Artefatos criados: ${artifacts.length}`);
|
|
613
|
-
console.log(` Decisoes tomadas: ${decisions.length}`);
|
|
614
|
-
console.log(` Knowledge items: ${knowledge.length}`);
|
|
615
|
-
console.log(` Subagentes envolvidos: ${Object.keys(tasksByAgent).length}`);
|
|
616
|
-
|
|
617
|
-
console.log(`\n${"=".repeat(60)}`);
|
|
618
|
-
console.log(`PROXIMOS PASSOS:`);
|
|
619
|
-
console.log(`${"=".repeat(60)}`);
|
|
620
|
-
console.log(`\n Para revisar o codigo: review start`);
|
|
621
|
-
console.log(` Para pular o review: review skip`);
|
|
622
|
-
console.log(`\n O agente aguarda sua decisao.`);
|
|
623
|
-
console.log(`${"─".repeat(50)}\n`);
|
|
1
|
+
import { getDb } from "../db/connection";
|
|
2
|
+
import { initSchema, getPatternsForFiles, getPatternsByScope, addSessionSummary, getRecentReasoning } from "../db/schema";
|
|
3
|
+
import { enforceGate } from "../gates/validator";
|
|
4
|
+
import { parseSubagentReturn, formatValidationErrors } from "../protocol/subagent-protocol";
|
|
5
|
+
import { processSubagentReturn, formatProcessResult } from "../protocol/process-return";
|
|
6
|
+
import { getContextForSubagent } from "./utils";
|
|
7
|
+
import { getUnreadKnowledgeForTask } from "./knowledge";
|
|
8
|
+
|
|
9
|
+
export function taskNext(json: boolean = false): void {
|
|
10
|
+
initSchema();
|
|
11
|
+
|
|
12
|
+
const db = getDb();
|
|
13
|
+
const spec = db
|
|
14
|
+
.query("SELECT * FROM specs WHERE phase = 'implementing' ORDER BY created_at DESC LIMIT 1")
|
|
15
|
+
.get() as any;
|
|
16
|
+
|
|
17
|
+
if (!spec) {
|
|
18
|
+
if (json) {
|
|
19
|
+
console.log(JSON.stringify({ available: [], message: "Nenhuma feature em implementacao" }));
|
|
20
|
+
} else {
|
|
21
|
+
console.error("\nNenhuma feature em fase de implementacao.");
|
|
22
|
+
console.error("Aprove o plano com: check approve\n");
|
|
23
|
+
}
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Buscar tasks pendentes cujas dependencias estao todas concluidas
|
|
28
|
+
const allTasks = db
|
|
29
|
+
.query("SELECT * FROM tasks WHERE spec_id = ? ORDER BY number")
|
|
30
|
+
.all(spec.id) as any[];
|
|
31
|
+
|
|
32
|
+
const available: any[] = [];
|
|
33
|
+
|
|
34
|
+
for (const task of allTasks) {
|
|
35
|
+
if (task.status !== "pending") continue;
|
|
36
|
+
|
|
37
|
+
// Verificar dependencias
|
|
38
|
+
const deps = task.depends_on ? JSON.parse(task.depends_on) : [];
|
|
39
|
+
const allDepsDone = deps.every((depNum: number) => {
|
|
40
|
+
const depTask = allTasks.find((t) => t.number === depNum);
|
|
41
|
+
return depTask?.status === "done";
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (allDepsDone) {
|
|
45
|
+
available.push(task);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (json) {
|
|
50
|
+
console.log(JSON.stringify({ available }));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (available.length === 0) {
|
|
55
|
+
// Verificar se todas estao concluidas
|
|
56
|
+
const pending = allTasks.filter((t) => t.status !== "done");
|
|
57
|
+
if (pending.length === 0) {
|
|
58
|
+
console.log("\nTodas as tasks foram concluidas!");
|
|
59
|
+
console.log("Inicie o review com: review start\n");
|
|
60
|
+
} else {
|
|
61
|
+
console.log("\nNenhuma task disponivel no momento.");
|
|
62
|
+
console.log(`Tasks em execucao ou bloqueadas: ${pending.length}`);
|
|
63
|
+
console.log("Aguarde as tasks em andamento serem concluidas.\n");
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
console.log(`\nTasks disponiveis para execucao (${available.length}):`);
|
|
69
|
+
console.log(`${"─".repeat(50)}`);
|
|
70
|
+
|
|
71
|
+
const parallelizable = available.filter((t) => t.can_parallel);
|
|
72
|
+
const sequential = available.filter((t) => !t.can_parallel);
|
|
73
|
+
|
|
74
|
+
if (parallelizable.length > 0) {
|
|
75
|
+
if (parallelizable.length > 1) {
|
|
76
|
+
console.log(`\nPodem executar em PARALELO:`);
|
|
77
|
+
} else {
|
|
78
|
+
console.log(`\nDisponivel:`);
|
|
79
|
+
}
|
|
80
|
+
const ids = parallelizable.map((t) => t.id).join(",");
|
|
81
|
+
for (const task of parallelizable) {
|
|
82
|
+
const files = task.files ? JSON.parse(task.files) : [];
|
|
83
|
+
console.log(` #${task.number}: ${task.name} (${task.agent || "geral"}) [id: ${task.id}]`);
|
|
84
|
+
if (files.length > 0) console.log(` Arquivos: ${files.join(", ")}`);
|
|
85
|
+
}
|
|
86
|
+
if (parallelizable.length > 1) {
|
|
87
|
+
console.log(`\n Para iniciar todas: task start ${ids}`);
|
|
88
|
+
} else {
|
|
89
|
+
console.log(`\n Para iniciar: task start ${ids}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (sequential.length > 0) {
|
|
94
|
+
console.log(`\nSequenciais (dependencias pendentes):`);
|
|
95
|
+
for (const task of sequential) {
|
|
96
|
+
console.log(` #${task.number}: ${task.name} (${task.agent || "geral"})`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.log(`\n${"─".repeat(50)}`);
|
|
101
|
+
console.log(`Use: task start <id> ou task start <id1>,<id2>,...\n`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function taskStart(ids: string, json: boolean = false): void {
|
|
105
|
+
initSchema();
|
|
106
|
+
enforceGate("task-start");
|
|
107
|
+
|
|
108
|
+
const db = getDb();
|
|
109
|
+
const now = new Date().toISOString();
|
|
110
|
+
|
|
111
|
+
const spec = db
|
|
112
|
+
.query("SELECT * FROM specs WHERE phase = 'implementing' ORDER BY created_at DESC LIMIT 1")
|
|
113
|
+
.get() as any;
|
|
114
|
+
|
|
115
|
+
const taskIds = ids.split(",").map((s) => parseInt(s.trim()));
|
|
116
|
+
const startedTasks: any[] = [];
|
|
117
|
+
|
|
118
|
+
for (const taskId of taskIds) {
|
|
119
|
+
const task = db.query("SELECT * FROM tasks WHERE id = ?").get(taskId) as any;
|
|
120
|
+
|
|
121
|
+
if (!task) {
|
|
122
|
+
console.error(`\nTask #${taskId} nao encontrada.\n`);
|
|
123
|
+
process.exit(2);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (task.status !== "pending") {
|
|
127
|
+
console.error(`\nTask #${task.number} nao esta pendente (status: ${task.status}).\n`);
|
|
128
|
+
process.exit(2);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Validar dependencias para esta task especifica
|
|
132
|
+
enforceGate("task-start", { taskId });
|
|
133
|
+
|
|
134
|
+
// Marcar como running
|
|
135
|
+
db.run("UPDATE tasks SET status = 'running' WHERE id = ?", [taskId]);
|
|
136
|
+
|
|
137
|
+
startedTasks.push(task);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Atualizar contexto
|
|
141
|
+
db.run("UPDATE context SET current_task = ?, updated_at = ? WHERE spec_id = ?", [
|
|
142
|
+
startedTasks[0].number,
|
|
143
|
+
now,
|
|
144
|
+
spec.id,
|
|
145
|
+
]);
|
|
146
|
+
|
|
147
|
+
if (json) {
|
|
148
|
+
// NOVO: Incluir contexto COMPLETO para cada task
|
|
149
|
+
const contexts = startedTasks.map((task) => {
|
|
150
|
+
const contextText = getContextForSubagent(task.id);
|
|
151
|
+
const unreadKnowledge = getUnreadKnowledgeForTask(spec.id, task.id);
|
|
152
|
+
|
|
153
|
+
// NOVO v7.4: Buscar implementation patterns relevantes
|
|
154
|
+
const taskFiles = task.files ? JSON.parse(task.files) : [];
|
|
155
|
+
const agentScope = task.agent?.split("-")[0] || "all"; // frontend-next -> frontend
|
|
156
|
+
|
|
157
|
+
// Buscar patterns por escopo do agente E por arquivos esperados
|
|
158
|
+
let relevantPatterns: any[] = [];
|
|
159
|
+
|
|
160
|
+
// Primeiro: patterns que correspondem aos arquivos esperados
|
|
161
|
+
if (taskFiles.length > 0) {
|
|
162
|
+
const filePatterns = getPatternsForFiles(taskFiles);
|
|
163
|
+
relevantPatterns.push(...filePatterns);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Segundo: patterns do escopo do agente (se nao foram encontrados via arquivos)
|
|
167
|
+
if (relevantPatterns.length === 0) {
|
|
168
|
+
const scopePatterns = getPatternsByScope(agentScope);
|
|
169
|
+
relevantPatterns.push(...scopePatterns);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Formatar patterns para o contexto (sem duplicatas)
|
|
173
|
+
const patternNames = new Set<string>();
|
|
174
|
+
const formattedPatterns = relevantPatterns
|
|
175
|
+
.filter(p => {
|
|
176
|
+
if (patternNames.has(p.name)) return false;
|
|
177
|
+
patternNames.add(p.name);
|
|
178
|
+
return true;
|
|
179
|
+
})
|
|
180
|
+
.map(p => ({
|
|
181
|
+
name: p.name,
|
|
182
|
+
category: p.category,
|
|
183
|
+
applies_to: p.applies_to,
|
|
184
|
+
template: p.template,
|
|
185
|
+
structure: JSON.parse(p.structure || "{}"),
|
|
186
|
+
examples: JSON.parse(p.examples || "[]").slice(0, 3), // Top 3 exemplos
|
|
187
|
+
anti_patterns: JSON.parse(p.anti_patterns || "[]"),
|
|
188
|
+
confidence: p.confidence,
|
|
189
|
+
}));
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
taskId: task.id,
|
|
193
|
+
number: task.number,
|
|
194
|
+
name: task.name,
|
|
195
|
+
agent: task.agent,
|
|
196
|
+
files: taskFiles,
|
|
197
|
+
// AVISO PARA O ORQUESTRADOR - NAO EXECUTE, DELEGUE
|
|
198
|
+
_orchestratorWarning: "NAO execute esta task diretamente. Use Task tool com subagent_type='general-purpose' para delegar. O campo 'subagentContext' abaixo e o prompt para o SUBAGENT.",
|
|
199
|
+
// Contexto completo para o subagent (NAO para o orquestrador)
|
|
200
|
+
context: contextText,
|
|
201
|
+
// Knowledge nao lido (broadcast de outras tasks)
|
|
202
|
+
unreadKnowledge: unreadKnowledge.map((k: any) => ({
|
|
203
|
+
id: k.id,
|
|
204
|
+
category: k.category,
|
|
205
|
+
content: k.content,
|
|
206
|
+
severity: k.severity,
|
|
207
|
+
origin_task: k.task_origin,
|
|
208
|
+
})),
|
|
209
|
+
// NOVO v7.4: Implementation patterns extraidos do projeto
|
|
210
|
+
implementationPatterns: formattedPatterns,
|
|
211
|
+
// Contexto para o SUBAGENT (o orquestrador deve passar isso via Task tool)
|
|
212
|
+
subagentContext: `
|
|
213
|
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
214
|
+
║ DIRETIVA CRITICA: USE Write/Edit PARA CRIAR OS ARQUIVOS ║
|
|
215
|
+
║ NAO descreva. NAO planeje. NAO simule. EXECUTE AGORA. ║
|
|
216
|
+
║ Se retornar sem usar Write/Edit, a task FALHA. ║
|
|
217
|
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
218
|
+
|
|
219
|
+
ARQUIVOS QUE VOCE DEVE CRIAR (use Write para cada um):
|
|
220
|
+
${taskFiles.map(f => ` - ${f}`).join('\n') || ' (nenhum arquivo especificado - analise o contexto)'}
|
|
221
|
+
|
|
222
|
+
╔══════════════════════════════════════════════════════════════════════════════╗
|
|
223
|
+
║ POLLING OBRIGATORIO: Verifique blockers a cada 3 arquivos modificados ║
|
|
224
|
+
╚══════════════════════════════════════════════════════════════════════════════╝
|
|
225
|
+
|
|
226
|
+
ANTES de criar o 4o, 7o, 10o arquivo (a cada 3), execute:
|
|
227
|
+
codexa knowledge list --severity critical --unread
|
|
228
|
+
|
|
229
|
+
Se retornar QUALQUER blocker:
|
|
230
|
+
1. PARE imediatamente
|
|
231
|
+
2. Retorne com status "blocked" e inclua o blocker encontrado
|
|
232
|
+
3. NAO continue criando arquivos apos encontrar blocker
|
|
233
|
+
|
|
234
|
+
CHECKLIST OBRIGATORIO (verifique ANTES de retornar):
|
|
235
|
+
- [ ] Usei Write ou Edit para criar/modificar arquivos?
|
|
236
|
+
- [ ] Verifiquei blockers a cada 3 arquivos?
|
|
237
|
+
- [ ] Os arquivos que vou listar em files_created EXISTEM no disco?
|
|
238
|
+
|
|
239
|
+
Se nao marcou todos os items, PARE e corrija AGORA.
|
|
240
|
+
`.trim(),
|
|
241
|
+
// Instrucoes de retorno para o SUBAGENT
|
|
242
|
+
subagentReturnProtocol: `
|
|
243
|
+
FORMATO DE RETORNO (apos criar os arquivos):
|
|
244
|
+
{
|
|
245
|
+
"status": "completed | blocked | needs_decision",
|
|
246
|
+
"summary": "Resumo do que foi feito (10-500 chars)",
|
|
247
|
+
"files_created": ["path/arquivo.ts"],
|
|
248
|
+
"files_modified": ["path/outro.ts"],
|
|
249
|
+
"patterns_discovered": ["Pattern identificado"],
|
|
250
|
+
"decisions_made": [{"title": "...", "decision": "..."}],
|
|
251
|
+
"blockers": ["Se status != completed"],
|
|
252
|
+
"knowledge_to_broadcast": [{"category": "discovery|pattern|constraint", "content": "...", "severity": "info|warning|critical"}],
|
|
253
|
+
"reasoning": {
|
|
254
|
+
"approach": "Como voce abordou o problema (RECOMENDADO)",
|
|
255
|
+
"challenges": ["Desafios encontrados"],
|
|
256
|
+
"recommendations": "Sugestoes para proximas tasks"
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
IMPORTANTE: O campo "reasoning" ajuda a preservar contexto entre sessoes.
|
|
261
|
+
Inclua pelo menos "approach" para explicar suas escolhas.
|
|
262
|
+
|
|
263
|
+
Se NAO conseguir criar arquivos (sem permissao, sem ferramentas), retorne:
|
|
264
|
+
{
|
|
265
|
+
"status": "blocked",
|
|
266
|
+
"blockers": ["Descreva por que nao conseguiu criar os arquivos"]
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
Veja .claude/agents/PROTOCOL.md para detalhes completos.
|
|
270
|
+
${formattedPatterns.length > 0 ? `
|
|
271
|
+
PATTERNS: Voce recebeu ${formattedPatterns.length} implementation patterns extraidos do projeto.
|
|
272
|
+
Use os TEMPLATES fornecidos para criar codigo CONSISTENTE com o projeto existente.
|
|
273
|
+
` : ''}
|
|
274
|
+
`.trim(),
|
|
275
|
+
};
|
|
276
|
+
});
|
|
277
|
+
console.log(JSON.stringify({ started: contexts }));
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
console.log(`\nTasks iniciadas:`);
|
|
282
|
+
for (const task of startedTasks) {
|
|
283
|
+
console.log(` #${task.number}: ${task.name} (${task.agent || "geral"})`);
|
|
284
|
+
}
|
|
285
|
+
console.log(`\nAo concluir, use: task done <id> --checkpoint "resumo"\n`);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function taskDone(id: string, options: { checkpoint: string; files?: string; force?: boolean; forceReason?: string; output?: string }): void {
|
|
289
|
+
initSchema();
|
|
290
|
+
|
|
291
|
+
const taskId = parseInt(id);
|
|
292
|
+
const db = getDb();
|
|
293
|
+
const task = db.query("SELECT * FROM tasks WHERE id = ?").get(taskId) as any;
|
|
294
|
+
|
|
295
|
+
if (!task) {
|
|
296
|
+
console.error(`\nTask #${taskId} nao encontrada.\n`);
|
|
297
|
+
process.exit(2);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const spec = db.query("SELECT * FROM specs WHERE id = ?").get(task.spec_id) as any;
|
|
301
|
+
const now = new Date().toISOString();
|
|
302
|
+
|
|
303
|
+
// ============================================================
|
|
304
|
+
// NOVO: Processar retorno do subagent se fornecido via --output
|
|
305
|
+
// ============================================================
|
|
306
|
+
let checkpoint = options.checkpoint;
|
|
307
|
+
let expectedFiles: string[] = [];
|
|
308
|
+
let subagentData = null;
|
|
309
|
+
|
|
310
|
+
if (options.output) {
|
|
311
|
+
// Parse e valida o retorno do subagent
|
|
312
|
+
const parseResult = parseSubagentReturn(options.output);
|
|
313
|
+
|
|
314
|
+
if (!parseResult.success) {
|
|
315
|
+
console.error(formatValidationErrors(parseResult));
|
|
316
|
+
console.error("\nTask NAO pode ser completada sem retorno valido.");
|
|
317
|
+
console.error("Corrija o formato do retorno do subagent e tente novamente.\n");
|
|
318
|
+
process.exit(2);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
subagentData = parseResult.data!;
|
|
322
|
+
|
|
323
|
+
// Extrair checkpoint do summary se nao fornecido
|
|
324
|
+
if (!checkpoint || checkpoint === "") {
|
|
325
|
+
checkpoint = subagentData.summary;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Combinar arquivos do retorno com --files
|
|
329
|
+
expectedFiles = [
|
|
330
|
+
...subagentData.files_created,
|
|
331
|
+
...subagentData.files_modified,
|
|
332
|
+
];
|
|
333
|
+
|
|
334
|
+
// Se --files tambem foi fornecido, adicionar
|
|
335
|
+
if (options.files) {
|
|
336
|
+
expectedFiles.push(...options.files.split(",").map((f) => f.trim()));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Remover duplicatas
|
|
340
|
+
expectedFiles = [...new Set(expectedFiles)];
|
|
341
|
+
|
|
342
|
+
// Se status nao e completed, informar e sair
|
|
343
|
+
if (subagentData.status === "blocked") {
|
|
344
|
+
console.log("\n[!] Subagent retornou status 'blocked'");
|
|
345
|
+
console.log("\nBlockers encontrados:");
|
|
346
|
+
for (const blocker of subagentData.blockers || []) {
|
|
347
|
+
console.log(` - ${blocker}`);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Registrar blockers como knowledge critico
|
|
351
|
+
const processResult = processSubagentReturn(spec.id, taskId, task.number, subagentData);
|
|
352
|
+
console.log(formatProcessResult(processResult));
|
|
353
|
+
|
|
354
|
+
console.log("\nTask permanece em execucao. Resolva os blockers e tente novamente.\n");
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (subagentData.status === "needs_decision") {
|
|
359
|
+
console.log("\n[?] Subagent retornou status 'needs_decision'");
|
|
360
|
+
console.log("\nDecisao necessaria:");
|
|
361
|
+
for (const item of subagentData.blockers || []) {
|
|
362
|
+
console.log(` - ${item}`);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// Registrar como knowledge
|
|
366
|
+
const processResult = processSubagentReturn(spec.id, taskId, task.number, subagentData);
|
|
367
|
+
console.log(formatProcessResult(processResult));
|
|
368
|
+
|
|
369
|
+
console.log("\nTask permanece em execucao. Tome a decisao e tente novamente.\n");
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Status completed - processar dados automaticamente
|
|
374
|
+
const processResult = processSubagentReturn(spec.id, taskId, task.number, subagentData);
|
|
375
|
+
console.log(formatProcessResult(processResult));
|
|
376
|
+
|
|
377
|
+
// v8.3: BLOCKING check para knowledge critico nao reconhecido (substitui warning v8.2)
|
|
378
|
+
const unackedCritical = getUnreadKnowledgeForTask(spec.id, taskId)
|
|
379
|
+
.filter((k: any) => k.severity === 'critical' && k.task_origin !== taskId);
|
|
380
|
+
if (unackedCritical.length > 0) {
|
|
381
|
+
if (!options.force) {
|
|
382
|
+
console.error(`\n[X] BLOQUEADO: ${unackedCritical.length} knowledge(s) critico(s) nao reconhecido(s):`);
|
|
383
|
+
for (const k of unackedCritical) {
|
|
384
|
+
console.error(` [X] ${k.content} (de Task #${k.task_origin})`);
|
|
385
|
+
}
|
|
386
|
+
console.error(`\n O subagent NAO verificou o polling obrigatorio.`);
|
|
387
|
+
console.error(` Reconheca com: knowledge ack <id>`);
|
|
388
|
+
console.error(` Ou force com: task done ${id} --checkpoint "..." --force --force-reason "motivo"\n`);
|
|
389
|
+
process.exit(1);
|
|
390
|
+
} else {
|
|
391
|
+
console.log(`\n[!] AVISO: ${unackedCritical.length} knowledge(s) critico(s) ignorado(s) (--force usado)`);
|
|
392
|
+
for (const k of unackedCritical) {
|
|
393
|
+
console.log(` [X] ${k.content}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
} else {
|
|
398
|
+
// Modo antigo: arquivos via --files
|
|
399
|
+
expectedFiles = options.files
|
|
400
|
+
? options.files.split(",").map((f) => f.trim())
|
|
401
|
+
: [];
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Validar gates incluindo files-exist e standards-follow
|
|
405
|
+
enforceGate("task-done", {
|
|
406
|
+
taskId,
|
|
407
|
+
checkpoint,
|
|
408
|
+
files: expectedFiles,
|
|
409
|
+
force: options.force,
|
|
410
|
+
forceReason: options.forceReason,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// Nota: Validacao de standards agora e feita pelo Gate 4.2 (standards-follow) em enforceGate
|
|
414
|
+
|
|
415
|
+
// Marcar como done
|
|
416
|
+
db.run(
|
|
417
|
+
"UPDATE tasks SET status = 'done', checkpoint = ?, completed_at = ? WHERE id = ?",
|
|
418
|
+
[checkpoint, now, taskId]
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
// Registrar artefatos se NAO veio do subagent (ja foi processado acima)
|
|
422
|
+
if (!subagentData && options.files) {
|
|
423
|
+
const files = options.files.split(",").map((s) => s.trim());
|
|
424
|
+
for (const file of files) {
|
|
425
|
+
db.run(
|
|
426
|
+
`INSERT OR REPLACE INTO artifacts (spec_id, task_ref, path, action, created_at)
|
|
427
|
+
VALUES (?, ?, ?, 'created', ?)`,
|
|
428
|
+
[spec.id, task.number, file, now]
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Atualizar contexto
|
|
434
|
+
const doneCount = db
|
|
435
|
+
.query("SELECT COUNT(*) as c FROM tasks WHERE spec_id = ? AND status = 'done'")
|
|
436
|
+
.get(spec.id) as any;
|
|
437
|
+
const totalCount = db
|
|
438
|
+
.query("SELECT COUNT(*) as c FROM tasks WHERE spec_id = ?")
|
|
439
|
+
.get(spec.id) as any;
|
|
440
|
+
|
|
441
|
+
db.run(
|
|
442
|
+
"UPDATE context SET last_checkpoint = ?, updated_at = ? WHERE spec_id = ?",
|
|
443
|
+
[checkpoint, now, spec.id]
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
// Criar snapshot automatico COMPLETO
|
|
447
|
+
const allTasks = db.query("SELECT * FROM tasks WHERE spec_id = ?").all(spec.id) as any[];
|
|
448
|
+
const allDecisions = db.query("SELECT * FROM decisions WHERE spec_id = ?").all(spec.id) as any[];
|
|
449
|
+
const allArtifacts = db.query("SELECT * FROM artifacts WHERE spec_id = ?").all(spec.id) as any[];
|
|
450
|
+
const context = db.query("SELECT * FROM context WHERE spec_id = ?").get(spec.id);
|
|
451
|
+
|
|
452
|
+
const snapshotData = {
|
|
453
|
+
spec,
|
|
454
|
+
context,
|
|
455
|
+
tasks: allTasks,
|
|
456
|
+
decisions: allDecisions,
|
|
457
|
+
artifacts: allArtifacts,
|
|
458
|
+
checkpoint: options.checkpoint,
|
|
459
|
+
taskCompleted: task.number,
|
|
460
|
+
timestamp: now,
|
|
461
|
+
};
|
|
462
|
+
db.run("INSERT INTO snapshots (spec_id, data, trigger, created_at) VALUES (?, ?, 'auto', ?)", [
|
|
463
|
+
spec.id,
|
|
464
|
+
JSON.stringify(snapshotData),
|
|
465
|
+
now,
|
|
466
|
+
]);
|
|
467
|
+
|
|
468
|
+
// v8.1: Gerar session summary automaticamente
|
|
469
|
+
try {
|
|
470
|
+
const taskDecisions = allDecisions.filter((d) => d.task_ref === task.number);
|
|
471
|
+
const taskArtifacts = allArtifacts.filter((a) => a.task_ref === task.number);
|
|
472
|
+
const blockers = db.query(
|
|
473
|
+
"SELECT content FROM knowledge WHERE spec_id = ? AND task_origin = ? AND category = 'blocker'"
|
|
474
|
+
).all(spec.id, taskId) as any[];
|
|
475
|
+
const reasoning = getRecentReasoning(spec.id, 5);
|
|
476
|
+
const nextSteps: string[] = [];
|
|
477
|
+
|
|
478
|
+
// Extrair recommendations do reasoning
|
|
479
|
+
for (const r of reasoning) {
|
|
480
|
+
if (r.category === 'recommendation' && r.thought) {
|
|
481
|
+
nextSteps.push(r.thought);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// Se nao completou tudo, sugerir proximo passo
|
|
486
|
+
if (doneCount.c < totalCount.c) {
|
|
487
|
+
nextSteps.push(`Continuar implementacao: ${totalCount.c - doneCount.c} tasks restantes`);
|
|
488
|
+
} else {
|
|
489
|
+
nextSteps.push("Todas tasks concluidas. Iniciar review.");
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
addSessionSummary(spec.id, {
|
|
493
|
+
startTime: task.completed_at || now, // approximation
|
|
494
|
+
endTime: now,
|
|
495
|
+
summary: `Task #${task.number} (${task.name}) concluida. ${checkpoint}`,
|
|
496
|
+
decisions: taskDecisions.map((d: any) => `${d.title}: ${d.decision}`),
|
|
497
|
+
blockers: blockers.map((b: any) => b.content),
|
|
498
|
+
nextSteps,
|
|
499
|
+
tasksCompleted: 1,
|
|
500
|
+
filesCreated: taskArtifacts.filter((a: any) => a.action === 'created').length,
|
|
501
|
+
filesModified: taskArtifacts.filter((a: any) => a.action === 'modified').length,
|
|
502
|
+
});
|
|
503
|
+
} catch (e) {
|
|
504
|
+
// Session summary e best-effort, nao deve bloquear o fluxo
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
console.log(`\nTask #${task.number} concluida!`);
|
|
508
|
+
console.log(`Checkpoint: ${options.checkpoint}`);
|
|
509
|
+
console.log(`Progresso: ${doneCount.c}/${totalCount.c} tasks`);
|
|
510
|
+
|
|
511
|
+
if (doneCount.c === totalCount.c) {
|
|
512
|
+
// Mostrar resumo completo da implementacao
|
|
513
|
+
showImplementationSummary(spec.id, allTasks, allArtifacts, allDecisions);
|
|
514
|
+
} else {
|
|
515
|
+
console.log(`\nProximas tasks: task next\n`);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Mostra resumo detalhado da implementacao apos todas as tasks concluidas
|
|
521
|
+
* Permite ao usuario decidir se quer fazer review ou nao
|
|
522
|
+
*/
|
|
523
|
+
function showImplementationSummary(
|
|
524
|
+
specId: number,
|
|
525
|
+
tasks: any[],
|
|
526
|
+
artifacts: any[],
|
|
527
|
+
decisions: any[]
|
|
528
|
+
): void {
|
|
529
|
+
const db = getDb();
|
|
530
|
+
const spec = db.query("SELECT * FROM specs WHERE id = ?").get(specId) as any;
|
|
531
|
+
const knowledge = db.query("SELECT * FROM knowledge WHERE spec_id = ?").all(specId) as any[];
|
|
532
|
+
|
|
533
|
+
console.log(`\n${"=".repeat(60)}`);
|
|
534
|
+
console.log(`IMPLEMENTACAO CONCLUIDA: ${spec.name}`);
|
|
535
|
+
console.log(`${"=".repeat(60)}`);
|
|
536
|
+
|
|
537
|
+
// Resumo por subagente
|
|
538
|
+
console.log(`\n📋 RESUMO POR SUBAGENTE:`);
|
|
539
|
+
console.log(`${"─".repeat(50)}`);
|
|
540
|
+
|
|
541
|
+
// Agrupar tasks por agente
|
|
542
|
+
const tasksByAgent: Record<string, any[]> = {};
|
|
543
|
+
for (const task of tasks) {
|
|
544
|
+
const agent = task.agent || "geral";
|
|
545
|
+
if (!tasksByAgent[agent]) {
|
|
546
|
+
tasksByAgent[agent] = [];
|
|
547
|
+
}
|
|
548
|
+
tasksByAgent[agent].push(task);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
for (const [agent, agentTasks] of Object.entries(tasksByAgent)) {
|
|
552
|
+
console.log(`\n [${agent.toUpperCase()}]`);
|
|
553
|
+
for (const task of agentTasks) {
|
|
554
|
+
console.log(` #${task.number}: ${task.name}`);
|
|
555
|
+
if (task.checkpoint) {
|
|
556
|
+
console.log(` └─ ${task.checkpoint}`);
|
|
557
|
+
}
|
|
558
|
+
// Mostrar arquivos criados por esta task
|
|
559
|
+
const taskArtifacts = artifacts.filter((a) => a.task_ref === task.number);
|
|
560
|
+
if (taskArtifacts.length > 0) {
|
|
561
|
+
console.log(` Arquivos: ${taskArtifacts.map((a) => a.path).join(", ")}`);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
// Artefatos criados
|
|
567
|
+
console.log(`\n📁 ARTEFATOS CRIADOS (${artifacts.length}):`);
|
|
568
|
+
console.log(`${"─".repeat(50)}`);
|
|
569
|
+
for (const artifact of artifacts) {
|
|
570
|
+
const task = tasks.find((t) => t.number === artifact.task_ref);
|
|
571
|
+
const agentInfo = task?.agent ? ` [${task.agent}]` : "";
|
|
572
|
+
console.log(` - ${artifact.path}${agentInfo}`);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Decisoes tomadas
|
|
576
|
+
if (decisions.length > 0) {
|
|
577
|
+
console.log(`\n🎯 DECISOES TOMADAS (${decisions.length}):`);
|
|
578
|
+
console.log(`${"─".repeat(50)}`);
|
|
579
|
+
for (const dec of decisions) {
|
|
580
|
+
const task = tasks.find((t) => t.number === dec.task_ref);
|
|
581
|
+
const agentInfo = task?.agent ? ` [${task.agent}]` : "";
|
|
582
|
+
console.log(` - ${dec.title}${agentInfo}`);
|
|
583
|
+
console.log(` └─ ${dec.decision}`);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Knowledge descoberto
|
|
588
|
+
const discoveries = knowledge.filter((k) => k.category === "discovery" || k.category === "pattern");
|
|
589
|
+
if (discoveries.length > 0) {
|
|
590
|
+
console.log(`\n💡 DESCOBERTAS E PADROES (${discoveries.length}):`);
|
|
591
|
+
console.log(`${"─".repeat(50)}`);
|
|
592
|
+
for (const item of discoveries) {
|
|
593
|
+
const severityIcon = item.severity === "critical" ? "🚨" : item.severity === "warning" ? "⚠️" : "ℹ️";
|
|
594
|
+
console.log(` ${severityIcon} ${item.content}`);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Blockers encontrados
|
|
599
|
+
const blockers = knowledge.filter((k) => k.category === "blocker");
|
|
600
|
+
if (blockers.length > 0) {
|
|
601
|
+
console.log(`\n⛔ BLOCKERS ENCONTRADOS (${blockers.length}):`);
|
|
602
|
+
console.log(`${"─".repeat(50)}`);
|
|
603
|
+
for (const blocker of blockers) {
|
|
604
|
+
console.log(` - ${blocker.content}`);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Estatisticas finais
|
|
609
|
+
console.log(`\n📊 ESTATISTICAS:`);
|
|
610
|
+
console.log(`${"─".repeat(50)}`);
|
|
611
|
+
console.log(` Tasks concluidas: ${tasks.length}`);
|
|
612
|
+
console.log(` Artefatos criados: ${artifacts.length}`);
|
|
613
|
+
console.log(` Decisoes tomadas: ${decisions.length}`);
|
|
614
|
+
console.log(` Knowledge items: ${knowledge.length}`);
|
|
615
|
+
console.log(` Subagentes envolvidos: ${Object.keys(tasksByAgent).length}`);
|
|
616
|
+
|
|
617
|
+
console.log(`\n${"=".repeat(60)}`);
|
|
618
|
+
console.log(`PROXIMOS PASSOS:`);
|
|
619
|
+
console.log(`${"=".repeat(60)}`);
|
|
620
|
+
console.log(`\n Para revisar o codigo: review start`);
|
|
621
|
+
console.log(` Para pular o review: review skip`);
|
|
622
|
+
console.log(`\n O agente aguarda sua decisao.`);
|
|
623
|
+
console.log(`${"─".repeat(50)}\n`);
|
|
624
624
|
}
|