@cleocode/cleo 2026.4.51 → 2026.4.52
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/dist/cli/index.js +515 -36
- package/dist/cli/index.js.map +4 -4
- package/package.json +8 -8
package/dist/cli/index.js
CHANGED
|
@@ -9600,11 +9600,14 @@ var init_brain_schema = __esm({
|
|
|
9600
9600
|
source: text("source").notNull(),
|
|
9601
9601
|
/** Estimated tokens consumed by this retrieval. */
|
|
9602
9602
|
tokensUsed: integer("tokens_used"),
|
|
9603
|
+
/** Session ID (soft FK to tasks.db sessions). Enables grouping retrievals by session for STDP analysis. */
|
|
9604
|
+
sessionId: text("session_id"),
|
|
9603
9605
|
createdAt: text("created_at").notNull().default(sql2`(datetime('now'))`)
|
|
9604
9606
|
},
|
|
9605
9607
|
(table) => [
|
|
9606
9608
|
index("idx_retrieval_log_created").on(table.createdAt),
|
|
9607
|
-
index("idx_retrieval_log_source").on(table.source)
|
|
9609
|
+
index("idx_retrieval_log_source").on(table.source),
|
|
9610
|
+
index("idx_retrieval_log_session").on(table.sessionId)
|
|
9608
9611
|
]
|
|
9609
9612
|
);
|
|
9610
9613
|
brainPlasticityEvents = sqliteTable(
|
|
@@ -14952,7 +14955,7 @@ function clearEmbeddingProvider() {
|
|
|
14952
14955
|
currentProvider = null;
|
|
14953
14956
|
}
|
|
14954
14957
|
async function embedText(text3) {
|
|
14955
|
-
if (!currentProvider
|
|
14958
|
+
if (!currentProvider?.isAvailable()) return null;
|
|
14956
14959
|
return currentProvider.embed(text3);
|
|
14957
14960
|
}
|
|
14958
14961
|
function isEmbeddingAvailable() {
|
|
@@ -22823,7 +22826,7 @@ function parseIdPrefix(id) {
|
|
|
22823
22826
|
return null;
|
|
22824
22827
|
}
|
|
22825
22828
|
async function searchSimilar(query, projectRoot, limit) {
|
|
22826
|
-
if (!query
|
|
22829
|
+
if (!query?.trim()) return [];
|
|
22827
22830
|
if (!isEmbeddingAvailable()) return [];
|
|
22828
22831
|
const maxResults = limit ?? 10;
|
|
22829
22832
|
const queryVector = await embedText(query);
|
|
@@ -50370,7 +50373,7 @@ async function computeLastSession(projectRoot, scopeFilter) {
|
|
|
50370
50373
|
const accessor = await getAccessor(projectRoot);
|
|
50371
50374
|
const allSessions = await accessor.loadSessions();
|
|
50372
50375
|
const session = allSessions.find((s3) => s3.id === sessionId);
|
|
50373
|
-
if (!session
|
|
50376
|
+
if (!session?.endedAt) return null;
|
|
50374
50377
|
let duration3 = 0;
|
|
50375
50378
|
if (session.startedAt) {
|
|
50376
50379
|
duration3 = Math.round(
|
|
@@ -51557,10 +51560,17 @@ async function searchBrainCompact(projectRoot, params) {
|
|
|
51557
51560
|
setImmediate(() => {
|
|
51558
51561
|
incrementCitationCounts(projectRoot, returnedIds).catch(() => {
|
|
51559
51562
|
});
|
|
51560
|
-
|
|
51561
|
-
(
|
|
51562
|
-
|
|
51563
|
-
|
|
51563
|
+
getCurrentSessionId(projectRoot).then((sessionId) => {
|
|
51564
|
+
return logRetrieval(
|
|
51565
|
+
projectRoot,
|
|
51566
|
+
query,
|
|
51567
|
+
returnedIds,
|
|
51568
|
+
"find-rrf",
|
|
51569
|
+
results2.length * 50,
|
|
51570
|
+
sessionId
|
|
51571
|
+
);
|
|
51572
|
+
}).catch(() => {
|
|
51573
|
+
});
|
|
51564
51574
|
});
|
|
51565
51575
|
}
|
|
51566
51576
|
return { results: results2, total: results2.length, tokensEstimated: results2.length * 50 };
|
|
@@ -51623,7 +51633,16 @@ async function searchBrainCompact(projectRoot, params) {
|
|
|
51623
51633
|
setImmediate(() => {
|
|
51624
51634
|
incrementCitationCounts(projectRoot, returnedIds).catch(() => {
|
|
51625
51635
|
});
|
|
51626
|
-
|
|
51636
|
+
getCurrentSessionId(projectRoot).then((sessionId) => {
|
|
51637
|
+
return logRetrieval(
|
|
51638
|
+
projectRoot,
|
|
51639
|
+
query,
|
|
51640
|
+
returnedIds,
|
|
51641
|
+
"find",
|
|
51642
|
+
results.length * 50,
|
|
51643
|
+
sessionId
|
|
51644
|
+
);
|
|
51645
|
+
}).catch(() => {
|
|
51627
51646
|
});
|
|
51628
51647
|
});
|
|
51629
51648
|
}
|
|
@@ -51811,13 +51830,16 @@ async function fetchBrainEntries(projectRoot, params) {
|
|
|
51811
51830
|
setImmediate(() => {
|
|
51812
51831
|
incrementCitationCounts(projectRoot, fetchedIds).catch(() => {
|
|
51813
51832
|
});
|
|
51814
|
-
|
|
51815
|
-
|
|
51816
|
-
|
|
51817
|
-
|
|
51818
|
-
|
|
51819
|
-
|
|
51820
|
-
|
|
51833
|
+
getCurrentSessionId(projectRoot).then((sessionId) => {
|
|
51834
|
+
return logRetrieval(
|
|
51835
|
+
projectRoot,
|
|
51836
|
+
fetchedIds.join(","),
|
|
51837
|
+
fetchedIds,
|
|
51838
|
+
"fetch",
|
|
51839
|
+
results.length * 500,
|
|
51840
|
+
sessionId
|
|
51841
|
+
);
|
|
51842
|
+
}).catch(() => {
|
|
51821
51843
|
});
|
|
51822
51844
|
});
|
|
51823
51845
|
}
|
|
@@ -52235,6 +52257,15 @@ async function retrieveWithBudget(projectRoot, query, tokenBudget = 500, options
|
|
|
52235
52257
|
excluded
|
|
52236
52258
|
};
|
|
52237
52259
|
}
|
|
52260
|
+
async function getCurrentSessionId(projectRoot) {
|
|
52261
|
+
try {
|
|
52262
|
+
const { sessionStatus: sessionStatus3 } = await Promise.resolve().then(() => (init_sessions2(), sessions_exports2));
|
|
52263
|
+
const session = await sessionStatus3(projectRoot);
|
|
52264
|
+
return session?.id;
|
|
52265
|
+
} catch {
|
|
52266
|
+
return void 0;
|
|
52267
|
+
}
|
|
52268
|
+
}
|
|
52238
52269
|
async function incrementCitationCounts(projectRoot, ids) {
|
|
52239
52270
|
if (ids.length === 0) return;
|
|
52240
52271
|
const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
|
|
@@ -52261,13 +52292,13 @@ async function incrementCitationCounts(projectRoot, ids) {
|
|
|
52261
52292
|
}
|
|
52262
52293
|
}
|
|
52263
52294
|
}
|
|
52264
|
-
async function logRetrieval(projectRoot, query, entryIds, source, tokensUsed) {
|
|
52295
|
+
async function logRetrieval(projectRoot, query, entryIds, source, tokensUsed, sessionId) {
|
|
52265
52296
|
if (entryIds.length === 0) return;
|
|
52266
52297
|
const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
|
|
52267
52298
|
await getBrainDb2(projectRoot);
|
|
52268
52299
|
const nativeDb = getBrainNativeDb2();
|
|
52269
52300
|
if (!nativeDb) return;
|
|
52270
|
-
const createSql = "CREATE TABLE IF NOT EXISTS brain_retrieval_log (id INTEGER PRIMARY KEY AUTOINCREMENT,query TEXT NOT NULL,entry_ids TEXT NOT NULL,entry_count INTEGER NOT NULL,source TEXT NOT NULL,tokens_used INTEGER,created_at TEXT NOT NULL DEFAULT (datetime('now')))";
|
|
52301
|
+
const createSql = "CREATE TABLE IF NOT EXISTS brain_retrieval_log (id INTEGER PRIMARY KEY AUTOINCREMENT,query TEXT NOT NULL,entry_ids TEXT NOT NULL,entry_count INTEGER NOT NULL,source TEXT NOT NULL,tokens_used INTEGER,session_id TEXT,created_at TEXT NOT NULL DEFAULT (datetime('now')))";
|
|
52271
52302
|
try {
|
|
52272
52303
|
nativeDb.prepare(createSql).run();
|
|
52273
52304
|
} catch {
|
|
@@ -52275,8 +52306,15 @@ async function logRetrieval(projectRoot, query, entryIds, source, tokensUsed) {
|
|
|
52275
52306
|
}
|
|
52276
52307
|
try {
|
|
52277
52308
|
nativeDb.prepare(
|
|
52278
|
-
"INSERT INTO brain_retrieval_log (query, entry_ids, entry_count, source, tokens_used) VALUES (?, ?, ?, ?, ?)"
|
|
52279
|
-
).run(
|
|
52309
|
+
"INSERT INTO brain_retrieval_log (query, entry_ids, entry_count, source, tokens_used, session_id) VALUES (?, ?, ?, ?, ?, ?)"
|
|
52310
|
+
).run(
|
|
52311
|
+
query,
|
|
52312
|
+
entryIds.join(","),
|
|
52313
|
+
entryIds.length,
|
|
52314
|
+
source,
|
|
52315
|
+
tokensUsed ?? null,
|
|
52316
|
+
sessionId ?? null
|
|
52317
|
+
);
|
|
52280
52318
|
} catch {
|
|
52281
52319
|
}
|
|
52282
52320
|
}
|
|
@@ -58851,7 +58889,7 @@ function prepareSpawnMulti(skillNames, tokenValues, cwd) {
|
|
|
58851
58889
|
const skillName = skillNames[i];
|
|
58852
58890
|
const isPrimary = i === 0;
|
|
58853
58891
|
const skill = findSkill(skillName, cwd);
|
|
58854
|
-
if (!skill
|
|
58892
|
+
if (!skill?.content) {
|
|
58855
58893
|
continue;
|
|
58856
58894
|
}
|
|
58857
58895
|
let content = isPrimary ? skill.content : loadProgressive(skill.content);
|
|
@@ -74367,7 +74405,7 @@ async function injectProtocol(skillContent, taskId, tokenValues, cwd, tier) {
|
|
|
74367
74405
|
}
|
|
74368
74406
|
async function orchestratorSpawnSkill(taskId, skillName, tokenValues, cwd, tier) {
|
|
74369
74407
|
const skill = findSkill(skillName, cwd);
|
|
74370
|
-
if (!skill
|
|
74408
|
+
if (!skill?.content) {
|
|
74371
74409
|
throw new CleoError(4 /* NOT_FOUND */, `Skill not found: ${skillName}`, {
|
|
74372
74410
|
fix: `Check skills directory for ${skillName}/SKILL.md`
|
|
74373
74411
|
});
|
|
@@ -74937,7 +74975,7 @@ async function buildPrompt(taskId, templateName = "TASK-EXECUTOR", cwd, tier) {
|
|
|
74937
74975
|
throw new CleoError(4 /* NOT_FOUND */, `Task ${taskId} not found`);
|
|
74938
74976
|
}
|
|
74939
74977
|
const skill = findSkill(templateName, cwd);
|
|
74940
|
-
if (!skill
|
|
74978
|
+
if (!skill?.content) {
|
|
74941
74979
|
const { canonical } = mapSkillName(templateName);
|
|
74942
74980
|
throw new CleoError(4 /* NOT_FOUND */, `Skill template ${templateName} not found`, {
|
|
74943
74981
|
fix: `Expected at skills/${canonical}/SKILL.md`
|
|
@@ -77732,7 +77770,7 @@ async function analyzeArchive(opts, accessor) {
|
|
|
77732
77770
|
const acc = accessor ?? await getAccessor(opts.cwd);
|
|
77733
77771
|
const data = await acc.loadArchive();
|
|
77734
77772
|
const reportType = opts.report ?? "summary";
|
|
77735
|
-
if (!data
|
|
77773
|
+
if (!data?.archivedTasks?.length) {
|
|
77736
77774
|
return {
|
|
77737
77775
|
report: reportType,
|
|
77738
77776
|
filters: null,
|
|
@@ -85793,16 +85831,12 @@ function allEpicChildrenVerified(epicId, tasks2) {
|
|
|
85793
85831
|
if (children.length === 0) return false;
|
|
85794
85832
|
const incomplete = children.filter((t) => t.status !== "done");
|
|
85795
85833
|
if (incomplete.length > 0) return false;
|
|
85796
|
-
const unverified = children.filter(
|
|
85797
|
-
(t) => t.status === "done" && (!t.verification || !t.verification.passed)
|
|
85798
|
-
);
|
|
85834
|
+
const unverified = children.filter((t) => t.status === "done" && !t.verification?.passed);
|
|
85799
85835
|
return unverified.length === 0;
|
|
85800
85836
|
}
|
|
85801
85837
|
function allSiblingsVerified(parentId, tasks2) {
|
|
85802
85838
|
const siblings = tasks2.filter((t) => t.parentId === parentId);
|
|
85803
|
-
const unverifiedDone = siblings.filter(
|
|
85804
|
-
(t) => t.status === "done" && (!t.verification || !t.verification.passed)
|
|
85805
|
-
);
|
|
85839
|
+
const unverifiedDone = siblings.filter((t) => t.status === "done" && !t.verification?.passed);
|
|
85806
85840
|
const incomplete = siblings.filter(
|
|
85807
85841
|
(t) => t.status === "pending" || t.status === "active" || t.status === "blocked"
|
|
85808
85842
|
);
|
|
@@ -87208,7 +87242,7 @@ var init_init = __esm({
|
|
|
87208
87242
|
// packages/core/src/sessions/context-alert.ts
|
|
87209
87243
|
import { existsSync as existsSync109, readFileSync as readFileSync80, writeFileSync as writeFileSync20 } from "node:fs";
|
|
87210
87244
|
import { join as join109 } from "node:path";
|
|
87211
|
-
function
|
|
87245
|
+
function getCurrentSessionId2(cwd) {
|
|
87212
87246
|
if (process.env.CLEO_SESSION) return process.env.CLEO_SESSION;
|
|
87213
87247
|
const sessionFile = join109(getCleoDir(cwd), ".current-session");
|
|
87214
87248
|
if (existsSync109(sessionFile)) {
|
|
@@ -87242,7 +87276,7 @@ function createCliMeta(operation, duration_ms = 0) {
|
|
|
87242
87276
|
duration_ms,
|
|
87243
87277
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
87244
87278
|
};
|
|
87245
|
-
const sessionId =
|
|
87279
|
+
const sessionId = getCurrentSessionId2();
|
|
87246
87280
|
if (sessionId) {
|
|
87247
87281
|
meta3["sessionId"] = sessionId;
|
|
87248
87282
|
}
|
|
@@ -90912,6 +90946,160 @@ var init_brain_backfill = __esm({
|
|
|
90912
90946
|
}
|
|
90913
90947
|
});
|
|
90914
90948
|
|
|
90949
|
+
// packages/core/src/memory/brain-export.ts
|
|
90950
|
+
async function exportBrainAsGexf(projectRoot) {
|
|
90951
|
+
const db = await getBrainDb(projectRoot);
|
|
90952
|
+
let nodes = [];
|
|
90953
|
+
let edges = [];
|
|
90954
|
+
try {
|
|
90955
|
+
nodes = await db.select({
|
|
90956
|
+
id: brainPageNodes.id,
|
|
90957
|
+
nodeType: brainPageNodes.nodeType,
|
|
90958
|
+
label: brainPageNodes.label,
|
|
90959
|
+
qualityScore: brainPageNodes.qualityScore,
|
|
90960
|
+
contentHash: brainPageNodes.contentHash,
|
|
90961
|
+
lastActivityAt: brainPageNodes.lastActivityAt,
|
|
90962
|
+
metadataJson: brainPageNodes.metadataJson,
|
|
90963
|
+
createdAt: brainPageNodes.createdAt,
|
|
90964
|
+
updatedAt: brainPageNodes.updatedAt
|
|
90965
|
+
}).from(brainPageNodes);
|
|
90966
|
+
} catch {
|
|
90967
|
+
nodes = [];
|
|
90968
|
+
}
|
|
90969
|
+
try {
|
|
90970
|
+
const rawEdges = await db.select({
|
|
90971
|
+
fromId: brainPageEdges.fromId,
|
|
90972
|
+
toId: brainPageEdges.toId,
|
|
90973
|
+
edgeType: brainPageEdges.edgeType,
|
|
90974
|
+
weight: brainPageEdges.weight,
|
|
90975
|
+
createdAt: brainPageEdges.createdAt
|
|
90976
|
+
}).from(brainPageEdges);
|
|
90977
|
+
edges = rawEdges.map((e) => ({ ...e, provenance: null }));
|
|
90978
|
+
} catch {
|
|
90979
|
+
edges = [];
|
|
90980
|
+
}
|
|
90981
|
+
const gexf = buildGexfDocument(nodes, edges);
|
|
90982
|
+
return {
|
|
90983
|
+
success: true,
|
|
90984
|
+
format: "gexf",
|
|
90985
|
+
nodeCount: nodes.length,
|
|
90986
|
+
edgeCount: edges.length,
|
|
90987
|
+
content: gexf,
|
|
90988
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
90989
|
+
};
|
|
90990
|
+
}
|
|
90991
|
+
async function exportBrainAsJson(projectRoot) {
|
|
90992
|
+
const db = await getBrainDb(projectRoot);
|
|
90993
|
+
let nodes = [];
|
|
90994
|
+
let edges = [];
|
|
90995
|
+
try {
|
|
90996
|
+
nodes = await db.select({
|
|
90997
|
+
id: brainPageNodes.id,
|
|
90998
|
+
nodeType: brainPageNodes.nodeType,
|
|
90999
|
+
label: brainPageNodes.label,
|
|
91000
|
+
qualityScore: brainPageNodes.qualityScore,
|
|
91001
|
+
contentHash: brainPageNodes.contentHash,
|
|
91002
|
+
lastActivityAt: brainPageNodes.lastActivityAt,
|
|
91003
|
+
metadataJson: brainPageNodes.metadataJson,
|
|
91004
|
+
createdAt: brainPageNodes.createdAt,
|
|
91005
|
+
updatedAt: brainPageNodes.updatedAt
|
|
91006
|
+
}).from(brainPageNodes);
|
|
91007
|
+
} catch {
|
|
91008
|
+
nodes = [];
|
|
91009
|
+
}
|
|
91010
|
+
try {
|
|
91011
|
+
const rawEdges = await db.select({
|
|
91012
|
+
fromId: brainPageEdges.fromId,
|
|
91013
|
+
toId: brainPageEdges.toId,
|
|
91014
|
+
edgeType: brainPageEdges.edgeType,
|
|
91015
|
+
weight: brainPageEdges.weight,
|
|
91016
|
+
createdAt: brainPageEdges.createdAt
|
|
91017
|
+
}).from(brainPageEdges);
|
|
91018
|
+
edges = rawEdges.map((e) => ({ ...e, provenance: null }));
|
|
91019
|
+
} catch {
|
|
91020
|
+
edges = [];
|
|
91021
|
+
}
|
|
91022
|
+
return {
|
|
91023
|
+
success: true,
|
|
91024
|
+
format: "json",
|
|
91025
|
+
nodeCount: nodes.length,
|
|
91026
|
+
edgeCount: edges.length,
|
|
91027
|
+
nodes,
|
|
91028
|
+
edges,
|
|
91029
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
91030
|
+
};
|
|
91031
|
+
}
|
|
91032
|
+
function buildGexfDocument(nodes, edges) {
|
|
91033
|
+
const lines = [
|
|
91034
|
+
'<?xml version="1.0" encoding="UTF-8"?>',
|
|
91035
|
+
'<gexf xmlns="http://www.gexf.net/1.3draft" version="1.3">',
|
|
91036
|
+
' <meta lastmodifieddate="' + (/* @__PURE__ */ new Date()).toISOString() + '">',
|
|
91037
|
+
" <creator>CLEO Brain Export (T626-M6)</creator>",
|
|
91038
|
+
" <description>Living brain knowledge graph (brain_page_nodes + brain_page_edges)</description>",
|
|
91039
|
+
" </meta>",
|
|
91040
|
+
' <graph mode="static" defaultedgetype="directed">'
|
|
91041
|
+
];
|
|
91042
|
+
lines.push(' <attributes class="node">');
|
|
91043
|
+
lines.push(' <attribute id="node_type" title="Node Type" type="string"/>');
|
|
91044
|
+
lines.push(' <attribute id="quality_score" title="Quality Score" type="double"/>');
|
|
91045
|
+
lines.push(' <attribute id="content_hash" title="Content Hash" type="string"/>');
|
|
91046
|
+
lines.push(' <attribute id="last_activity_at" title="Last Activity" type="string"/>');
|
|
91047
|
+
lines.push(' <attribute id="created_at" title="Created At" type="string"/>');
|
|
91048
|
+
lines.push(" </attributes>");
|
|
91049
|
+
lines.push(' <attributes class="edge">');
|
|
91050
|
+
lines.push(' <attribute id="edge_type" title="Edge Type" type="string"/>');
|
|
91051
|
+
lines.push(' <attribute id="provenance" title="Provenance" type="string"/>');
|
|
91052
|
+
lines.push(' <attribute id="created_at" title="Created At" type="string"/>');
|
|
91053
|
+
lines.push(" </attributes>");
|
|
91054
|
+
lines.push(" <nodes>");
|
|
91055
|
+
for (const node of nodes) {
|
|
91056
|
+
lines.push(` <node id="${escapeXml(node.id)}" label="${escapeXml(node.label)}">`);
|
|
91057
|
+
lines.push(" <attvalues>");
|
|
91058
|
+
lines.push(` <attvalue for="node_type" value="${escapeXml(node.nodeType)}"/>`);
|
|
91059
|
+
lines.push(` <attvalue for="quality_score" value="${node.qualityScore ?? 0.5}"/>`);
|
|
91060
|
+
if (node.contentHash) {
|
|
91061
|
+
lines.push(` <attvalue for="content_hash" value="${escapeXml(node.contentHash)}"/>`);
|
|
91062
|
+
}
|
|
91063
|
+
lines.push(
|
|
91064
|
+
` <attvalue for="last_activity_at" value="${escapeXml(node.lastActivityAt)}"/>`
|
|
91065
|
+
);
|
|
91066
|
+
lines.push(` <attvalue for="created_at" value="${escapeXml(node.createdAt)}"/>`);
|
|
91067
|
+
lines.push(" </attvalues>");
|
|
91068
|
+
lines.push(" </node>");
|
|
91069
|
+
}
|
|
91070
|
+
lines.push(" </nodes>");
|
|
91071
|
+
lines.push(" <edges>");
|
|
91072
|
+
for (let i = 0; i < edges.length; i++) {
|
|
91073
|
+
const edge = edges[i];
|
|
91074
|
+
const weight = edge.weight ?? 1;
|
|
91075
|
+
lines.push(
|
|
91076
|
+
` <edge id="${i}" source="${escapeXml(edge.fromId)}" target="${escapeXml(edge.toId)}" weight="${weight}">`
|
|
91077
|
+
);
|
|
91078
|
+
lines.push(" <attvalues>");
|
|
91079
|
+
lines.push(` <attvalue for="edge_type" value="${escapeXml(edge.edgeType)}"/>`);
|
|
91080
|
+
if (edge.provenance) {
|
|
91081
|
+
lines.push(` <attvalue for="provenance" value="${escapeXml(edge.provenance)}"/>`);
|
|
91082
|
+
}
|
|
91083
|
+
lines.push(` <attvalue for="created_at" value="${escapeXml(edge.createdAt)}"/>`);
|
|
91084
|
+
lines.push(" </attvalues>");
|
|
91085
|
+
lines.push(" </edge>");
|
|
91086
|
+
}
|
|
91087
|
+
lines.push(" </edges>");
|
|
91088
|
+
lines.push(" </graph>");
|
|
91089
|
+
lines.push("</gexf>");
|
|
91090
|
+
return lines.join("\n");
|
|
91091
|
+
}
|
|
91092
|
+
function escapeXml(text3) {
|
|
91093
|
+
return text3.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
91094
|
+
}
|
|
91095
|
+
var init_brain_export = __esm({
|
|
91096
|
+
"packages/core/src/memory/brain-export.ts"() {
|
|
91097
|
+
"use strict";
|
|
91098
|
+
init_brain_schema();
|
|
91099
|
+
init_brain_sqlite();
|
|
91100
|
+
}
|
|
91101
|
+
});
|
|
91102
|
+
|
|
90915
91103
|
// packages/core/src/memory/brain-maintenance.ts
|
|
90916
91104
|
async function runBrainMaintenance(projectRoot, options) {
|
|
90917
91105
|
const {
|
|
@@ -91321,7 +91509,7 @@ Use --source <path> to specify a custom location.`
|
|
|
91321
91509
|
}
|
|
91322
91510
|
try {
|
|
91323
91511
|
for (const row of batch) {
|
|
91324
|
-
if (!row.learned
|
|
91512
|
+
if (!row.learned?.trim()) {
|
|
91325
91513
|
continue;
|
|
91326
91514
|
}
|
|
91327
91515
|
const learnId = `CML-${row.id}`;
|
|
@@ -98701,7 +98889,7 @@ function measureDependencyDepth(taskId, taskMap, visited = /* @__PURE__ */ new S
|
|
|
98701
98889
|
if (visited.has(taskId)) return 0;
|
|
98702
98890
|
visited.add(taskId);
|
|
98703
98891
|
const task = taskMap.get(taskId);
|
|
98704
|
-
if (!task
|
|
98892
|
+
if (!task?.depends || task.depends.length === 0) return 0;
|
|
98705
98893
|
let maxDepth = 0;
|
|
98706
98894
|
for (const depId of task.depends) {
|
|
98707
98895
|
const depth = 1 + measureDependencyDepth(depId, taskMap, visited);
|
|
@@ -99055,7 +99243,7 @@ async function coreTaskUnarchive(projectRoot, taskId, params) {
|
|
|
99055
99243
|
throw new Error(`Task '${taskId}' already exists in active tasks`);
|
|
99056
99244
|
}
|
|
99057
99245
|
const archive = await accessor.loadArchive();
|
|
99058
|
-
if (!archive
|
|
99246
|
+
if (!archive?.archivedTasks) {
|
|
99059
99247
|
throw new Error("No archive file found");
|
|
99060
99248
|
}
|
|
99061
99249
|
const taskIndex = archive.archivedTasks.findIndex((t) => t.id === taskId);
|
|
@@ -113440,6 +113628,8 @@ __export(internal_exports, {
|
|
|
113440
113628
|
ensureSqliteDb: () => ensureSqliteDb,
|
|
113441
113629
|
estimateContext: () => estimateContext,
|
|
113442
113630
|
executeTransfer: () => executeTransfer,
|
|
113631
|
+
exportBrainAsGexf: () => exportBrainAsGexf,
|
|
113632
|
+
exportBrainAsJson: () => exportBrainAsJson,
|
|
113443
113633
|
exportSnapshot: () => exportSnapshot,
|
|
113444
113634
|
exportTasks: () => exportTasks,
|
|
113445
113635
|
exportTasksPackage: () => exportTasksPackage,
|
|
@@ -113509,7 +113699,7 @@ __export(internal_exports, {
|
|
|
113509
113699
|
getConfigValue: () => getConfigValue,
|
|
113510
113700
|
getContextDrift: () => getContextDrift,
|
|
113511
113701
|
getCriticalPath: () => getCriticalPath,
|
|
113512
|
-
getCurrentSessionId: () =>
|
|
113702
|
+
getCurrentSessionId: () => getCurrentSessionId2,
|
|
113513
113703
|
getDashboard: () => getDashboard,
|
|
113514
113704
|
getDb: () => getDb,
|
|
113515
113705
|
getDecisionLog: () => getDecisionLog,
|
|
@@ -114017,6 +114207,7 @@ var init_internal = __esm({
|
|
|
114017
114207
|
init_stages();
|
|
114018
114208
|
init_tessera_engine();
|
|
114019
114209
|
init_brain_backfill();
|
|
114210
|
+
init_brain_export();
|
|
114020
114211
|
init_brain_lifecycle();
|
|
114021
114212
|
init_brain_maintenance();
|
|
114022
114213
|
init_brain_purge();
|
|
@@ -136332,6 +136523,47 @@ Recent Events (newest first, limit=${limit})`);
|
|
|
136332
136523
|
process.exit(1);
|
|
136333
136524
|
}
|
|
136334
136525
|
});
|
|
136526
|
+
brain.command("export").description("Export brain graph as GEXF (Gephi) or JSON format").option(
|
|
136527
|
+
"--format <format>",
|
|
136528
|
+
"Export format: gexf (Gephi standard) or json (flat arrays)",
|
|
136529
|
+
"gexf"
|
|
136530
|
+
).option("--output <file>", "Write to file instead of stdout (optional)").action(async (opts) => {
|
|
136531
|
+
const root = getProjectRoot();
|
|
136532
|
+
const format = opts.format ?? "gexf";
|
|
136533
|
+
if (format !== "gexf" && format !== "json") {
|
|
136534
|
+
console.error(`Invalid format: ${format}. Use 'gexf' or 'json'.`);
|
|
136535
|
+
process.exit(1);
|
|
136536
|
+
}
|
|
136537
|
+
try {
|
|
136538
|
+
let content;
|
|
136539
|
+
let nodeCount;
|
|
136540
|
+
let edgeCount;
|
|
136541
|
+
if (format === "gexf") {
|
|
136542
|
+
const result = await exportBrainAsGexf(root);
|
|
136543
|
+
content = result.content;
|
|
136544
|
+
nodeCount = result.nodeCount;
|
|
136545
|
+
edgeCount = result.edgeCount;
|
|
136546
|
+
} else {
|
|
136547
|
+
const result = await exportBrainAsJson(root);
|
|
136548
|
+
content = JSON.stringify(result, null, 2);
|
|
136549
|
+
nodeCount = result.nodeCount;
|
|
136550
|
+
edgeCount = result.edgeCount;
|
|
136551
|
+
}
|
|
136552
|
+
if (opts.output) {
|
|
136553
|
+
const fs11 = await import("node:fs");
|
|
136554
|
+
fs11.writeFileSync(opts.output, content, "utf-8");
|
|
136555
|
+
console.log(
|
|
136556
|
+
`Exported to ${opts.output}: ${nodeCount} nodes, ${edgeCount} edges (${format.toUpperCase()})`
|
|
136557
|
+
);
|
|
136558
|
+
} else {
|
|
136559
|
+
console.log(content);
|
|
136560
|
+
}
|
|
136561
|
+
} catch (err) {
|
|
136562
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
136563
|
+
console.error(`Brain export failed: ${message}`);
|
|
136564
|
+
process.exit(1);
|
|
136565
|
+
}
|
|
136566
|
+
});
|
|
136335
136567
|
}
|
|
136336
136568
|
|
|
136337
136569
|
// packages/cleo/src/cli/commands/briefing.ts
|
|
@@ -139643,6 +139875,175 @@ var NODE_KIND_PRIORITY = {
|
|
|
139643
139875
|
file: 40,
|
|
139644
139876
|
folder: 41
|
|
139645
139877
|
};
|
|
139878
|
+
function generateGexf(nodes, relations) {
|
|
139879
|
+
const nodeById = /* @__PURE__ */ new Map();
|
|
139880
|
+
for (const n of nodes) {
|
|
139881
|
+
nodeById.set(String(n["id"]), n);
|
|
139882
|
+
}
|
|
139883
|
+
const kindColors = {
|
|
139884
|
+
function: "#3498db",
|
|
139885
|
+
// blue
|
|
139886
|
+
method: "#2980b9",
|
|
139887
|
+
// darker blue
|
|
139888
|
+
class: "#e74c3c",
|
|
139889
|
+
// red
|
|
139890
|
+
interface: "#e67e22",
|
|
139891
|
+
// orange
|
|
139892
|
+
file: "#95a5a6",
|
|
139893
|
+
// gray
|
|
139894
|
+
folder: "#34495e",
|
|
139895
|
+
// dark gray
|
|
139896
|
+
community: "#9b59b6",
|
|
139897
|
+
// purple
|
|
139898
|
+
process: "#1abc9c",
|
|
139899
|
+
// teal
|
|
139900
|
+
import: "#f39c12",
|
|
139901
|
+
// amber
|
|
139902
|
+
default: "#7f8c8d"
|
|
139903
|
+
// medium gray
|
|
139904
|
+
};
|
|
139905
|
+
const getNodeColor = (kind) => {
|
|
139906
|
+
return kindColors[kind] ?? kindColors["default"];
|
|
139907
|
+
};
|
|
139908
|
+
let gexf = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
139909
|
+
gexf += '<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:viz="http://www.gexf.net/1.2draft/viz" version="1.2">\n';
|
|
139910
|
+
gexf += ' <meta lastmodifieddate="' + (/* @__PURE__ */ new Date()).toISOString().split("T")[0] + '">\n';
|
|
139911
|
+
gexf += " <creator>CLEO nexus export</creator>\n";
|
|
139912
|
+
gexf += " <description>Code intelligence graph from CLEO nexus</description>\n";
|
|
139913
|
+
gexf += " </meta>\n";
|
|
139914
|
+
gexf += ' <graph mode="static" defaultedgetype="directed">\n';
|
|
139915
|
+
gexf += ' <attributes class="node">\n';
|
|
139916
|
+
gexf += ' <attribute id="kind" title="Node Kind" type="string" />\n';
|
|
139917
|
+
gexf += ' <attribute id="filePath" title="File Path" type="string" />\n';
|
|
139918
|
+
gexf += ' <attribute id="language" title="Language" type="string" />\n';
|
|
139919
|
+
gexf += ' <attribute id="startLine" title="Start Line" type="integer" />\n';
|
|
139920
|
+
gexf += ' <attribute id="endLine" title="End Line" type="integer" />\n';
|
|
139921
|
+
gexf += ' <attribute id="isExported" title="Is Exported" type="boolean" />\n';
|
|
139922
|
+
gexf += ' <attribute id="projectId" title="Project ID" type="string" />\n';
|
|
139923
|
+
gexf += " </attributes>\n";
|
|
139924
|
+
gexf += ' <attributes class="edge">\n';
|
|
139925
|
+
gexf += ' <attribute id="relationType" title="Relation Type" type="string" />\n';
|
|
139926
|
+
gexf += ' <attribute id="confidence" title="Confidence" type="double" />\n';
|
|
139927
|
+
gexf += ' <attribute id="reason" title="Reason" type="string" />\n';
|
|
139928
|
+
gexf += " </attributes>\n";
|
|
139929
|
+
gexf += " <nodes>\n";
|
|
139930
|
+
for (const node of nodes) {
|
|
139931
|
+
const nodeId = String(node["id"]).replace(/[<>"'&]/g, (c) => {
|
|
139932
|
+
const map2 = {
|
|
139933
|
+
"<": "<",
|
|
139934
|
+
">": ">",
|
|
139935
|
+
'"': """,
|
|
139936
|
+
"'": "'",
|
|
139937
|
+
"&": "&"
|
|
139938
|
+
};
|
|
139939
|
+
return map2[c];
|
|
139940
|
+
});
|
|
139941
|
+
const label = String(node["label"] ?? node["id"]);
|
|
139942
|
+
const kind = String(node["kind"] ?? "unknown");
|
|
139943
|
+
const color = getNodeColor(kind);
|
|
139944
|
+
gexf += ` <node id="${nodeId}" label="${escapeXml2(label)}">
|
|
139945
|
+
`;
|
|
139946
|
+
gexf += ` <viz:color r="${hexToRgb(color).r}" g="${hexToRgb(color).g}" b="${hexToRgb(color).b}" />
|
|
139947
|
+
`;
|
|
139948
|
+
gexf += " <attvalues>\n";
|
|
139949
|
+
gexf += ` <attvalue id="kind" value="${escapeXml2(kind)}" />
|
|
139950
|
+
`;
|
|
139951
|
+
if (node["filePath"]) {
|
|
139952
|
+
gexf += ` <attvalue id="filePath" value="${escapeXml2(String(node["filePath"]))}" />
|
|
139953
|
+
`;
|
|
139954
|
+
}
|
|
139955
|
+
if (node["language"]) {
|
|
139956
|
+
gexf += ` <attvalue id="language" value="${escapeXml2(String(node["language"]))}" />
|
|
139957
|
+
`;
|
|
139958
|
+
}
|
|
139959
|
+
if (node["startLine"] != null) {
|
|
139960
|
+
gexf += ` <attvalue id="startLine" value="${node["startLine"]}" />
|
|
139961
|
+
`;
|
|
139962
|
+
}
|
|
139963
|
+
if (node["endLine"] != null) {
|
|
139964
|
+
gexf += ` <attvalue id="endLine" value="${node["endLine"]}" />
|
|
139965
|
+
`;
|
|
139966
|
+
}
|
|
139967
|
+
if (node["isExported"] != null) {
|
|
139968
|
+
gexf += ` <attvalue id="isExported" value="${node["isExported"] ? "true" : "false"}" />
|
|
139969
|
+
`;
|
|
139970
|
+
}
|
|
139971
|
+
if (node["projectId"]) {
|
|
139972
|
+
gexf += ` <attvalue id="projectId" value="${escapeXml2(String(node["projectId"]))}" />
|
|
139973
|
+
`;
|
|
139974
|
+
}
|
|
139975
|
+
gexf += " </attvalues>\n";
|
|
139976
|
+
gexf += " </node>\n";
|
|
139977
|
+
}
|
|
139978
|
+
gexf += " </nodes>\n";
|
|
139979
|
+
gexf += " <edges>\n";
|
|
139980
|
+
for (let i = 0; i < relations.length; i++) {
|
|
139981
|
+
const rel = relations[i];
|
|
139982
|
+
const sourceId = String(rel["sourceId"]).replace(/[<>"'&]/g, (c) => {
|
|
139983
|
+
const map2 = {
|
|
139984
|
+
"<": "<",
|
|
139985
|
+
">": ">",
|
|
139986
|
+
'"': """,
|
|
139987
|
+
"'": "'",
|
|
139988
|
+
"&": "&"
|
|
139989
|
+
};
|
|
139990
|
+
return map2[c];
|
|
139991
|
+
});
|
|
139992
|
+
const targetId = String(rel["targetId"]).replace(/[<>"'&]/g, (c) => {
|
|
139993
|
+
const map2 = {
|
|
139994
|
+
"<": "<",
|
|
139995
|
+
">": ">",
|
|
139996
|
+
'"': """,
|
|
139997
|
+
"'": "'",
|
|
139998
|
+
"&": "&"
|
|
139999
|
+
};
|
|
140000
|
+
return map2[c];
|
|
140001
|
+
});
|
|
140002
|
+
if (!nodeById.has(String(rel["sourceId"])) || !nodeById.has(String(rel["targetId"]))) {
|
|
140003
|
+
continue;
|
|
140004
|
+
}
|
|
140005
|
+
const confidence = typeof rel["confidence"] === "number" ? rel["confidence"] : 1;
|
|
140006
|
+
const relationType = String(rel["type"] ?? "unknown");
|
|
140007
|
+
const reason = rel["reason"] ? String(rel["reason"]) : "";
|
|
140008
|
+
gexf += ` <edge id="e${i}" source="${sourceId}" target="${targetId}" weight="${confidence}">
|
|
140009
|
+
`;
|
|
140010
|
+
gexf += " <attvalues>\n";
|
|
140011
|
+
gexf += ` <attvalue id="relationType" value="${escapeXml2(relationType)}" />
|
|
140012
|
+
`;
|
|
140013
|
+
gexf += ` <attvalue id="confidence" value="${confidence}" />
|
|
140014
|
+
`;
|
|
140015
|
+
if (reason) {
|
|
140016
|
+
gexf += ` <attvalue id="reason" value="${escapeXml2(reason)}" />
|
|
140017
|
+
`;
|
|
140018
|
+
}
|
|
140019
|
+
gexf += " </attvalues>\n";
|
|
140020
|
+
gexf += " </edge>\n";
|
|
140021
|
+
}
|
|
140022
|
+
gexf += " </edges>\n";
|
|
140023
|
+
gexf += " </graph>\n";
|
|
140024
|
+
gexf += "</gexf>\n";
|
|
140025
|
+
return gexf;
|
|
140026
|
+
}
|
|
140027
|
+
function escapeXml2(str) {
|
|
140028
|
+
return String(str).replace(/[<>"'&]/g, (c) => {
|
|
140029
|
+
const map2 = {
|
|
140030
|
+
"<": "<",
|
|
140031
|
+
">": ">",
|
|
140032
|
+
'"': """,
|
|
140033
|
+
"'": "'",
|
|
140034
|
+
"&": "&"
|
|
140035
|
+
};
|
|
140036
|
+
return map2[c];
|
|
140037
|
+
});
|
|
140038
|
+
}
|
|
140039
|
+
function hexToRgb(hex3) {
|
|
140040
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex3);
|
|
140041
|
+
return result ? {
|
|
140042
|
+
r: parseInt(result[1], 16),
|
|
140043
|
+
g: parseInt(result[2], 16),
|
|
140044
|
+
b: parseInt(result[3], 16)
|
|
140045
|
+
} : { r: 127, g: 140, b: 141 };
|
|
140046
|
+
}
|
|
139646
140047
|
function sortMatchingNodes(nodes, symbolName) {
|
|
139647
140048
|
const lowerSymbol = symbolName.toLowerCase();
|
|
139648
140049
|
return [...nodes].sort((a, b2) => {
|
|
@@ -140877,6 +141278,84 @@ function registerNexusCommand(program) {
|
|
|
140877
141278
|
process.exitCode = 1;
|
|
140878
141279
|
}
|
|
140879
141280
|
});
|
|
141281
|
+
nexus.command("export").description("Export nexus graph to GEXF (Gephi) or JSON format").option("--format <format>", "Output format: gexf, json", "gexf").option("--output <file>", "Output file path (stdout if omitted)").option("--project <id>", "Filter by project ID (exports all projects if omitted)").action(async (opts) => {
|
|
141282
|
+
const startTime = Date.now();
|
|
141283
|
+
const format = opts["format"] ?? "gexf";
|
|
141284
|
+
const outputFile = opts["output"];
|
|
141285
|
+
const projectFilter = opts["project"];
|
|
141286
|
+
try {
|
|
141287
|
+
const { getNexusDb: getNexusDb2, nexusSchema } = await import("@cleocode/core/store/nexus-sqlite");
|
|
141288
|
+
const db = await getNexusDb2();
|
|
141289
|
+
let allNodes = [];
|
|
141290
|
+
let allRelations = [];
|
|
141291
|
+
try {
|
|
141292
|
+
allNodes = db.select().from(nexusSchema.nexusNodes).all();
|
|
141293
|
+
allRelations = db.select().from(nexusSchema.nexusRelations).all();
|
|
141294
|
+
} catch {
|
|
141295
|
+
}
|
|
141296
|
+
const nodes = projectFilter ? allNodes.filter((n) => n["projectId"] === projectFilter) : allNodes;
|
|
141297
|
+
const relations = projectFilter ? allRelations.filter((r) => r["projectId"] === projectFilter) : allRelations;
|
|
141298
|
+
let output = "";
|
|
141299
|
+
if (format === "json") {
|
|
141300
|
+
output = JSON.stringify(
|
|
141301
|
+
{
|
|
141302
|
+
nodes: nodes.map((n) => ({
|
|
141303
|
+
id: n["id"],
|
|
141304
|
+
kind: n["kind"],
|
|
141305
|
+
label: n["label"],
|
|
141306
|
+
name: n["name"],
|
|
141307
|
+
filePath: n["filePath"],
|
|
141308
|
+
language: n["language"],
|
|
141309
|
+
isExported: n["isExported"],
|
|
141310
|
+
startLine: n["startLine"],
|
|
141311
|
+
endLine: n["endLine"],
|
|
141312
|
+
projectId: n["projectId"]
|
|
141313
|
+
})),
|
|
141314
|
+
edges: relations.map((r) => ({
|
|
141315
|
+
id: r["id"],
|
|
141316
|
+
source: r["sourceId"],
|
|
141317
|
+
target: r["targetId"],
|
|
141318
|
+
type: r["type"],
|
|
141319
|
+
confidence: r["confidence"],
|
|
141320
|
+
reason: r["reason"]
|
|
141321
|
+
}))
|
|
141322
|
+
},
|
|
141323
|
+
null,
|
|
141324
|
+
2
|
|
141325
|
+
);
|
|
141326
|
+
} else if (format === "gexf") {
|
|
141327
|
+
output = generateGexf(nodes, relations);
|
|
141328
|
+
} else {
|
|
141329
|
+
process.stderr.write(
|
|
141330
|
+
`[nexus] Error: Unknown format '${format}'. Supported: gexf, json
|
|
141331
|
+
`
|
|
141332
|
+
);
|
|
141333
|
+
process.exitCode = 1;
|
|
141334
|
+
return;
|
|
141335
|
+
}
|
|
141336
|
+
if (outputFile) {
|
|
141337
|
+
const { writeFileSync: writeFileSync26 } = await import("node:fs");
|
|
141338
|
+
writeFileSync26(outputFile, output, "utf-8");
|
|
141339
|
+
process.stdout.write(
|
|
141340
|
+
`[nexus] Exported to ${outputFile} (${nodes.length} nodes, ${relations.length} edges)
|
|
141341
|
+
`
|
|
141342
|
+
);
|
|
141343
|
+
} else {
|
|
141344
|
+
process.stdout.write(output);
|
|
141345
|
+
if (!output.endsWith("\n")) process.stdout.write("\n");
|
|
141346
|
+
}
|
|
141347
|
+
const durationMs = Date.now() - startTime;
|
|
141348
|
+
if (outputFile) {
|
|
141349
|
+
process.stderr.write(`[nexus] Export completed in ${durationMs}ms
|
|
141350
|
+
`);
|
|
141351
|
+
}
|
|
141352
|
+
} catch (err) {
|
|
141353
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
141354
|
+
process.stderr.write(`[nexus] Error: ${msg}
|
|
141355
|
+
`);
|
|
141356
|
+
process.exitCode = 1;
|
|
141357
|
+
}
|
|
141358
|
+
});
|
|
140880
141359
|
nexus.command("diff").description(
|
|
140881
141360
|
"Compare NEXUS index state between two git commits \u2014 shows new/removed relations and broken call chains"
|
|
140882
141361
|
).option("--before <sha>", 'Git SHA or ref for the "before" snapshot (default: HEAD~1)').option("--after <sha>", 'Git SHA or ref for the "after" snapshot (default: HEAD)', "HEAD").option("--path <dir>", "Repository directory to analyze (default: cwd)").option("--json", "Output result as JSON (LAFS envelope format)").option("--project-id <id>", "Override the project ID (default: auto-detected from path)").action(async (opts) => {
|