@codexa/cli 8.6.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.
@@ -1,451 +1,451 @@
1
- /**
2
- * Process Subagent Return
3
- *
4
- * Este modulo processa automaticamente o retorno de um subagent,
5
- * extraindo e registrando:
6
- * - Knowledge broadcast
7
- * - Decisions
8
- * - Artifacts (files created/modified)
9
- * - Patterns descobertos
10
- */
11
-
12
- import { getDb } from "../db/connection";
13
- import { SubagentReturn, Knowledge } from "./subagent-protocol";
14
- import { addReasoning, addGraphRelation, getRelatedFiles, upsertUtility } from "../db/schema";
15
- import { extractUtilitiesFromFile, inferScopeFromPath } from "../commands/patterns";
16
-
17
- interface ProcessResult {
18
- success: boolean;
19
- knowledgeAdded: number;
20
- decisionsAdded: number;
21
- artifactsAdded: number;
22
- patternsAdded: number;
23
- reasoningAdded: number; // v8.0
24
- relationsAdded: number; // v8.0
25
- utilitiesRegistered: number; // v8.5
26
- errors: string[];
27
- }
28
-
29
- function getNextDecisionId(specId: string): string {
30
- const db = getDb();
31
- const last = db
32
- .query("SELECT id FROM decisions WHERE spec_id = ? ORDER BY created_at DESC LIMIT 1")
33
- .get(specId) as any;
34
-
35
- if (!last) return "DEC-001";
36
-
37
- const num = parseInt(last.id.replace("DEC-", "")) + 1;
38
- return `DEC-${num.toString().padStart(3, "0")}`;
39
- }
40
-
41
- /**
42
- * Processa o retorno de um subagent e registra automaticamente
43
- * todos os dados extraidos no banco
44
- */
45
- export function processSubagentReturn(
46
- specId: string,
47
- taskId: number,
48
- taskNumber: number,
49
- data: SubagentReturn
50
- ): ProcessResult {
51
- const db = getDb();
52
- const now = new Date().toISOString();
53
- const result: ProcessResult = {
54
- success: true,
55
- knowledgeAdded: 0,
56
- decisionsAdded: 0,
57
- artifactsAdded: 0,
58
- patternsAdded: 0,
59
- reasoningAdded: 0,
60
- relationsAdded: 0,
61
- utilitiesRegistered: 0,
62
- errors: [],
63
- };
64
-
65
- // 1. Registrar Knowledge Broadcast (v8.3: com deduplicacao)
66
- if (data.knowledge_to_broadcast && data.knowledge_to_broadcast.length > 0) {
67
- for (const k of data.knowledge_to_broadcast) {
68
- try {
69
- // v8.3: Deduplicacao - verificar se knowledge identico ja existe
70
- const existing = db.query(
71
- `SELECT id FROM knowledge WHERE spec_id = ? AND category = ? AND content = ? LIMIT 1`
72
- ).get(specId, k.category, k.content) as any;
73
-
74
- if (existing) continue; // Skip duplicata
75
-
76
- db.run(
77
- `INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
78
- VALUES (?, ?, ?, ?, ?, 'all', ?)`,
79
- [specId, taskId, k.category, k.content, k.severity, now]
80
- );
81
- result.knowledgeAdded++;
82
- } catch (e) {
83
- result.errors.push(`Erro ao registrar knowledge: ${(e as Error).message}`);
84
- }
85
- }
86
- }
87
-
88
- // 2. Registrar Decisions
89
- if (data.decisions_made && data.decisions_made.length > 0) {
90
- for (const dec of data.decisions_made) {
91
- try {
92
- const decisionId = getNextDecisionId(specId);
93
- db.run(
94
- `INSERT INTO decisions (id, spec_id, task_ref, title, decision, rationale, status, created_at)
95
- VALUES (?, ?, ?, ?, ?, ?, 'active', ?)`,
96
- [decisionId, specId, taskNumber, dec.title, dec.decision, dec.rationale || null, now]
97
- );
98
- result.decisionsAdded++;
99
- } catch (e) {
100
- result.errors.push(`Erro ao registrar decision: ${(e as Error).message}`);
101
- }
102
- }
103
- }
104
-
105
- // 2b. v8.3: Deteccao automatica de contradicoes entre decisoes
106
- if (data.decisions_made && data.decisions_made.length > 0) {
107
- try {
108
- const existingDecisions = db.query(
109
- "SELECT id, title, decision FROM decisions WHERE spec_id = ? AND status = 'active'"
110
- ).all(specId) as any[];
111
-
112
- const allTaskFiles = [...data.files_created, ...data.files_modified];
113
-
114
- for (const newDec of data.decisions_made) {
115
- for (const existingDec of existingDecisions) {
116
- // Skip se mesma decisao (recem adicionada)
117
- if (existingDec.title === newDec.title) continue;
118
-
119
- const newDecLower = `${newDec.title} ${newDec.decision}`.toLowerCase();
120
- const existDecLower = `${existingDec.title} ${existingDec.decision}`.toLowerCase();
121
-
122
- // Verificar overlap de arquivos
123
- const existingDecFiles = getRelatedFiles(existingDec.id, "decision");
124
- const fileOverlap = allTaskFiles.some(f => existingDecFiles.includes(f));
125
-
126
- if (fileOverlap) {
127
- // Heuristica de keywords opostos
128
- const opposites = [
129
- ['usar', 'nao usar'], ['usar', 'evitar'],
130
- ['use', 'avoid'], ['add', 'remove'],
131
- ['incluir', 'excluir'], ['habilitar', 'desabilitar'],
132
- ['enable', 'disable'], ['create', 'delete'],
133
- ['client', 'server'], ['sync', 'async'],
134
- ];
135
-
136
- let isContradiction = false;
137
- for (const [word1, word2] of opposites) {
138
- if ((newDecLower.includes(word1) && existDecLower.includes(word2)) ||
139
- (newDecLower.includes(word2) && existDecLower.includes(word1))) {
140
- isContradiction = true;
141
- break;
142
- }
143
- }
144
-
145
- if (isContradiction) {
146
- const newDecId = db.query(
147
- "SELECT id FROM decisions WHERE spec_id = ? AND title = ? ORDER BY created_at DESC LIMIT 1"
148
- ).get(specId, newDec.title) as any;
149
-
150
- if (newDecId) {
151
- addGraphRelation(specId, {
152
- sourceType: "decision",
153
- sourceId: newDecId.id,
154
- targetType: "decision",
155
- targetId: existingDec.id,
156
- relation: "contradicts",
157
- strength: 0.7,
158
- metadata: { reason: "Arquivos sobrepostos com semantica oposta" },
159
- });
160
- result.relationsAdded++;
161
- }
162
- }
163
- }
164
- }
165
- }
166
- } catch (e) {
167
- // Nao-critico: nao falhar processamento por deteccao de contradicoes
168
- result.errors.push(`Aviso contradicoes: ${(e as Error).message}`);
169
- }
170
- }
171
-
172
- // 3. Registrar Artifacts (files_created e files_modified)
173
- const allFiles = [
174
- ...data.files_created.map((f) => ({ path: f, action: "created" })),
175
- ...data.files_modified.map((f) => ({ path: f, action: "modified" })),
176
- ];
177
-
178
- for (const file of allFiles) {
179
- try {
180
- db.run(
181
- `INSERT OR REPLACE INTO artifacts (spec_id, task_ref, path, action, created_at)
182
- VALUES (?, ?, ?, ?, ?)`,
183
- [specId, taskNumber, file.path, file.action, now]
184
- );
185
- result.artifactsAdded++;
186
- } catch (e) {
187
- result.errors.push(`Erro ao registrar artifact: ${(e as Error).message}`);
188
- }
189
- }
190
-
191
- // 4. Registrar Patterns descobertos no contexto
192
- if (data.patterns_discovered && data.patterns_discovered.length > 0) {
193
- try {
194
- const context = db.query("SELECT * FROM context WHERE spec_id = ?").get(specId) as any;
195
- const currentPatterns = context?.patterns ? JSON.parse(context.patterns) : [];
196
-
197
- for (const pattern of data.patterns_discovered) {
198
- currentPatterns.push({
199
- pattern,
200
- detected_at: now,
201
- source_task: taskNumber,
202
- });
203
- result.patternsAdded++;
204
- }
205
-
206
- db.run(
207
- "UPDATE context SET patterns = ?, updated_at = ? WHERE spec_id = ?",
208
- [JSON.stringify(currentPatterns), now, specId]
209
- );
210
- } catch (e) {
211
- result.errors.push(`Erro ao registrar patterns: ${(e as Error).message}`);
212
- }
213
- }
214
-
215
- // 4b. v8.3: Broadcast patterns como knowledge (com deduplicacao)
216
- if (data.patterns_discovered && data.patterns_discovered.length > 0) {
217
- for (const pattern of data.patterns_discovered) {
218
- try {
219
- const content = `Pattern descoberto: ${pattern}`;
220
- const existing = db.query(
221
- `SELECT id FROM knowledge WHERE spec_id = ? AND category = 'pattern' AND content = ? LIMIT 1`
222
- ).get(specId, content) as any;
223
-
224
- if (!existing) {
225
- db.run(
226
- `INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
227
- VALUES (?, ?, 'pattern', ?, 'info', 'all', ?)`,
228
- [specId, taskId, content, now]
229
- );
230
- }
231
- } catch { /* ignore */ }
232
- }
233
- }
234
-
235
- // 5. Se status for blocked ou needs_decision, registrar como knowledge critico
236
- if (data.status === "blocked" && data.blockers) {
237
- for (const blocker of data.blockers) {
238
- try {
239
- db.run(
240
- `INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
241
- VALUES (?, ?, 'blocker', ?, 'critical', 'all', ?)`,
242
- [specId, taskId, blocker, now]
243
- );
244
- result.knowledgeAdded++;
245
- } catch (e) {
246
- result.errors.push(`Erro ao registrar blocker: ${(e as Error).message}`);
247
- }
248
- }
249
- }
250
-
251
- // 6. v8.0: Registrar Reasoning no reasoning_log
252
- if (data.reasoning) {
253
- try {
254
- // Abordagem principal
255
- if (data.reasoning.approach) {
256
- addReasoning(specId, taskId, {
257
- category: "decision",
258
- thought: `Abordagem: ${data.reasoning.approach}`,
259
- relatedFiles: [...data.files_created, ...data.files_modified],
260
- importance: "normal",
261
- });
262
- result.reasoningAdded++;
263
- }
264
-
265
- // Desafios encontrados
266
- if (data.reasoning.challenges && data.reasoning.challenges.length > 0) {
267
- for (const challenge of data.reasoning.challenges) {
268
- addReasoning(specId, taskId, {
269
- category: "challenge",
270
- thought: challenge,
271
- relatedFiles: [...data.files_created, ...data.files_modified],
272
- importance: "high",
273
- });
274
- result.reasoningAdded++;
275
- }
276
- }
277
-
278
- // Alternativas consideradas
279
- if (data.reasoning.alternatives && data.reasoning.alternatives.length > 0) {
280
- for (const alt of data.reasoning.alternatives) {
281
- addReasoning(specId, taskId, {
282
- category: "decision",
283
- thought: `Alternativa considerada: ${alt}`,
284
- relatedFiles: [...data.files_created, ...data.files_modified],
285
- importance: "normal",
286
- });
287
- result.reasoningAdded++;
288
- }
289
- }
290
-
291
- // Recomendações para próximas tasks
292
- if (data.reasoning.recommendations) {
293
- addReasoning(specId, taskId, {
294
- category: "recommendation",
295
- thought: data.reasoning.recommendations,
296
- relatedFiles: [...data.files_created, ...data.files_modified],
297
- importance: "high",
298
- });
299
- result.reasoningAdded++;
300
- }
301
- } catch (e) {
302
- result.errors.push(`Erro ao registrar reasoning: ${(e as Error).message}`);
303
- }
304
- }
305
-
306
- // 7. v8.0: Criar relações no knowledge graph
307
- try {
308
- // Relação: task -> arquivos criados
309
- for (const file of data.files_created) {
310
- addGraphRelation(specId, {
311
- sourceType: "task",
312
- sourceId: String(taskId),
313
- targetType: "file",
314
- targetId: file,
315
- relation: "creates",
316
- strength: 1.0,
317
- });
318
- result.relationsAdded++;
319
- }
320
-
321
- // Relação: task -> arquivos modificados
322
- for (const file of data.files_modified) {
323
- addGraphRelation(specId, {
324
- sourceType: "task",
325
- sourceId: String(taskId),
326
- targetType: "file",
327
- targetId: file,
328
- relation: "modifies",
329
- strength: 1.0,
330
- });
331
- result.relationsAdded++;
332
- }
333
-
334
- // Relação: decision -> arquivos (se houver decisions)
335
- if (data.decisions_made) {
336
- for (const dec of data.decisions_made) {
337
- const decisionId = getNextDecisionId(specId);
338
- for (const file of [...data.files_created, ...data.files_modified]) {
339
- addGraphRelation(specId, {
340
- sourceType: "decision",
341
- sourceId: decisionId,
342
- targetType: "file",
343
- targetId: file,
344
- relation: "implements",
345
- strength: 0.8,
346
- });
347
- result.relationsAdded++;
348
- }
349
- }
350
- }
351
-
352
- // Relação: pattern -> arquivos (se patterns descobertos)
353
- if (data.patterns_discovered) {
354
- for (const pattern of data.patterns_discovered) {
355
- for (const file of data.files_created) {
356
- addGraphRelation(specId, {
357
- sourceType: "pattern",
358
- sourceId: pattern,
359
- targetType: "file",
360
- targetId: file,
361
- relation: "extracted_from",
362
- strength: 0.9,
363
- });
364
- result.relationsAdded++;
365
- }
366
- }
367
- }
368
- } catch (e) {
369
- result.errors.push(`Erro ao criar relações: ${(e as Error).message}`);
370
- }
371
-
372
- // 8. v8.5: Registrar utilities de arquivos criados/modificados (DRY map)
373
- try {
374
- // 8a. Usar utilities declaradas pelo subagent (metadata mais rica)
375
- if (data.utilities_created && data.utilities_created.length > 0) {
376
- for (const util of data.utilities_created) {
377
- upsertUtility({
378
- filePath: util.file,
379
- utilityName: util.name,
380
- utilityType: util.type || "function",
381
- scope: inferScopeFromPath(util.file),
382
- signature: util.signature,
383
- description: util.description,
384
- }, specId, taskNumber);
385
- result.utilitiesRegistered++;
386
- }
387
- }
388
-
389
- // 8b. Fallback: escanear exports de arquivos criados/modificados
390
- for (const file of allFiles.map(f => f.path)) {
391
- const utilities = extractUtilitiesFromFile(file);
392
- const scope = inferScopeFromPath(file);
393
- for (const util of utilities) {
394
- upsertUtility({
395
- filePath: file,
396
- utilityName: util.name,
397
- utilityType: util.type,
398
- scope,
399
- signature: util.signature,
400
- }, specId, taskNumber);
401
- result.utilitiesRegistered++;
402
- }
403
- }
404
- } catch (e) {
405
- // Nao-critico: nao falhar processamento por extracao de utilities
406
- result.errors.push(`Aviso utilities: ${(e as Error).message}`);
407
- }
408
-
409
- result.success = result.errors.length === 0;
410
- return result;
411
- }
412
-
413
- /**
414
- * Formata o resultado do processamento para exibicao
415
- */
416
- export function formatProcessResult(result: ProcessResult): string {
417
- let output = "\n[i] Retorno do subagent processado automaticamente:\n";
418
-
419
- if (result.knowledgeAdded > 0) {
420
- output += ` + ${result.knowledgeAdded} knowledge(s) registrado(s) para broadcast\n`;
421
- }
422
- if (result.decisionsAdded > 0) {
423
- output += ` + ${result.decisionsAdded} decisao(es) registrada(s)\n`;
424
- }
425
- if (result.artifactsAdded > 0) {
426
- output += ` + ${result.artifactsAdded} artefato(s) registrado(s)\n`;
427
- }
428
- if (result.patternsAdded > 0) {
429
- output += ` + ${result.patternsAdded} pattern(s) adicionado(s) ao contexto\n`;
430
- }
431
- // v8.0: Novas métricas
432
- if (result.reasoningAdded > 0) {
433
- output += ` + ${result.reasoningAdded} raciocinio(s) registrado(s) no historico\n`;
434
- }
435
- if (result.relationsAdded > 0) {
436
- output += ` + ${result.relationsAdded} relacao(oes) criada(s) no grafo de conhecimento\n`;
437
- }
438
- // v8.5: Utilities registradas
439
- if (result.utilitiesRegistered > 0) {
440
- output += ` + ${result.utilitiesRegistered} utilidade(s) registrada(s) no mapa DRY\n`;
441
- }
442
-
443
- if (result.errors.length > 0) {
444
- output += "\n[!] Erros durante processamento:\n";
445
- for (const error of result.errors) {
446
- output += ` - ${error}\n`;
447
- }
448
- }
449
-
450
- return output;
1
+ /**
2
+ * Process Subagent Return
3
+ *
4
+ * Este modulo processa automaticamente o retorno de um subagent,
5
+ * extraindo e registrando:
6
+ * - Knowledge broadcast
7
+ * - Decisions
8
+ * - Artifacts (files created/modified)
9
+ * - Patterns descobertos
10
+ */
11
+
12
+ import { getDb } from "../db/connection";
13
+ import { SubagentReturn, Knowledge } from "./subagent-protocol";
14
+ import { addReasoning, addGraphRelation, getRelatedFiles, upsertUtility } from "../db/schema";
15
+ import { extractUtilitiesFromFile, inferScopeFromPath } from "../commands/patterns";
16
+
17
+ interface ProcessResult {
18
+ success: boolean;
19
+ knowledgeAdded: number;
20
+ decisionsAdded: number;
21
+ artifactsAdded: number;
22
+ patternsAdded: number;
23
+ reasoningAdded: number; // v8.0
24
+ relationsAdded: number; // v8.0
25
+ utilitiesRegistered: number; // v8.5
26
+ errors: string[];
27
+ }
28
+
29
+ function getNextDecisionId(specId: string): string {
30
+ const db = getDb();
31
+ const last = db
32
+ .query("SELECT id FROM decisions WHERE spec_id = ? ORDER BY created_at DESC LIMIT 1")
33
+ .get(specId) as any;
34
+
35
+ if (!last) return "DEC-001";
36
+
37
+ const num = parseInt(last.id.replace("DEC-", "")) + 1;
38
+ return `DEC-${num.toString().padStart(3, "0")}`;
39
+ }
40
+
41
+ /**
42
+ * Processa o retorno de um subagent e registra automaticamente
43
+ * todos os dados extraidos no banco
44
+ */
45
+ export function processSubagentReturn(
46
+ specId: string,
47
+ taskId: number,
48
+ taskNumber: number,
49
+ data: SubagentReturn
50
+ ): ProcessResult {
51
+ const db = getDb();
52
+ const now = new Date().toISOString();
53
+ const result: ProcessResult = {
54
+ success: true,
55
+ knowledgeAdded: 0,
56
+ decisionsAdded: 0,
57
+ artifactsAdded: 0,
58
+ patternsAdded: 0,
59
+ reasoningAdded: 0,
60
+ relationsAdded: 0,
61
+ utilitiesRegistered: 0,
62
+ errors: [],
63
+ };
64
+
65
+ // 1. Registrar Knowledge Broadcast (v8.3: com deduplicacao)
66
+ if (data.knowledge_to_broadcast && data.knowledge_to_broadcast.length > 0) {
67
+ for (const k of data.knowledge_to_broadcast) {
68
+ try {
69
+ // v8.3: Deduplicacao - verificar se knowledge identico ja existe
70
+ const existing = db.query(
71
+ `SELECT id FROM knowledge WHERE spec_id = ? AND category = ? AND content = ? LIMIT 1`
72
+ ).get(specId, k.category, k.content) as any;
73
+
74
+ if (existing) continue; // Skip duplicata
75
+
76
+ db.run(
77
+ `INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
78
+ VALUES (?, ?, ?, ?, ?, 'all', ?)`,
79
+ [specId, taskId, k.category, k.content, k.severity, now]
80
+ );
81
+ result.knowledgeAdded++;
82
+ } catch (e) {
83
+ result.errors.push(`Erro ao registrar knowledge: ${(e as Error).message}`);
84
+ }
85
+ }
86
+ }
87
+
88
+ // 2. Registrar Decisions
89
+ if (data.decisions_made && data.decisions_made.length > 0) {
90
+ for (const dec of data.decisions_made) {
91
+ try {
92
+ const decisionId = getNextDecisionId(specId);
93
+ db.run(
94
+ `INSERT INTO decisions (id, spec_id, task_ref, title, decision, rationale, status, created_at)
95
+ VALUES (?, ?, ?, ?, ?, ?, 'active', ?)`,
96
+ [decisionId, specId, taskNumber, dec.title, dec.decision, dec.rationale || null, now]
97
+ );
98
+ result.decisionsAdded++;
99
+ } catch (e) {
100
+ result.errors.push(`Erro ao registrar decision: ${(e as Error).message}`);
101
+ }
102
+ }
103
+ }
104
+
105
+ // 2b. v8.3: Deteccao automatica de contradicoes entre decisoes
106
+ if (data.decisions_made && data.decisions_made.length > 0) {
107
+ try {
108
+ const existingDecisions = db.query(
109
+ "SELECT id, title, decision FROM decisions WHERE spec_id = ? AND status = 'active'"
110
+ ).all(specId) as any[];
111
+
112
+ const allTaskFiles = [...data.files_created, ...data.files_modified];
113
+
114
+ for (const newDec of data.decisions_made) {
115
+ for (const existingDec of existingDecisions) {
116
+ // Skip se mesma decisao (recem adicionada)
117
+ if (existingDec.title === newDec.title) continue;
118
+
119
+ const newDecLower = `${newDec.title} ${newDec.decision}`.toLowerCase();
120
+ const existDecLower = `${existingDec.title} ${existingDec.decision}`.toLowerCase();
121
+
122
+ // Verificar overlap de arquivos
123
+ const existingDecFiles = getRelatedFiles(existingDec.id, "decision");
124
+ const fileOverlap = allTaskFiles.some(f => existingDecFiles.includes(f));
125
+
126
+ if (fileOverlap) {
127
+ // Heuristica de keywords opostos
128
+ const opposites = [
129
+ ['usar', 'nao usar'], ['usar', 'evitar'],
130
+ ['use', 'avoid'], ['add', 'remove'],
131
+ ['incluir', 'excluir'], ['habilitar', 'desabilitar'],
132
+ ['enable', 'disable'], ['create', 'delete'],
133
+ ['client', 'server'], ['sync', 'async'],
134
+ ];
135
+
136
+ let isContradiction = false;
137
+ for (const [word1, word2] of opposites) {
138
+ if ((newDecLower.includes(word1) && existDecLower.includes(word2)) ||
139
+ (newDecLower.includes(word2) && existDecLower.includes(word1))) {
140
+ isContradiction = true;
141
+ break;
142
+ }
143
+ }
144
+
145
+ if (isContradiction) {
146
+ const newDecId = db.query(
147
+ "SELECT id FROM decisions WHERE spec_id = ? AND title = ? ORDER BY created_at DESC LIMIT 1"
148
+ ).get(specId, newDec.title) as any;
149
+
150
+ if (newDecId) {
151
+ addGraphRelation(specId, {
152
+ sourceType: "decision",
153
+ sourceId: newDecId.id,
154
+ targetType: "decision",
155
+ targetId: existingDec.id,
156
+ relation: "contradicts",
157
+ strength: 0.7,
158
+ metadata: { reason: "Arquivos sobrepostos com semantica oposta" },
159
+ });
160
+ result.relationsAdded++;
161
+ }
162
+ }
163
+ }
164
+ }
165
+ }
166
+ } catch (e) {
167
+ // Nao-critico: nao falhar processamento por deteccao de contradicoes
168
+ result.errors.push(`Aviso contradicoes: ${(e as Error).message}`);
169
+ }
170
+ }
171
+
172
+ // 3. Registrar Artifacts (files_created e files_modified)
173
+ const allFiles = [
174
+ ...data.files_created.map((f) => ({ path: f, action: "created" })),
175
+ ...data.files_modified.map((f) => ({ path: f, action: "modified" })),
176
+ ];
177
+
178
+ for (const file of allFiles) {
179
+ try {
180
+ db.run(
181
+ `INSERT OR REPLACE INTO artifacts (spec_id, task_ref, path, action, created_at)
182
+ VALUES (?, ?, ?, ?, ?)`,
183
+ [specId, taskNumber, file.path, file.action, now]
184
+ );
185
+ result.artifactsAdded++;
186
+ } catch (e) {
187
+ result.errors.push(`Erro ao registrar artifact: ${(e as Error).message}`);
188
+ }
189
+ }
190
+
191
+ // 4. Registrar Patterns descobertos no contexto
192
+ if (data.patterns_discovered && data.patterns_discovered.length > 0) {
193
+ try {
194
+ const context = db.query("SELECT * FROM context WHERE spec_id = ?").get(specId) as any;
195
+ const currentPatterns = context?.patterns ? JSON.parse(context.patterns) : [];
196
+
197
+ for (const pattern of data.patterns_discovered) {
198
+ currentPatterns.push({
199
+ pattern,
200
+ detected_at: now,
201
+ source_task: taskNumber,
202
+ });
203
+ result.patternsAdded++;
204
+ }
205
+
206
+ db.run(
207
+ "UPDATE context SET patterns = ?, updated_at = ? WHERE spec_id = ?",
208
+ [JSON.stringify(currentPatterns), now, specId]
209
+ );
210
+ } catch (e) {
211
+ result.errors.push(`Erro ao registrar patterns: ${(e as Error).message}`);
212
+ }
213
+ }
214
+
215
+ // 4b. v8.3: Broadcast patterns como knowledge (com deduplicacao)
216
+ if (data.patterns_discovered && data.patterns_discovered.length > 0) {
217
+ for (const pattern of data.patterns_discovered) {
218
+ try {
219
+ const content = `Pattern descoberto: ${pattern}`;
220
+ const existing = db.query(
221
+ `SELECT id FROM knowledge WHERE spec_id = ? AND category = 'pattern' AND content = ? LIMIT 1`
222
+ ).get(specId, content) as any;
223
+
224
+ if (!existing) {
225
+ db.run(
226
+ `INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
227
+ VALUES (?, ?, 'pattern', ?, 'info', 'all', ?)`,
228
+ [specId, taskId, content, now]
229
+ );
230
+ }
231
+ } catch { /* ignore */ }
232
+ }
233
+ }
234
+
235
+ // 5. Se status for blocked ou needs_decision, registrar como knowledge critico
236
+ if (data.status === "blocked" && data.blockers) {
237
+ for (const blocker of data.blockers) {
238
+ try {
239
+ db.run(
240
+ `INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
241
+ VALUES (?, ?, 'blocker', ?, 'critical', 'all', ?)`,
242
+ [specId, taskId, blocker, now]
243
+ );
244
+ result.knowledgeAdded++;
245
+ } catch (e) {
246
+ result.errors.push(`Erro ao registrar blocker: ${(e as Error).message}`);
247
+ }
248
+ }
249
+ }
250
+
251
+ // 6. v8.0: Registrar Reasoning no reasoning_log
252
+ if (data.reasoning) {
253
+ try {
254
+ // Abordagem principal
255
+ if (data.reasoning.approach) {
256
+ addReasoning(specId, taskId, {
257
+ category: "decision",
258
+ thought: `Abordagem: ${data.reasoning.approach}`,
259
+ relatedFiles: [...data.files_created, ...data.files_modified],
260
+ importance: "normal",
261
+ });
262
+ result.reasoningAdded++;
263
+ }
264
+
265
+ // Desafios encontrados
266
+ if (data.reasoning.challenges && data.reasoning.challenges.length > 0) {
267
+ for (const challenge of data.reasoning.challenges) {
268
+ addReasoning(specId, taskId, {
269
+ category: "challenge",
270
+ thought: challenge,
271
+ relatedFiles: [...data.files_created, ...data.files_modified],
272
+ importance: "high",
273
+ });
274
+ result.reasoningAdded++;
275
+ }
276
+ }
277
+
278
+ // Alternativas consideradas
279
+ if (data.reasoning.alternatives && data.reasoning.alternatives.length > 0) {
280
+ for (const alt of data.reasoning.alternatives) {
281
+ addReasoning(specId, taskId, {
282
+ category: "decision",
283
+ thought: `Alternativa considerada: ${alt}`,
284
+ relatedFiles: [...data.files_created, ...data.files_modified],
285
+ importance: "normal",
286
+ });
287
+ result.reasoningAdded++;
288
+ }
289
+ }
290
+
291
+ // Recomendações para próximas tasks
292
+ if (data.reasoning.recommendations) {
293
+ addReasoning(specId, taskId, {
294
+ category: "recommendation",
295
+ thought: data.reasoning.recommendations,
296
+ relatedFiles: [...data.files_created, ...data.files_modified],
297
+ importance: "high",
298
+ });
299
+ result.reasoningAdded++;
300
+ }
301
+ } catch (e) {
302
+ result.errors.push(`Erro ao registrar reasoning: ${(e as Error).message}`);
303
+ }
304
+ }
305
+
306
+ // 7. v8.0: Criar relações no knowledge graph
307
+ try {
308
+ // Relação: task -> arquivos criados
309
+ for (const file of data.files_created) {
310
+ addGraphRelation(specId, {
311
+ sourceType: "task",
312
+ sourceId: String(taskId),
313
+ targetType: "file",
314
+ targetId: file,
315
+ relation: "creates",
316
+ strength: 1.0,
317
+ });
318
+ result.relationsAdded++;
319
+ }
320
+
321
+ // Relação: task -> arquivos modificados
322
+ for (const file of data.files_modified) {
323
+ addGraphRelation(specId, {
324
+ sourceType: "task",
325
+ sourceId: String(taskId),
326
+ targetType: "file",
327
+ targetId: file,
328
+ relation: "modifies",
329
+ strength: 1.0,
330
+ });
331
+ result.relationsAdded++;
332
+ }
333
+
334
+ // Relação: decision -> arquivos (se houver decisions)
335
+ if (data.decisions_made) {
336
+ for (const dec of data.decisions_made) {
337
+ const decisionId = getNextDecisionId(specId);
338
+ for (const file of [...data.files_created, ...data.files_modified]) {
339
+ addGraphRelation(specId, {
340
+ sourceType: "decision",
341
+ sourceId: decisionId,
342
+ targetType: "file",
343
+ targetId: file,
344
+ relation: "implements",
345
+ strength: 0.8,
346
+ });
347
+ result.relationsAdded++;
348
+ }
349
+ }
350
+ }
351
+
352
+ // Relação: pattern -> arquivos (se patterns descobertos)
353
+ if (data.patterns_discovered) {
354
+ for (const pattern of data.patterns_discovered) {
355
+ for (const file of data.files_created) {
356
+ addGraphRelation(specId, {
357
+ sourceType: "pattern",
358
+ sourceId: pattern,
359
+ targetType: "file",
360
+ targetId: file,
361
+ relation: "extracted_from",
362
+ strength: 0.9,
363
+ });
364
+ result.relationsAdded++;
365
+ }
366
+ }
367
+ }
368
+ } catch (e) {
369
+ result.errors.push(`Erro ao criar relações: ${(e as Error).message}`);
370
+ }
371
+
372
+ // 8. v8.5: Registrar utilities de arquivos criados/modificados (DRY map)
373
+ try {
374
+ // 8a. Usar utilities declaradas pelo subagent (metadata mais rica)
375
+ if (data.utilities_created && data.utilities_created.length > 0) {
376
+ for (const util of data.utilities_created) {
377
+ upsertUtility({
378
+ filePath: util.file,
379
+ utilityName: util.name,
380
+ utilityType: util.type || "function",
381
+ scope: inferScopeFromPath(util.file),
382
+ signature: util.signature,
383
+ description: util.description,
384
+ }, specId, taskNumber);
385
+ result.utilitiesRegistered++;
386
+ }
387
+ }
388
+
389
+ // 8b. Fallback: escanear exports de arquivos criados/modificados
390
+ for (const file of allFiles.map(f => f.path)) {
391
+ const utilities = extractUtilitiesFromFile(file);
392
+ const scope = inferScopeFromPath(file);
393
+ for (const util of utilities) {
394
+ upsertUtility({
395
+ filePath: file,
396
+ utilityName: util.name,
397
+ utilityType: util.type,
398
+ scope,
399
+ signature: util.signature,
400
+ }, specId, taskNumber);
401
+ result.utilitiesRegistered++;
402
+ }
403
+ }
404
+ } catch (e) {
405
+ // Nao-critico: nao falhar processamento por extracao de utilities
406
+ result.errors.push(`Aviso utilities: ${(e as Error).message}`);
407
+ }
408
+
409
+ result.success = result.errors.length === 0;
410
+ return result;
411
+ }
412
+
413
+ /**
414
+ * Formata o resultado do processamento para exibicao
415
+ */
416
+ export function formatProcessResult(result: ProcessResult): string {
417
+ let output = "\n[i] Retorno do subagent processado automaticamente:\n";
418
+
419
+ if (result.knowledgeAdded > 0) {
420
+ output += ` + ${result.knowledgeAdded} knowledge(s) registrado(s) para broadcast\n`;
421
+ }
422
+ if (result.decisionsAdded > 0) {
423
+ output += ` + ${result.decisionsAdded} decisao(es) registrada(s)\n`;
424
+ }
425
+ if (result.artifactsAdded > 0) {
426
+ output += ` + ${result.artifactsAdded} artefato(s) registrado(s)\n`;
427
+ }
428
+ if (result.patternsAdded > 0) {
429
+ output += ` + ${result.patternsAdded} pattern(s) adicionado(s) ao contexto\n`;
430
+ }
431
+ // v8.0: Novas métricas
432
+ if (result.reasoningAdded > 0) {
433
+ output += ` + ${result.reasoningAdded} raciocinio(s) registrado(s) no historico\n`;
434
+ }
435
+ if (result.relationsAdded > 0) {
436
+ output += ` + ${result.relationsAdded} relacao(oes) criada(s) no grafo de conhecimento\n`;
437
+ }
438
+ // v8.5: Utilities registradas
439
+ if (result.utilitiesRegistered > 0) {
440
+ output += ` + ${result.utilitiesRegistered} utilidade(s) registrada(s) no mapa DRY\n`;
441
+ }
442
+
443
+ if (result.errors.length > 0) {
444
+ output += "\n[!] Erros durante processamento:\n";
445
+ for (const error of result.errors) {
446
+ output += ` - ${error}\n`;
447
+ }
448
+ }
449
+
450
+ return output;
451
451
  }