@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.
- package/commands/architect.ts +760 -760
- package/commands/check.ts +131 -131
- package/commands/clear.ts +170 -170
- package/commands/decide.ts +249 -249
- package/commands/discover.ts +1071 -1071
- package/commands/knowledge.ts +361 -361
- package/commands/patterns.ts +621 -621
- package/commands/plan.ts +376 -376
- package/commands/product.ts +626 -626
- package/commands/research.ts +754 -754
- package/commands/review.ts +463 -463
- package/commands/standards.ts +200 -200
- package/commands/task.ts +623 -623
- package/commands/utils.ts +1021 -1021
- package/db/connection.ts +32 -32
- package/db/schema.ts +719 -719
- 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 -128
- package/detectors/node.ts +493 -493
- package/detectors/python.ts +423 -423
- package/detectors/rust.ts +348 -348
- package/gates/standards-validator.ts +204 -204
- package/gates/validator.ts +441 -441
- package/package.json +44 -43
- package/protocol/process-return.ts +450 -450
- package/protocol/subagent-protocol.ts +401 -401
- package/workflow.ts +783 -782
package/commands/knowledge.ts
CHANGED
|
@@ -1,362 +1,362 @@
|
|
|
1
|
-
import { getDb } from "../db/connection";
|
|
2
|
-
import { initSchema, getRelatedDecisions, getRelatedFiles, findContradictions } from "../db/schema";
|
|
3
|
-
|
|
4
|
-
type KnowledgeCategory = "discovery" | "decision" | "blocker" | "pattern" | "constraint";
|
|
5
|
-
type KnowledgeSeverity = "info" | "warning" | "critical";
|
|
6
|
-
|
|
7
|
-
interface AddKnowledgeOptions {
|
|
8
|
-
content: string;
|
|
9
|
-
category: KnowledgeCategory;
|
|
10
|
-
severity?: KnowledgeSeverity;
|
|
11
|
-
broadcastTo?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function getActiveSpec(): any {
|
|
15
|
-
const db = getDb();
|
|
16
|
-
return db
|
|
17
|
-
.query("SELECT * FROM specs WHERE phase NOT IN ('completed', 'cancelled') ORDER BY created_at DESC LIMIT 1")
|
|
18
|
-
.get();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function getCurrentTask(specId: string): any {
|
|
22
|
-
const db = getDb();
|
|
23
|
-
return db
|
|
24
|
-
.query("SELECT * FROM tasks WHERE spec_id = ? AND status = 'running' LIMIT 1")
|
|
25
|
-
.get(specId);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function addKnowledge(options: AddKnowledgeOptions): void {
|
|
29
|
-
initSchema();
|
|
30
|
-
|
|
31
|
-
const db = getDb();
|
|
32
|
-
const now = new Date().toISOString();
|
|
33
|
-
|
|
34
|
-
const spec = getActiveSpec();
|
|
35
|
-
if (!spec) {
|
|
36
|
-
console.error("\nNenhuma feature ativa.\n");
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const currentTask = getCurrentTask(spec.id);
|
|
41
|
-
if (!currentTask) {
|
|
42
|
-
console.error("\nNenhuma task em execucao.");
|
|
43
|
-
console.error("Knowledge so pode ser adicionado durante execucao de uma task.\n");
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const validCategories: KnowledgeCategory[] = ["discovery", "decision", "blocker", "pattern", "constraint"];
|
|
48
|
-
if (!validCategories.includes(options.category)) {
|
|
49
|
-
console.error(`\nCategoria invalida: ${options.category}`);
|
|
50
|
-
console.error(`Validas: ${validCategories.join(", ")}\n`);
|
|
51
|
-
process.exit(1);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const severity = options.severity || "info";
|
|
55
|
-
const broadcastTo = options.broadcastTo || "all";
|
|
56
|
-
|
|
57
|
-
db.run(
|
|
58
|
-
`INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
|
|
59
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
60
|
-
[spec.id, currentTask.id, options.category, options.content, severity, broadcastTo, now]
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
const severityIcon = {
|
|
64
|
-
info: "i",
|
|
65
|
-
warning: "!",
|
|
66
|
-
critical: "X",
|
|
67
|
-
}[severity];
|
|
68
|
-
|
|
69
|
-
console.log(`\n[${severityIcon}] Knowledge registrado`);
|
|
70
|
-
console.log(`Categoria: ${options.category}`);
|
|
71
|
-
console.log(`Severidade: ${severity}`);
|
|
72
|
-
console.log(`Origem: Task #${currentTask.number}`);
|
|
73
|
-
console.log(`Broadcast: ${broadcastTo}`);
|
|
74
|
-
console.log(`Conteudo: ${options.content}\n`);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export function listKnowledge(options: {
|
|
78
|
-
unread?: boolean;
|
|
79
|
-
category?: string;
|
|
80
|
-
severity?: string; // v8.0: Filtro por severidade
|
|
81
|
-
json?: boolean;
|
|
82
|
-
}): void {
|
|
83
|
-
initSchema();
|
|
84
|
-
|
|
85
|
-
const db = getDb();
|
|
86
|
-
|
|
87
|
-
const spec = getActiveSpec();
|
|
88
|
-
if (!spec) {
|
|
89
|
-
if (options.json) {
|
|
90
|
-
console.log(JSON.stringify({ knowledge: [], message: "Nenhuma feature ativa" }));
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
console.error("\nNenhuma feature ativa.\n");
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
let query = "SELECT * FROM knowledge WHERE spec_id = ?";
|
|
98
|
-
const params: any[] = [spec.id];
|
|
99
|
-
|
|
100
|
-
if (options.category) {
|
|
101
|
-
query += " AND category = ?";
|
|
102
|
-
params.push(options.category);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// v8.0: Filtro por severidade (critical mostra só critical, warning mostra warning+critical)
|
|
106
|
-
if (options.severity) {
|
|
107
|
-
if (options.severity === "critical") {
|
|
108
|
-
query += " AND severity = 'critical'";
|
|
109
|
-
} else if (options.severity === "warning") {
|
|
110
|
-
query += " AND severity IN ('warning', 'critical')";
|
|
111
|
-
}
|
|
112
|
-
// info mostra tudo (comportamento padrão)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
query += " ORDER BY severity DESC, created_at DESC";
|
|
116
|
-
|
|
117
|
-
const knowledge = db.query(query).all(...params) as any[];
|
|
118
|
-
|
|
119
|
-
// Filtrar unread se solicitado
|
|
120
|
-
let filtered = knowledge;
|
|
121
|
-
if (options.unread) {
|
|
122
|
-
const currentTask = getCurrentTask(spec.id);
|
|
123
|
-
if (currentTask) {
|
|
124
|
-
filtered = knowledge.filter((k) => {
|
|
125
|
-
if (!k.acknowledged_by) return true;
|
|
126
|
-
const acked = JSON.parse(k.acknowledged_by) as number[];
|
|
127
|
-
return !acked.includes(currentTask.id);
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (options.json) {
|
|
133
|
-
console.log(JSON.stringify({ knowledge: filtered }));
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (filtered.length === 0) {
|
|
138
|
-
console.log("\nNenhum knowledge encontrado.\n");
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
console.log(`\nKnowledge Broadcast (${filtered.length}):`);
|
|
143
|
-
console.log(`${"─".repeat(60)}`);
|
|
144
|
-
|
|
145
|
-
const severityIcon = {
|
|
146
|
-
info: "i",
|
|
147
|
-
warning: "!",
|
|
148
|
-
critical: "X",
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
for (const k of filtered) {
|
|
152
|
-
const icon = severityIcon[k.severity as KnowledgeSeverity] || "?";
|
|
153
|
-
const taskOrigin = k.task_origin ? `Task #${k.task_origin}` : "Sistema";
|
|
154
|
-
console.log(`[${icon}] #${k.id} (${k.category}) - ${taskOrigin}`);
|
|
155
|
-
console.log(` ${k.content}`);
|
|
156
|
-
console.log(` Broadcast: ${k.broadcast_to} | ${k.created_at}`);
|
|
157
|
-
console.log();
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function acknowledgeKnowledge(knowledgeId: string): void {
|
|
162
|
-
initSchema();
|
|
163
|
-
|
|
164
|
-
const db = getDb();
|
|
165
|
-
|
|
166
|
-
const spec = getActiveSpec();
|
|
167
|
-
if (!spec) {
|
|
168
|
-
console.error("\nNenhuma feature ativa.\n");
|
|
169
|
-
process.exit(1);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const currentTask = getCurrentTask(spec.id);
|
|
173
|
-
if (!currentTask) {
|
|
174
|
-
console.error("\nNenhuma task em execucao.\n");
|
|
175
|
-
process.exit(1);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const kid = parseInt(knowledgeId);
|
|
179
|
-
const knowledge = db.query("SELECT * FROM knowledge WHERE id = ?").get(kid) as any;
|
|
180
|
-
|
|
181
|
-
if (!knowledge) {
|
|
182
|
-
console.error(`\nKnowledge #${kid} nao encontrado.\n`);
|
|
183
|
-
process.exit(1);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
const acknowledged = knowledge.acknowledged_by
|
|
187
|
-
? (JSON.parse(knowledge.acknowledged_by) as number[])
|
|
188
|
-
: [];
|
|
189
|
-
|
|
190
|
-
if (!acknowledged.includes(currentTask.id)) {
|
|
191
|
-
acknowledged.push(currentTask.id);
|
|
192
|
-
db.run("UPDATE knowledge SET acknowledged_by = ? WHERE id = ?", [
|
|
193
|
-
JSON.stringify(acknowledged),
|
|
194
|
-
kid,
|
|
195
|
-
]);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
console.log(`\nKnowledge #${kid} marcado como lido pela Task #${currentTask.number}.\n`);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
export function getKnowledgeForTask(specId: string, taskId: number): any[] {
|
|
202
|
-
const db = getDb();
|
|
203
|
-
|
|
204
|
-
// Buscar todo knowledge da spec e filtrar em codigo
|
|
205
|
-
// Fix: LIKE '%taskId%' causava substring match (taskId=1 matchava "1,10,11")
|
|
206
|
-
const knowledge = db
|
|
207
|
-
.query(
|
|
208
|
-
`SELECT * FROM knowledge
|
|
209
|
-
WHERE spec_id = ?
|
|
210
|
-
ORDER BY severity DESC, created_at DESC
|
|
211
|
-
LIMIT 50`
|
|
212
|
-
)
|
|
213
|
-
.all(specId) as any[];
|
|
214
|
-
|
|
215
|
-
return knowledge.filter((k) => {
|
|
216
|
-
if (k.broadcast_to === 'all') return true;
|
|
217
|
-
const targets = k.broadcast_to.split(',').map((t: string) => parseInt(t.trim(), 10));
|
|
218
|
-
return targets.includes(taskId);
|
|
219
|
-
}).slice(0, 20);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export function getUnreadKnowledgeForTask(specId: string, taskId: number): any[] {
|
|
223
|
-
const all = getKnowledgeForTask(specId, taskId);
|
|
224
|
-
|
|
225
|
-
return all.filter((k) => {
|
|
226
|
-
if (!k.acknowledged_by) return true;
|
|
227
|
-
const acked = JSON.parse(k.acknowledged_by) as number[];
|
|
228
|
-
return !acked.includes(taskId);
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// v8.1: Consultar knowledge_graph - encontrar relacoes para arquivo, decisao ou pattern
|
|
233
|
-
export function queryGraph(options: {
|
|
234
|
-
file?: string;
|
|
235
|
-
decision?: string;
|
|
236
|
-
contradictions?: boolean;
|
|
237
|
-
json?: boolean;
|
|
238
|
-
}): void {
|
|
239
|
-
initSchema();
|
|
240
|
-
const db = getDb();
|
|
241
|
-
|
|
242
|
-
const spec = getActiveSpec();
|
|
243
|
-
if (!spec) {
|
|
244
|
-
console.error("\nNenhuma feature ativa.\n");
|
|
245
|
-
process.exit(1);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Buscar contradicoes
|
|
249
|
-
if (options.contradictions) {
|
|
250
|
-
const contradictions = findContradictions(spec.id);
|
|
251
|
-
|
|
252
|
-
if (options.json) {
|
|
253
|
-
console.log(JSON.stringify({ contradictions }));
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (contradictions.length === 0) {
|
|
258
|
-
console.log("\nNenhuma contradicao detectada.\n");
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
console.log(`\nContradicoes detectadas (${contradictions.length}):`);
|
|
263
|
-
console.log(`${"─".repeat(50)}`);
|
|
264
|
-
for (const c of contradictions) {
|
|
265
|
-
console.log(` [!] "${c.decision1}" <-> "${c.decision2}"`);
|
|
266
|
-
console.log(` Detectada em: ${c.createdAt}\n`);
|
|
267
|
-
}
|
|
268
|
-
return;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Buscar relacoes para um arquivo
|
|
272
|
-
if (options.file) {
|
|
273
|
-
const decisions = getRelatedDecisions(options.file, "file");
|
|
274
|
-
const relatedFiles = db.query(`
|
|
275
|
-
SELECT DISTINCT kg.target_id as file, kg.relation
|
|
276
|
-
FROM knowledge_graph kg
|
|
277
|
-
WHERE kg.source_id = ? AND kg.target_type = 'file'
|
|
278
|
-
UNION
|
|
279
|
-
SELECT DISTINCT kg.source_id as file, kg.relation
|
|
280
|
-
FROM knowledge_graph kg
|
|
281
|
-
WHERE kg.target_id = ? AND kg.source_type = 'file'
|
|
282
|
-
`).all(options.file, options.file) as any[];
|
|
283
|
-
|
|
284
|
-
if (options.json) {
|
|
285
|
-
console.log(JSON.stringify({ file: options.file, decisions, relatedFiles }));
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
console.log(`\nRelacoes para: ${options.file}`);
|
|
290
|
-
console.log(`${"─".repeat(50)}`);
|
|
291
|
-
|
|
292
|
-
if (decisions.length > 0) {
|
|
293
|
-
console.log(`\nDecisoes que afetam este arquivo:`);
|
|
294
|
-
for (const d of decisions) {
|
|
295
|
-
console.log(` - ${d.title}: ${d.decision}`);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (relatedFiles.length > 0) {
|
|
300
|
-
console.log(`\nArquivos relacionados:`);
|
|
301
|
-
for (const f of relatedFiles) {
|
|
302
|
-
console.log(` - ${f.file} (${f.relation})`);
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if (decisions.length === 0 && relatedFiles.length === 0) {
|
|
307
|
-
console.log(" Nenhuma relacao encontrada.");
|
|
308
|
-
}
|
|
309
|
-
console.log();
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
// Buscar arquivos afetados por uma decisao
|
|
314
|
-
if (options.decision) {
|
|
315
|
-
const files = getRelatedFiles(options.decision, "decision");
|
|
316
|
-
|
|
317
|
-
if (options.json) {
|
|
318
|
-
console.log(JSON.stringify({ decision: options.decision, files }));
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
console.log(`\nArquivos afetados pela decisao "${options.decision}":`);
|
|
323
|
-
console.log(`${"─".repeat(50)}`);
|
|
324
|
-
if (files.length > 0) {
|
|
325
|
-
for (const f of files) {
|
|
326
|
-
console.log(` - ${f}`);
|
|
327
|
-
}
|
|
328
|
-
} else {
|
|
329
|
-
console.log(" Nenhum arquivo relacionado encontrado.");
|
|
330
|
-
}
|
|
331
|
-
console.log();
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Sem opcao: mostrar resumo do grafo
|
|
336
|
-
const totalRelations = db.query(
|
|
337
|
-
"SELECT COUNT(*) as c FROM knowledge_graph WHERE spec_id = ?"
|
|
338
|
-
).get(spec.id) as any;
|
|
339
|
-
|
|
340
|
-
const relationTypes = db.query(
|
|
341
|
-
"SELECT relation, COUNT(*) as c FROM knowledge_graph WHERE spec_id = ? GROUP BY relation ORDER BY c DESC"
|
|
342
|
-
).all(spec.id) as any[];
|
|
343
|
-
|
|
344
|
-
if (options.json) {
|
|
345
|
-
console.log(JSON.stringify({ totalRelations: totalRelations.c, relationTypes }));
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
console.log(`\nKnowledge Graph: ${totalRelations.c} relacoes`);
|
|
350
|
-
console.log(`${"─".repeat(50)}`);
|
|
351
|
-
if (relationTypes.length > 0) {
|
|
352
|
-
for (const rt of relationTypes) {
|
|
353
|
-
console.log(` ${rt.relation}: ${rt.c}`);
|
|
354
|
-
}
|
|
355
|
-
} else {
|
|
356
|
-
console.log(" Grafo vazio. Relacoes serao criadas automaticamente durante a execucao.");
|
|
357
|
-
}
|
|
358
|
-
console.log(`\nComandos:`);
|
|
359
|
-
console.log(` knowledge graph --file <path> Relacoes de um arquivo`);
|
|
360
|
-
console.log(` knowledge graph --decision <id> Arquivos afetados por decisao`);
|
|
361
|
-
console.log(` knowledge graph --contradictions Detectar contradicoes\n`);
|
|
1
|
+
import { getDb } from "../db/connection";
|
|
2
|
+
import { initSchema, getRelatedDecisions, getRelatedFiles, findContradictions } from "../db/schema";
|
|
3
|
+
|
|
4
|
+
type KnowledgeCategory = "discovery" | "decision" | "blocker" | "pattern" | "constraint";
|
|
5
|
+
type KnowledgeSeverity = "info" | "warning" | "critical";
|
|
6
|
+
|
|
7
|
+
interface AddKnowledgeOptions {
|
|
8
|
+
content: string;
|
|
9
|
+
category: KnowledgeCategory;
|
|
10
|
+
severity?: KnowledgeSeverity;
|
|
11
|
+
broadcastTo?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getActiveSpec(): any {
|
|
15
|
+
const db = getDb();
|
|
16
|
+
return db
|
|
17
|
+
.query("SELECT * FROM specs WHERE phase NOT IN ('completed', 'cancelled') ORDER BY created_at DESC LIMIT 1")
|
|
18
|
+
.get();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getCurrentTask(specId: string): any {
|
|
22
|
+
const db = getDb();
|
|
23
|
+
return db
|
|
24
|
+
.query("SELECT * FROM tasks WHERE spec_id = ? AND status = 'running' LIMIT 1")
|
|
25
|
+
.get(specId);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function addKnowledge(options: AddKnowledgeOptions): void {
|
|
29
|
+
initSchema();
|
|
30
|
+
|
|
31
|
+
const db = getDb();
|
|
32
|
+
const now = new Date().toISOString();
|
|
33
|
+
|
|
34
|
+
const spec = getActiveSpec();
|
|
35
|
+
if (!spec) {
|
|
36
|
+
console.error("\nNenhuma feature ativa.\n");
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const currentTask = getCurrentTask(spec.id);
|
|
41
|
+
if (!currentTask) {
|
|
42
|
+
console.error("\nNenhuma task em execucao.");
|
|
43
|
+
console.error("Knowledge so pode ser adicionado durante execucao de uma task.\n");
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const validCategories: KnowledgeCategory[] = ["discovery", "decision", "blocker", "pattern", "constraint"];
|
|
48
|
+
if (!validCategories.includes(options.category)) {
|
|
49
|
+
console.error(`\nCategoria invalida: ${options.category}`);
|
|
50
|
+
console.error(`Validas: ${validCategories.join(", ")}\n`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const severity = options.severity || "info";
|
|
55
|
+
const broadcastTo = options.broadcastTo || "all";
|
|
56
|
+
|
|
57
|
+
db.run(
|
|
58
|
+
`INSERT INTO knowledge (spec_id, task_origin, category, content, severity, broadcast_to, created_at)
|
|
59
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
60
|
+
[spec.id, currentTask.id, options.category, options.content, severity, broadcastTo, now]
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const severityIcon = {
|
|
64
|
+
info: "i",
|
|
65
|
+
warning: "!",
|
|
66
|
+
critical: "X",
|
|
67
|
+
}[severity];
|
|
68
|
+
|
|
69
|
+
console.log(`\n[${severityIcon}] Knowledge registrado`);
|
|
70
|
+
console.log(`Categoria: ${options.category}`);
|
|
71
|
+
console.log(`Severidade: ${severity}`);
|
|
72
|
+
console.log(`Origem: Task #${currentTask.number}`);
|
|
73
|
+
console.log(`Broadcast: ${broadcastTo}`);
|
|
74
|
+
console.log(`Conteudo: ${options.content}\n`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function listKnowledge(options: {
|
|
78
|
+
unread?: boolean;
|
|
79
|
+
category?: string;
|
|
80
|
+
severity?: string; // v8.0: Filtro por severidade
|
|
81
|
+
json?: boolean;
|
|
82
|
+
}): void {
|
|
83
|
+
initSchema();
|
|
84
|
+
|
|
85
|
+
const db = getDb();
|
|
86
|
+
|
|
87
|
+
const spec = getActiveSpec();
|
|
88
|
+
if (!spec) {
|
|
89
|
+
if (options.json) {
|
|
90
|
+
console.log(JSON.stringify({ knowledge: [], message: "Nenhuma feature ativa" }));
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
console.error("\nNenhuma feature ativa.\n");
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let query = "SELECT * FROM knowledge WHERE spec_id = ?";
|
|
98
|
+
const params: any[] = [spec.id];
|
|
99
|
+
|
|
100
|
+
if (options.category) {
|
|
101
|
+
query += " AND category = ?";
|
|
102
|
+
params.push(options.category);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// v8.0: Filtro por severidade (critical mostra só critical, warning mostra warning+critical)
|
|
106
|
+
if (options.severity) {
|
|
107
|
+
if (options.severity === "critical") {
|
|
108
|
+
query += " AND severity = 'critical'";
|
|
109
|
+
} else if (options.severity === "warning") {
|
|
110
|
+
query += " AND severity IN ('warning', 'critical')";
|
|
111
|
+
}
|
|
112
|
+
// info mostra tudo (comportamento padrão)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
query += " ORDER BY severity DESC, created_at DESC";
|
|
116
|
+
|
|
117
|
+
const knowledge = db.query(query).all(...params) as any[];
|
|
118
|
+
|
|
119
|
+
// Filtrar unread se solicitado
|
|
120
|
+
let filtered = knowledge;
|
|
121
|
+
if (options.unread) {
|
|
122
|
+
const currentTask = getCurrentTask(spec.id);
|
|
123
|
+
if (currentTask) {
|
|
124
|
+
filtered = knowledge.filter((k) => {
|
|
125
|
+
if (!k.acknowledged_by) return true;
|
|
126
|
+
const acked = JSON.parse(k.acknowledged_by) as number[];
|
|
127
|
+
return !acked.includes(currentTask.id);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (options.json) {
|
|
133
|
+
console.log(JSON.stringify({ knowledge: filtered }));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (filtered.length === 0) {
|
|
138
|
+
console.log("\nNenhum knowledge encontrado.\n");
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.log(`\nKnowledge Broadcast (${filtered.length}):`);
|
|
143
|
+
console.log(`${"─".repeat(60)}`);
|
|
144
|
+
|
|
145
|
+
const severityIcon = {
|
|
146
|
+
info: "i",
|
|
147
|
+
warning: "!",
|
|
148
|
+
critical: "X",
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
for (const k of filtered) {
|
|
152
|
+
const icon = severityIcon[k.severity as KnowledgeSeverity] || "?";
|
|
153
|
+
const taskOrigin = k.task_origin ? `Task #${k.task_origin}` : "Sistema";
|
|
154
|
+
console.log(`[${icon}] #${k.id} (${k.category}) - ${taskOrigin}`);
|
|
155
|
+
console.log(` ${k.content}`);
|
|
156
|
+
console.log(` Broadcast: ${k.broadcast_to} | ${k.created_at}`);
|
|
157
|
+
console.log();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function acknowledgeKnowledge(knowledgeId: string): void {
|
|
162
|
+
initSchema();
|
|
163
|
+
|
|
164
|
+
const db = getDb();
|
|
165
|
+
|
|
166
|
+
const spec = getActiveSpec();
|
|
167
|
+
if (!spec) {
|
|
168
|
+
console.error("\nNenhuma feature ativa.\n");
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const currentTask = getCurrentTask(spec.id);
|
|
173
|
+
if (!currentTask) {
|
|
174
|
+
console.error("\nNenhuma task em execucao.\n");
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const kid = parseInt(knowledgeId);
|
|
179
|
+
const knowledge = db.query("SELECT * FROM knowledge WHERE id = ?").get(kid) as any;
|
|
180
|
+
|
|
181
|
+
if (!knowledge) {
|
|
182
|
+
console.error(`\nKnowledge #${kid} nao encontrado.\n`);
|
|
183
|
+
process.exit(1);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const acknowledged = knowledge.acknowledged_by
|
|
187
|
+
? (JSON.parse(knowledge.acknowledged_by) as number[])
|
|
188
|
+
: [];
|
|
189
|
+
|
|
190
|
+
if (!acknowledged.includes(currentTask.id)) {
|
|
191
|
+
acknowledged.push(currentTask.id);
|
|
192
|
+
db.run("UPDATE knowledge SET acknowledged_by = ? WHERE id = ?", [
|
|
193
|
+
JSON.stringify(acknowledged),
|
|
194
|
+
kid,
|
|
195
|
+
]);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
console.log(`\nKnowledge #${kid} marcado como lido pela Task #${currentTask.number}.\n`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function getKnowledgeForTask(specId: string, taskId: number): any[] {
|
|
202
|
+
const db = getDb();
|
|
203
|
+
|
|
204
|
+
// Buscar todo knowledge da spec e filtrar em codigo
|
|
205
|
+
// Fix: LIKE '%taskId%' causava substring match (taskId=1 matchava "1,10,11")
|
|
206
|
+
const knowledge = db
|
|
207
|
+
.query(
|
|
208
|
+
`SELECT * FROM knowledge
|
|
209
|
+
WHERE spec_id = ?
|
|
210
|
+
ORDER BY severity DESC, created_at DESC
|
|
211
|
+
LIMIT 50`
|
|
212
|
+
)
|
|
213
|
+
.all(specId) as any[];
|
|
214
|
+
|
|
215
|
+
return knowledge.filter((k) => {
|
|
216
|
+
if (k.broadcast_to === 'all') return true;
|
|
217
|
+
const targets = k.broadcast_to.split(',').map((t: string) => parseInt(t.trim(), 10));
|
|
218
|
+
return targets.includes(taskId);
|
|
219
|
+
}).slice(0, 20);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function getUnreadKnowledgeForTask(specId: string, taskId: number): any[] {
|
|
223
|
+
const all = getKnowledgeForTask(specId, taskId);
|
|
224
|
+
|
|
225
|
+
return all.filter((k) => {
|
|
226
|
+
if (!k.acknowledged_by) return true;
|
|
227
|
+
const acked = JSON.parse(k.acknowledged_by) as number[];
|
|
228
|
+
return !acked.includes(taskId);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// v8.1: Consultar knowledge_graph - encontrar relacoes para arquivo, decisao ou pattern
|
|
233
|
+
export function queryGraph(options: {
|
|
234
|
+
file?: string;
|
|
235
|
+
decision?: string;
|
|
236
|
+
contradictions?: boolean;
|
|
237
|
+
json?: boolean;
|
|
238
|
+
}): void {
|
|
239
|
+
initSchema();
|
|
240
|
+
const db = getDb();
|
|
241
|
+
|
|
242
|
+
const spec = getActiveSpec();
|
|
243
|
+
if (!spec) {
|
|
244
|
+
console.error("\nNenhuma feature ativa.\n");
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Buscar contradicoes
|
|
249
|
+
if (options.contradictions) {
|
|
250
|
+
const contradictions = findContradictions(spec.id);
|
|
251
|
+
|
|
252
|
+
if (options.json) {
|
|
253
|
+
console.log(JSON.stringify({ contradictions }));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (contradictions.length === 0) {
|
|
258
|
+
console.log("\nNenhuma contradicao detectada.\n");
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
console.log(`\nContradicoes detectadas (${contradictions.length}):`);
|
|
263
|
+
console.log(`${"─".repeat(50)}`);
|
|
264
|
+
for (const c of contradictions) {
|
|
265
|
+
console.log(` [!] "${c.decision1}" <-> "${c.decision2}"`);
|
|
266
|
+
console.log(` Detectada em: ${c.createdAt}\n`);
|
|
267
|
+
}
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Buscar relacoes para um arquivo
|
|
272
|
+
if (options.file) {
|
|
273
|
+
const decisions = getRelatedDecisions(options.file, "file");
|
|
274
|
+
const relatedFiles = db.query(`
|
|
275
|
+
SELECT DISTINCT kg.target_id as file, kg.relation
|
|
276
|
+
FROM knowledge_graph kg
|
|
277
|
+
WHERE kg.source_id = ? AND kg.target_type = 'file'
|
|
278
|
+
UNION
|
|
279
|
+
SELECT DISTINCT kg.source_id as file, kg.relation
|
|
280
|
+
FROM knowledge_graph kg
|
|
281
|
+
WHERE kg.target_id = ? AND kg.source_type = 'file'
|
|
282
|
+
`).all(options.file, options.file) as any[];
|
|
283
|
+
|
|
284
|
+
if (options.json) {
|
|
285
|
+
console.log(JSON.stringify({ file: options.file, decisions, relatedFiles }));
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
console.log(`\nRelacoes para: ${options.file}`);
|
|
290
|
+
console.log(`${"─".repeat(50)}`);
|
|
291
|
+
|
|
292
|
+
if (decisions.length > 0) {
|
|
293
|
+
console.log(`\nDecisoes que afetam este arquivo:`);
|
|
294
|
+
for (const d of decisions) {
|
|
295
|
+
console.log(` - ${d.title}: ${d.decision}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (relatedFiles.length > 0) {
|
|
300
|
+
console.log(`\nArquivos relacionados:`);
|
|
301
|
+
for (const f of relatedFiles) {
|
|
302
|
+
console.log(` - ${f.file} (${f.relation})`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (decisions.length === 0 && relatedFiles.length === 0) {
|
|
307
|
+
console.log(" Nenhuma relacao encontrada.");
|
|
308
|
+
}
|
|
309
|
+
console.log();
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Buscar arquivos afetados por uma decisao
|
|
314
|
+
if (options.decision) {
|
|
315
|
+
const files = getRelatedFiles(options.decision, "decision");
|
|
316
|
+
|
|
317
|
+
if (options.json) {
|
|
318
|
+
console.log(JSON.stringify({ decision: options.decision, files }));
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
console.log(`\nArquivos afetados pela decisao "${options.decision}":`);
|
|
323
|
+
console.log(`${"─".repeat(50)}`);
|
|
324
|
+
if (files.length > 0) {
|
|
325
|
+
for (const f of files) {
|
|
326
|
+
console.log(` - ${f}`);
|
|
327
|
+
}
|
|
328
|
+
} else {
|
|
329
|
+
console.log(" Nenhum arquivo relacionado encontrado.");
|
|
330
|
+
}
|
|
331
|
+
console.log();
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Sem opcao: mostrar resumo do grafo
|
|
336
|
+
const totalRelations = db.query(
|
|
337
|
+
"SELECT COUNT(*) as c FROM knowledge_graph WHERE spec_id = ?"
|
|
338
|
+
).get(spec.id) as any;
|
|
339
|
+
|
|
340
|
+
const relationTypes = db.query(
|
|
341
|
+
"SELECT relation, COUNT(*) as c FROM knowledge_graph WHERE spec_id = ? GROUP BY relation ORDER BY c DESC"
|
|
342
|
+
).all(spec.id) as any[];
|
|
343
|
+
|
|
344
|
+
if (options.json) {
|
|
345
|
+
console.log(JSON.stringify({ totalRelations: totalRelations.c, relationTypes }));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
console.log(`\nKnowledge Graph: ${totalRelations.c} relacoes`);
|
|
350
|
+
console.log(`${"─".repeat(50)}`);
|
|
351
|
+
if (relationTypes.length > 0) {
|
|
352
|
+
for (const rt of relationTypes) {
|
|
353
|
+
console.log(` ${rt.relation}: ${rt.c}`);
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
console.log(" Grafo vazio. Relacoes serao criadas automaticamente durante a execucao.");
|
|
357
|
+
}
|
|
358
|
+
console.log(`\nComandos:`);
|
|
359
|
+
console.log(` knowledge graph --file <path> Relacoes de um arquivo`);
|
|
360
|
+
console.log(` knowledge graph --decision <id> Arquivos afetados por decisao`);
|
|
361
|
+
console.log(` knowledge graph --contradictions Detectar contradicoes\n`);
|
|
362
362
|
}
|