@cleocode/cleo 2026.4.50 → 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 CHANGED
@@ -9071,12 +9071,13 @@ __export(brain_schema_exports, {
9071
9071
  brainPageEdges: () => brainPageEdges,
9072
9072
  brainPageNodes: () => brainPageNodes,
9073
9073
  brainPatterns: () => brainPatterns,
9074
+ brainPlasticityEvents: () => brainPlasticityEvents,
9074
9075
  brainRetrievalLog: () => brainRetrievalLog,
9075
9076
  brainSchemaMeta: () => brainSchemaMeta,
9076
9077
  brainStickyNotes: () => brainStickyNotes
9077
9078
  });
9078
9079
  import { sql as sql2 } from "drizzle-orm";
9079
- var BRAIN_MEMORY_TIERS, BRAIN_COGNITIVE_TYPES, BRAIN_SOURCE_CONFIDENCE, BRAIN_DECISION_TYPES, BRAIN_CONFIDENCE_LEVELS, BRAIN_OUTCOME_TYPES, BRAIN_PATTERN_TYPES, BRAIN_IMPACT_LEVELS, BRAIN_LINK_TYPES, BRAIN_OBSERVATION_TYPES2, BRAIN_OBSERVATION_SOURCE_TYPES, BRAIN_MEMORY_TYPES, BRAIN_STICKY_STATUSES, BRAIN_STICKY_COLORS, BRAIN_STICKY_PRIORITIES, brainDecisions, brainPatterns, brainLearnings, brainObservations, brainStickyNotes, brainMemoryLinks, brainSchemaMeta, BRAIN_NODE_TYPES, BRAIN_EDGE_TYPES, brainPageNodes, brainPageEdges, brainRetrievalLog;
9080
+ var BRAIN_MEMORY_TIERS, BRAIN_COGNITIVE_TYPES, BRAIN_SOURCE_CONFIDENCE, BRAIN_DECISION_TYPES, BRAIN_CONFIDENCE_LEVELS, BRAIN_OUTCOME_TYPES, BRAIN_PATTERN_TYPES, BRAIN_IMPACT_LEVELS, BRAIN_LINK_TYPES, BRAIN_OBSERVATION_TYPES2, BRAIN_OBSERVATION_SOURCE_TYPES, BRAIN_MEMORY_TYPES, BRAIN_STICKY_STATUSES, BRAIN_STICKY_COLORS, BRAIN_STICKY_PRIORITIES, brainDecisions, brainPatterns, brainLearnings, brainObservations, brainStickyNotes, brainMemoryLinks, brainSchemaMeta, BRAIN_NODE_TYPES, BRAIN_EDGE_TYPES, brainPageNodes, brainPageEdges, brainRetrievalLog, brainPlasticityEvents;
9080
9081
  var init_brain_schema = __esm({
9081
9082
  "packages/core/src/store/brain-schema.ts"() {
9082
9083
  "use strict";
@@ -9505,8 +9506,11 @@ var init_brain_schema = __esm({
9505
9506
  // Graph bridging (memory ↔ code)
9506
9507
  "references",
9507
9508
  // observation → references → symbol
9508
- "modified_by"
9509
+ "modified_by",
9509
9510
  // file → modified_by → session
9511
+ // Plasticity (Hebbian + STDP co-retrieval)
9512
+ "co_retrieved"
9513
+ // A → co_retrieved → B (Hebbian: frequently retrieved together)
9510
9514
  ];
9511
9515
  brainPageNodes = sqliteTable(
9512
9516
  "brain_page_nodes",
@@ -9596,11 +9600,45 @@ var init_brain_schema = __esm({
9596
9600
  source: text("source").notNull(),
9597
9601
  /** Estimated tokens consumed by this retrieval. */
9598
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"),
9599
9605
  createdAt: text("created_at").notNull().default(sql2`(datetime('now'))`)
9600
9606
  },
9601
9607
  (table) => [
9602
9608
  index("idx_retrieval_log_created").on(table.createdAt),
9603
- 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)
9611
+ ]
9612
+ );
9613
+ brainPlasticityEvents = sqliteTable(
9614
+ "brain_plasticity_events",
9615
+ {
9616
+ id: integer("id").primaryKey({ autoIncrement: true }),
9617
+ /** from_id of the affected brain_page_edges row. */
9618
+ sourceNode: text("source_node").notNull(),
9619
+ /** to_id of the affected brain_page_edges row. */
9620
+ targetNode: text("target_node").notNull(),
9621
+ /**
9622
+ * Signed weight delta applied to the edge.
9623
+ * Positive = potentiation (LTP), negative = depression (LTD).
9624
+ */
9625
+ deltaW: real("delta_w").notNull(),
9626
+ /**
9627
+ * STDP event kind: `ltp` (Long-Term Potentiation) or `ltd` (Long-Term
9628
+ * Depression).
9629
+ */
9630
+ kind: text("kind", { enum: ["ltp", "ltd"] }).notNull(),
9631
+ /** ISO 8601 timestamp when this event was applied. */
9632
+ timestamp: text("timestamp").notNull().default(sql2`(datetime('now'))`),
9633
+ /** Session ID that triggered the STDP pass, if available. */
9634
+ sessionId: text("session_id")
9635
+ },
9636
+ (table) => [
9637
+ index("idx_plasticity_source").on(table.sourceNode),
9638
+ index("idx_plasticity_target").on(table.targetNode),
9639
+ index("idx_plasticity_timestamp").on(table.timestamp),
9640
+ index("idx_plasticity_session").on(table.sessionId),
9641
+ index("idx_plasticity_kind").on(table.kind)
9604
9642
  ]
9605
9643
  );
9606
9644
  }
@@ -14917,7 +14955,7 @@ function clearEmbeddingProvider() {
14917
14955
  currentProvider = null;
14918
14956
  }
14919
14957
  async function embedText(text3) {
14920
- if (!currentProvider || !currentProvider.isAvailable()) return null;
14958
+ if (!currentProvider?.isAvailable()) return null;
14921
14959
  return currentProvider.embed(text3);
14922
14960
  }
14923
14961
  function isEmbeddingAvailable() {
@@ -15014,6 +15052,14 @@ function runBrainMigrations(nativeDb, db) {
15014
15052
  );
15015
15053
  }
15016
15054
  ensureColumns(nativeDb, "brain_observations", [{ name: "agent", ddl: "text" }], "brain");
15055
+ if (tableExists(nativeDb, "brain_page_edges")) {
15056
+ nativeDb.prepare(
15057
+ `UPDATE brain_page_edges
15058
+ SET edge_type = 'co_retrieved'
15059
+ WHERE edge_type = 'relates_to'
15060
+ AND provenance LIKE 'consolidation:%'`
15061
+ ).run();
15062
+ }
15017
15063
  }
15018
15064
  function loadBrainVecExtension(nativeDb) {
15019
15065
  try {
@@ -22780,7 +22826,7 @@ function parseIdPrefix(id) {
22780
22826
  return null;
22781
22827
  }
22782
22828
  async function searchSimilar(query, projectRoot, limit) {
22783
- if (!query || !query.trim()) return [];
22829
+ if (!query?.trim()) return [];
22784
22830
  if (!isEmbeddingAvailable()) return [];
22785
22831
  const maxResults = limit ?? 10;
22786
22832
  const queryVector = await embedText(query);
@@ -24415,6 +24461,160 @@ var init_anthropic_key_resolver = __esm({
24415
24461
  }
24416
24462
  });
24417
24463
 
24464
+ // packages/core/src/memory/decision-cross-link.ts
24465
+ function extractReferencedSymbols(text3) {
24466
+ const seen = /* @__PURE__ */ new Set();
24467
+ const refs = [];
24468
+ for (const match of text3.matchAll(FILE_PATH_RE)) {
24469
+ const raw = match[1];
24470
+ if (!raw) continue;
24471
+ const nodeId = `file:${raw}`;
24472
+ if (seen.has(nodeId)) continue;
24473
+ seen.add(nodeId);
24474
+ refs.push({ raw, nodeId, nodeType: "file", label: raw });
24475
+ }
24476
+ for (const match of text3.matchAll(SYMBOL_RE)) {
24477
+ const raw = match[0];
24478
+ if (!raw || raw.length < 4) continue;
24479
+ if (SYMBOL_STOP_WORDS.has(raw.toLowerCase())) continue;
24480
+ const nodeId = `symbol:${raw}`;
24481
+ if (seen.has(nodeId)) continue;
24482
+ seen.add(nodeId);
24483
+ refs.push({ raw, nodeId, nodeType: "symbol", label: raw });
24484
+ }
24485
+ return refs;
24486
+ }
24487
+ async function linkDecisionToTargets(projectRoot, decisionId, refs) {
24488
+ const fromId = `decision:${decisionId}`;
24489
+ const writes = refs.map(async (ref) => {
24490
+ await upsertGraphNode(
24491
+ projectRoot,
24492
+ ref.nodeId,
24493
+ ref.nodeType,
24494
+ ref.label,
24495
+ 0.5,
24496
+ // placeholder quality until nexus indexes it
24497
+ ref.raw
24498
+ );
24499
+ await addGraphEdge(
24500
+ projectRoot,
24501
+ fromId,
24502
+ ref.nodeId,
24503
+ "applies_to",
24504
+ 1,
24505
+ "auto:decision-cross-link"
24506
+ );
24507
+ });
24508
+ await Promise.allSettled(writes);
24509
+ }
24510
+ async function autoCrossLinkDecision(projectRoot, decisionId, decisionText, rationale) {
24511
+ try {
24512
+ const combined = `${decisionText} ${rationale}`;
24513
+ const refs = extractReferencedSymbols(combined);
24514
+ if (refs.length === 0) return;
24515
+ await linkDecisionToTargets(projectRoot, decisionId, refs);
24516
+ } catch {
24517
+ }
24518
+ }
24519
+ var FILE_PATH_RE, SYMBOL_RE, SYMBOL_STOP_WORDS;
24520
+ var init_decision_cross_link = __esm({
24521
+ "packages/core/src/memory/decision-cross-link.ts"() {
24522
+ "use strict";
24523
+ init_graph_auto_populate();
24524
+ FILE_PATH_RE = /(?:^|[\s`"'([\]{,])((\/[\w.\-/]+|[\w.-]+(?:\/[\w.-]+)+)\.(ts|tsx|js|jsx|rs|json))(?=$|[\s`"')[\]{,])/gm;
24525
+ SYMBOL_RE = /(?<![`"'/\w.])(?:[A-Z][a-zA-Z0-9]{2,}|[a-z][a-zA-Z0-9]*(?:[A-Z][a-zA-Z0-9]*)+|[a-z][a-z0-9]*(?:_[a-z][a-z0-9]*){2,})(?![`"'/\w])/g;
24526
+ SYMBOL_STOP_WORDS = /* @__PURE__ */ new Set([
24527
+ "this",
24528
+ "that",
24529
+ "with",
24530
+ "from",
24531
+ "into",
24532
+ "when",
24533
+ "then",
24534
+ "also",
24535
+ "both",
24536
+ "each",
24537
+ "such",
24538
+ "over",
24539
+ "after",
24540
+ "before",
24541
+ "always",
24542
+ "never",
24543
+ "should",
24544
+ "must",
24545
+ "will",
24546
+ "would",
24547
+ "could",
24548
+ "have",
24549
+ "been",
24550
+ "there",
24551
+ "their",
24552
+ "they",
24553
+ "them",
24554
+ "these",
24555
+ "those",
24556
+ "some",
24557
+ "only",
24558
+ "just",
24559
+ "more",
24560
+ "most",
24561
+ "many",
24562
+ "much",
24563
+ "well",
24564
+ "very",
24565
+ "here",
24566
+ "where",
24567
+ "which",
24568
+ "what",
24569
+ "why",
24570
+ "how",
24571
+ "the",
24572
+ "and",
24573
+ "but",
24574
+ "for",
24575
+ "not",
24576
+ "are",
24577
+ "was",
24578
+ "were",
24579
+ "has",
24580
+ "had",
24581
+ "its",
24582
+ "the",
24583
+ "data",
24584
+ "true",
24585
+ "false",
24586
+ "null",
24587
+ "none",
24588
+ "type",
24589
+ "test",
24590
+ "spec",
24591
+ "todo",
24592
+ "fixme",
24593
+ "note",
24594
+ "example",
24595
+ "index",
24596
+ "config",
24597
+ "error",
24598
+ "value",
24599
+ "input",
24600
+ "output",
24601
+ "result",
24602
+ "return",
24603
+ "default",
24604
+ "source",
24605
+ "target",
24606
+ "import",
24607
+ "export",
24608
+ "class",
24609
+ "interface",
24610
+ "function",
24611
+ "const",
24612
+ "async",
24613
+ "await"
24614
+ ]);
24615
+ }
24616
+ });
24617
+
24418
24618
  // packages/core/src/memory/decisions.ts
24419
24619
  var decisions_exports = {};
24420
24620
  __export(decisions_exports, {
@@ -24553,6 +24753,8 @@ async function storeDecision(projectRoot, params) {
24553
24753
  "auto:store-decision"
24554
24754
  );
24555
24755
  }
24756
+ autoCrossLinkDecision(projectRoot, saved.id, saved.decision, saved.rationale).catch(() => {
24757
+ });
24556
24758
  } catch {
24557
24759
  }
24558
24760
  detectSupersession(projectRoot, {
@@ -24621,6 +24823,7 @@ var init_decisions2 = __esm({
24621
24823
  init_brain_accessor();
24622
24824
  init_cross_db_cleanup();
24623
24825
  init_sqlite2();
24826
+ init_decision_cross_link();
24624
24827
  init_graph_auto_populate();
24625
24828
  init_quality_scoring();
24626
24829
  init_temporal_supersession();
@@ -45691,6 +45894,30 @@ var init_auto_extract = __esm({
45691
45894
  }
45692
45895
  });
45693
45896
 
45897
+ // packages/core/src/memory/edge-types.ts
45898
+ var EDGE_TYPES;
45899
+ var init_edge_types = __esm({
45900
+ "packages/core/src/memory/edge-types.ts"() {
45901
+ "use strict";
45902
+ EDGE_TYPES = {
45903
+ // Plasticity (Hebbian / STDP co-retrieval)
45904
+ CO_RETRIEVED: "co_retrieved",
45905
+ // Temporal supersession
45906
+ SUPERSEDES: "supersedes",
45907
+ // Task / decision / pattern → target context
45908
+ APPLIES_TO: "applies_to",
45909
+ // Provenance
45910
+ DERIVED_FROM: "derived_from",
45911
+ // Observation → symbol/file impact
45912
+ AFFECTS: "affects",
45913
+ // Observation → symbol name mention
45914
+ MENTIONS: "mentions",
45915
+ // Observation → symbol/file structural link
45916
+ DOCUMENTS: "documents"
45917
+ };
45918
+ }
45919
+ });
45920
+
45694
45921
  // packages/core/src/memory/brain-consolidator.ts
45695
45922
  var brain_consolidator_exports = {};
45696
45923
  __export(brain_consolidator_exports, {
@@ -45924,7 +46151,7 @@ function extractSymbolCandidates(text3) {
45924
46151
  const syms = /* @__PURE__ */ new Set();
45925
46152
  for (const m2 of text3.matchAll(SYMBOL_PATTERN)) {
45926
46153
  const s3 = m2[1];
45927
- if (s3 && s3.length >= 4 && !SYMBOL_STOP_WORDS.has(s3.toLowerCase())) {
46154
+ if (s3 && s3.length >= 4 && !SYMBOL_STOP_WORDS2.has(s3.toLowerCase())) {
45928
46155
  syms.add(s3);
45929
46156
  }
45930
46157
  }
@@ -46233,7 +46460,7 @@ async function listCodeLinks(projectRoot, limit = 100) {
46233
46460
  }
46234
46461
  return entries;
46235
46462
  }
46236
- var FILE_PATH_PATTERN, SYMBOL_PATTERN, SYMBOL_STOP_WORDS;
46463
+ var FILE_PATH_PATTERN, SYMBOL_PATTERN, SYMBOL_STOP_WORDS2;
46237
46464
  var init_graph_memory_bridge = __esm({
46238
46465
  "packages/core/src/memory/graph-memory-bridge.ts"() {
46239
46466
  "use strict";
@@ -46243,7 +46470,7 @@ var init_graph_memory_bridge = __esm({
46243
46470
  init_typed_query();
46244
46471
  FILE_PATH_PATTERN = /(?:^|\s|['"`(])([a-zA-Z0-9_\-./]+\.(?:ts|tsx|js|jsx|rs|go|py|mjs|cjs))(?:$|\s|['"`)])/g;
46245
46472
  SYMBOL_PATTERN = /\b([a-zA-Z_][a-zA-Z0-9_]*(?:[A-Z][a-zA-Z0-9_]*)+|[a-zA-Z_]{4,}[a-zA-Z0-9_]*)\b/g;
46246
- SYMBOL_STOP_WORDS = /* @__PURE__ */ new Set([
46473
+ SYMBOL_STOP_WORDS2 = /* @__PURE__ */ new Set([
46247
46474
  "true",
46248
46475
  "false",
46249
46476
  "null",
@@ -46287,6 +46514,207 @@ var init_graph_memory_bridge = __esm({
46287
46514
  }
46288
46515
  });
46289
46516
 
46517
+ // packages/core/src/memory/brain-stdp.ts
46518
+ var brain_stdp_exports = {};
46519
+ __export(brain_stdp_exports, {
46520
+ applyStdpPlasticity: () => applyStdpPlasticity,
46521
+ getPlasticityStats: () => getPlasticityStats
46522
+ });
46523
+ async function applyStdpPlasticity(projectRoot, sessionWindowMs = 5 * 60 * 1e3) {
46524
+ const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
46525
+ await getBrainDb2(projectRoot);
46526
+ const nativeDb = getBrainNativeDb2();
46527
+ const result = {
46528
+ ltpEvents: 0,
46529
+ ltdEvents: 0,
46530
+ edgesCreated: 0,
46531
+ pairsExamined: 0
46532
+ };
46533
+ if (!nativeDb) return result;
46534
+ try {
46535
+ nativeDb.prepare("SELECT 1 FROM brain_retrieval_log LIMIT 1").get();
46536
+ } catch {
46537
+ return result;
46538
+ }
46539
+ try {
46540
+ nativeDb.prepare("SELECT 1 FROM brain_plasticity_events LIMIT 1").get();
46541
+ } catch {
46542
+ return result;
46543
+ }
46544
+ const now2 = Date.now();
46545
+ const cutoffMs = now2 - sessionWindowMs;
46546
+ const cutoffIso = new Date(cutoffMs).toISOString().replace("T", " ").slice(0, 19);
46547
+ const nowIso = new Date(now2).toISOString().replace("T", " ").slice(0, 19);
46548
+ let logRows = [];
46549
+ try {
46550
+ logRows = typedAll(
46551
+ nativeDb.prepare(
46552
+ `SELECT id, entry_ids, created_at, retrieval_order, delta_ms
46553
+ FROM brain_retrieval_log
46554
+ WHERE created_at >= ?
46555
+ ORDER BY created_at ASC, id ASC
46556
+ LIMIT 2000`
46557
+ ),
46558
+ cutoffIso
46559
+ );
46560
+ } catch {
46561
+ return result;
46562
+ }
46563
+ if (logRows.length === 0) return result;
46564
+ const spikes = [];
46565
+ let globalOrder = 0;
46566
+ for (const row of logRows) {
46567
+ let ids;
46568
+ try {
46569
+ ids = JSON.parse(row.entry_ids);
46570
+ } catch {
46571
+ continue;
46572
+ }
46573
+ const rowTime = (/* @__PURE__ */ new Date(row.created_at.replace(" ", "T") + "Z")).getTime();
46574
+ for (const rawId of ids) {
46575
+ const entryId = rawId.includes(":") ? rawId : `observation:${rawId}`;
46576
+ spikes.push({
46577
+ entryId,
46578
+ rowId: row.id,
46579
+ retrievedAt: rowTime,
46580
+ order: row.retrieval_order ?? globalOrder
46581
+ });
46582
+ globalOrder++;
46583
+ }
46584
+ }
46585
+ spikes.sort((a, b2) => a.retrievedAt - b2.retrievedAt || a.order - b2.order);
46586
+ const prepareGetEdge = nativeDb.prepare(
46587
+ `SELECT weight FROM brain_page_edges
46588
+ WHERE from_id = ? AND to_id = ? AND edge_type = 'co_retrieved'`
46589
+ );
46590
+ const prepareUpdateEdge = nativeDb.prepare(
46591
+ `UPDATE brain_page_edges
46592
+ SET weight = MAX(?, MIN(?, weight + ?))
46593
+ WHERE from_id = ? AND to_id = ? AND edge_type = 'co_retrieved'`
46594
+ );
46595
+ const prepareInsertEdge = nativeDb.prepare(
46596
+ `INSERT OR IGNORE INTO brain_page_edges
46597
+ (from_id, to_id, edge_type, weight, provenance, created_at)
46598
+ VALUES (?, ?, 'co_retrieved', ?, 'plasticity:stdp-ltp', ?)`
46599
+ );
46600
+ const prepareLogEvent = nativeDb.prepare(
46601
+ `INSERT INTO brain_plasticity_events
46602
+ (source_node, target_node, delta_w, kind, timestamp)
46603
+ VALUES (?, ?, ?, ?, ?)`
46604
+ );
46605
+ for (let i = 0; i < spikes.length; i++) {
46606
+ const spikeA = spikes[i];
46607
+ for (let j2 = i + 1; j2 < spikes.length; j2++) {
46608
+ const spikeB = spikes[j2];
46609
+ const deltaT = spikeB.retrievedAt - spikeA.retrievedAt;
46610
+ if (deltaT > sessionWindowMs) break;
46611
+ if (spikeA.entryId === spikeB.entryId) continue;
46612
+ result.pairsExamined++;
46613
+ const deltaW = A_PRE * Math.exp(-deltaT / TAU_PRE_MS);
46614
+ if (deltaW < 1e-6) continue;
46615
+ const existingEdge = prepareGetEdge.get(spikeA.entryId, spikeB.entryId);
46616
+ try {
46617
+ if (existingEdge !== void 0) {
46618
+ prepareUpdateEdge.run(WEIGHT_MIN, WEIGHT_MAX, deltaW, spikeA.entryId, spikeB.entryId);
46619
+ } else {
46620
+ const initialWeight = Math.min(WEIGHT_MAX, deltaW);
46621
+ prepareInsertEdge.run(spikeA.entryId, spikeB.entryId, initialWeight, nowIso);
46622
+ result.edgesCreated++;
46623
+ }
46624
+ prepareLogEvent.run(spikeA.entryId, spikeB.entryId, deltaW, "ltp", nowIso);
46625
+ result.ltpEvents++;
46626
+ } catch {
46627
+ }
46628
+ const deltaWNeg = -(A_POST * Math.exp(-deltaT / TAU_POST_MS));
46629
+ const existingReverseEdge = prepareGetEdge.get(spikeB.entryId, spikeA.entryId);
46630
+ if (existingReverseEdge !== void 0 && Math.abs(deltaWNeg) >= 1e-6) {
46631
+ try {
46632
+ prepareUpdateEdge.run(WEIGHT_MIN, WEIGHT_MAX, deltaWNeg, spikeB.entryId, spikeA.entryId);
46633
+ prepareLogEvent.run(spikeB.entryId, spikeA.entryId, deltaWNeg, "ltd", nowIso);
46634
+ result.ltdEvents++;
46635
+ } catch {
46636
+ }
46637
+ }
46638
+ }
46639
+ }
46640
+ return result;
46641
+ }
46642
+ async function getPlasticityStats(projectRoot, limit = 20) {
46643
+ const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
46644
+ await getBrainDb2(projectRoot);
46645
+ const nativeDb = getBrainNativeDb2();
46646
+ const empty = {
46647
+ totalEvents: 0,
46648
+ ltpCount: 0,
46649
+ ltdCount: 0,
46650
+ netDeltaW: 0,
46651
+ lastEventAt: null,
46652
+ recentEvents: []
46653
+ };
46654
+ if (!nativeDb) return empty;
46655
+ try {
46656
+ nativeDb.prepare("SELECT 1 FROM brain_plasticity_events LIMIT 1").get();
46657
+ } catch {
46658
+ return empty;
46659
+ }
46660
+ let agg;
46661
+ try {
46662
+ agg = nativeDb.prepare(
46663
+ `SELECT
46664
+ COUNT(*) AS total,
46665
+ SUM(CASE WHEN kind = 'ltp' THEN 1 ELSE 0 END) AS ltp_count,
46666
+ SUM(CASE WHEN kind = 'ltd' THEN 1 ELSE 0 END) AS ltd_count,
46667
+ SUM(delta_w) AS net_delta_w,
46668
+ MAX(timestamp) AS last_event_at
46669
+ FROM brain_plasticity_events`
46670
+ ).get();
46671
+ } catch {
46672
+ return empty;
46673
+ }
46674
+ let recentRows = [];
46675
+ try {
46676
+ recentRows = typedAll(
46677
+ nativeDb.prepare(
46678
+ `SELECT id, source_node, target_node, delta_w, kind, timestamp, session_id
46679
+ FROM brain_plasticity_events
46680
+ ORDER BY timestamp DESC, id DESC
46681
+ LIMIT ?`
46682
+ ),
46683
+ limit
46684
+ );
46685
+ } catch {
46686
+ }
46687
+ return {
46688
+ totalEvents: agg?.total ?? 0,
46689
+ ltpCount: agg?.ltp_count ?? 0,
46690
+ ltdCount: agg?.ltd_count ?? 0,
46691
+ netDeltaW: agg?.net_delta_w ?? 0,
46692
+ lastEventAt: agg?.last_event_at ?? null,
46693
+ recentEvents: recentRows.map((r) => ({
46694
+ id: r.id,
46695
+ sourceNode: r.source_node,
46696
+ targetNode: r.target_node,
46697
+ deltaW: r.delta_w,
46698
+ kind: r.kind,
46699
+ timestamp: r.timestamp,
46700
+ sessionId: r.session_id
46701
+ }))
46702
+ };
46703
+ }
46704
+ var TAU_PRE_MS, TAU_POST_MS, A_PRE, A_POST, WEIGHT_MIN, WEIGHT_MAX;
46705
+ var init_brain_stdp = __esm({
46706
+ "packages/core/src/memory/brain-stdp.ts"() {
46707
+ "use strict";
46708
+ init_typed_query();
46709
+ TAU_PRE_MS = 2e4;
46710
+ TAU_POST_MS = 2e4;
46711
+ A_PRE = 0.05;
46712
+ A_POST = 0.06;
46713
+ WEIGHT_MIN = 0;
46714
+ WEIGHT_MAX = 1;
46715
+ }
46716
+ });
46717
+
46290
46718
  // packages/core/src/memory/brain-lifecycle.ts
46291
46719
  var brain_lifecycle_exports = {};
46292
46720
  __export(brain_lifecycle_exports, {
@@ -46592,6 +47020,13 @@ async function runConsolidation(projectRoot) {
46592
47020
  } catch (err) {
46593
47021
  console.warn("[consolidation] Step 8 graph memory bridge failed:", err);
46594
47022
  }
47023
+ try {
47024
+ const { applyStdpPlasticity: applyStdpPlasticity2 } = await Promise.resolve().then(() => (init_brain_stdp(), brain_stdp_exports));
47025
+ const stdpResult = await applyStdpPlasticity2(projectRoot);
47026
+ result.stdpPlasticity = stdpResult;
47027
+ } catch (err) {
47028
+ console.warn("[consolidation] Step 9 STDP plasticity failed:", err);
47029
+ }
46595
47030
  return result;
46596
47031
  }
46597
47032
  async function deduplicateByEmbedding(projectRoot) {
@@ -46753,16 +47188,16 @@ async function strengthenCoRetrievedEdges(projectRoot) {
46753
47188
  const updateStmt = nativeDb.prepare(`
46754
47189
  UPDATE brain_page_edges
46755
47190
  SET weight = MIN(1.0, weight + 0.1)
46756
- WHERE from_id = ? AND to_id = ? AND edge_type = 'relates_to'
47191
+ WHERE from_id = ? AND to_id = ? AND edge_type = ?
46757
47192
  `);
46758
- const updateResult = updateStmt.run(nodeFrom, nodeTo);
47193
+ const updateResult = updateStmt.run(nodeFrom, nodeTo, EDGE_TYPES.CO_RETRIEVED);
46759
47194
  const changes = typeof updateResult.changes === "number" ? updateResult.changes : 0;
46760
47195
  if (changes === 0) {
46761
47196
  nativeDb.prepare(`
46762
47197
  INSERT OR IGNORE INTO brain_page_edges
46763
47198
  (from_id, to_id, edge_type, weight, provenance, created_at)
46764
- VALUES (?, ?, 'relates_to', 0.3, 'consolidation:co-retrieval', ?)
46765
- `).run(nodeFrom, nodeTo, now2);
47199
+ VALUES (?, ?, ?, 0.3, 'consolidation:co-retrieval', ?)
47200
+ `).run(nodeFrom, nodeTo, EDGE_TYPES.CO_RETRIEVED, now2);
46766
47201
  }
46767
47202
  strengthened++;
46768
47203
  } catch {
@@ -46776,6 +47211,7 @@ var init_brain_lifecycle = __esm({
46776
47211
  "use strict";
46777
47212
  init_brain_accessor();
46778
47213
  init_typed_query();
47214
+ init_edge_types();
46779
47215
  STOP_WORDS3 = /* @__PURE__ */ new Set([
46780
47216
  "the",
46781
47217
  "a",
@@ -49937,7 +50373,7 @@ async function computeLastSession(projectRoot, scopeFilter) {
49937
50373
  const accessor = await getAccessor(projectRoot);
49938
50374
  const allSessions = await accessor.loadSessions();
49939
50375
  const session = allSessions.find((s3) => s3.id === sessionId);
49940
- if (!session || !session.endedAt) return null;
50376
+ if (!session?.endedAt) return null;
49941
50377
  let duration3 = 0;
49942
50378
  if (session.startedAt) {
49943
50379
  duration3 = Math.round(
@@ -51124,10 +51560,17 @@ async function searchBrainCompact(projectRoot, params) {
51124
51560
  setImmediate(() => {
51125
51561
  incrementCitationCounts(projectRoot, returnedIds).catch(() => {
51126
51562
  });
51127
- logRetrieval(projectRoot, query, returnedIds, "find-rrf", results2.length * 50).catch(
51128
- () => {
51129
- }
51130
- );
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
+ });
51131
51574
  });
51132
51575
  }
51133
51576
  return { results: results2, total: results2.length, tokensEstimated: results2.length * 50 };
@@ -51190,7 +51633,16 @@ async function searchBrainCompact(projectRoot, params) {
51190
51633
  setImmediate(() => {
51191
51634
  incrementCitationCounts(projectRoot, returnedIds).catch(() => {
51192
51635
  });
51193
- logRetrieval(projectRoot, query, returnedIds, "find", results.length * 50).catch(() => {
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(() => {
51194
51646
  });
51195
51647
  });
51196
51648
  }
@@ -51378,13 +51830,16 @@ async function fetchBrainEntries(projectRoot, params) {
51378
51830
  setImmediate(() => {
51379
51831
  incrementCitationCounts(projectRoot, fetchedIds).catch(() => {
51380
51832
  });
51381
- logRetrieval(
51382
- projectRoot,
51383
- fetchedIds.join(","),
51384
- fetchedIds,
51385
- "fetch",
51386
- results.length * 500
51387
- ).catch(() => {
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(() => {
51388
51843
  });
51389
51844
  });
51390
51845
  }
@@ -51802,6 +52257,15 @@ async function retrieveWithBudget(projectRoot, query, tokenBudget = 500, options
51802
52257
  excluded
51803
52258
  };
51804
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
+ }
51805
52269
  async function incrementCitationCounts(projectRoot, ids) {
51806
52270
  if (ids.length === 0) return;
51807
52271
  const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
@@ -51828,13 +52292,13 @@ async function incrementCitationCounts(projectRoot, ids) {
51828
52292
  }
51829
52293
  }
51830
52294
  }
51831
- async function logRetrieval(projectRoot, query, entryIds, source, tokensUsed) {
52295
+ async function logRetrieval(projectRoot, query, entryIds, source, tokensUsed, sessionId) {
51832
52296
  if (entryIds.length === 0) return;
51833
52297
  const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
51834
52298
  await getBrainDb2(projectRoot);
51835
52299
  const nativeDb = getBrainNativeDb2();
51836
52300
  if (!nativeDb) return;
51837
- 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')))";
51838
52302
  try {
51839
52303
  nativeDb.prepare(createSql).run();
51840
52304
  } catch {
@@ -51842,8 +52306,15 @@ async function logRetrieval(projectRoot, query, entryIds, source, tokensUsed) {
51842
52306
  }
51843
52307
  try {
51844
52308
  nativeDb.prepare(
51845
- "INSERT INTO brain_retrieval_log (query, entry_ids, entry_count, source, tokens_used) VALUES (?, ?, ?, ?, ?)"
51846
- ).run(query, entryIds.join(","), entryIds.length, source, tokensUsed ?? null);
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
+ );
51847
52318
  } catch {
51848
52319
  }
51849
52320
  }
@@ -58418,7 +58889,7 @@ function prepareSpawnMulti(skillNames, tokenValues, cwd) {
58418
58889
  const skillName = skillNames[i];
58419
58890
  const isPrimary = i === 0;
58420
58891
  const skill = findSkill(skillName, cwd);
58421
- if (!skill || !skill.content) {
58892
+ if (!skill?.content) {
58422
58893
  continue;
58423
58894
  }
58424
58895
  let content = isPrimary ? skill.content : loadProgressive(skill.content);
@@ -60729,6 +61200,7 @@ var init_extraction_gate = __esm({
60729
61200
  // packages/core/src/memory/index.ts
60730
61201
  var memory_exports = {};
60731
61202
  __export(memory_exports, {
61203
+ EDGE_TYPES: () => EDGE_TYPES,
60732
61204
  RRF_K: () => RRF_K,
60733
61205
  addResearch: () => addResearch,
60734
61206
  appendExtendedManifest: () => appendExtendedManifest,
@@ -61412,6 +61884,7 @@ var init_memory = __esm({
61412
61884
  init_brain_retrieval();
61413
61885
  init_brain_search();
61414
61886
  init_decisions2();
61887
+ init_edge_types();
61415
61888
  init_extraction_gate();
61416
61889
  init_learnings();
61417
61890
  init_patterns();
@@ -71602,6 +72075,7 @@ var init_remote = __esm({
71602
72075
  // packages/core/src/research/index.ts
71603
72076
  var research_exports = {};
71604
72077
  __export(research_exports, {
72078
+ EDGE_TYPES: () => EDGE_TYPES,
71605
72079
  RRF_K: () => RRF_K,
71606
72080
  addResearch: () => addResearch,
71607
72081
  appendExtendedManifest: () => appendExtendedManifest,
@@ -73931,7 +74405,7 @@ async function injectProtocol(skillContent, taskId, tokenValues, cwd, tier) {
73931
74405
  }
73932
74406
  async function orchestratorSpawnSkill(taskId, skillName, tokenValues, cwd, tier) {
73933
74407
  const skill = findSkill(skillName, cwd);
73934
- if (!skill || !skill.content) {
74408
+ if (!skill?.content) {
73935
74409
  throw new CleoError(4 /* NOT_FOUND */, `Skill not found: ${skillName}`, {
73936
74410
  fix: `Check skills directory for ${skillName}/SKILL.md`
73937
74411
  });
@@ -74501,7 +74975,7 @@ async function buildPrompt(taskId, templateName = "TASK-EXECUTOR", cwd, tier) {
74501
74975
  throw new CleoError(4 /* NOT_FOUND */, `Task ${taskId} not found`);
74502
74976
  }
74503
74977
  const skill = findSkill(templateName, cwd);
74504
- if (!skill || !skill.content) {
74978
+ if (!skill?.content) {
74505
74979
  const { canonical } = mapSkillName(templateName);
74506
74980
  throw new CleoError(4 /* NOT_FOUND */, `Skill template ${templateName} not found`, {
74507
74981
  fix: `Expected at skills/${canonical}/SKILL.md`
@@ -77296,7 +77770,7 @@ async function analyzeArchive(opts, accessor) {
77296
77770
  const acc = accessor ?? await getAccessor(opts.cwd);
77297
77771
  const data = await acc.loadArchive();
77298
77772
  const reportType = opts.report ?? "summary";
77299
- if (!data || !data.archivedTasks?.length) {
77773
+ if (!data?.archivedTasks?.length) {
77300
77774
  return {
77301
77775
  report: reportType,
77302
77776
  filters: null,
@@ -85357,16 +85831,12 @@ function allEpicChildrenVerified(epicId, tasks2) {
85357
85831
  if (children.length === 0) return false;
85358
85832
  const incomplete = children.filter((t) => t.status !== "done");
85359
85833
  if (incomplete.length > 0) return false;
85360
- const unverified = children.filter(
85361
- (t) => t.status === "done" && (!t.verification || !t.verification.passed)
85362
- );
85834
+ const unverified = children.filter((t) => t.status === "done" && !t.verification?.passed);
85363
85835
  return unverified.length === 0;
85364
85836
  }
85365
85837
  function allSiblingsVerified(parentId, tasks2) {
85366
85838
  const siblings = tasks2.filter((t) => t.parentId === parentId);
85367
- const unverifiedDone = siblings.filter(
85368
- (t) => t.status === "done" && (!t.verification || !t.verification.passed)
85369
- );
85839
+ const unverifiedDone = siblings.filter((t) => t.status === "done" && !t.verification?.passed);
85370
85840
  const incomplete = siblings.filter(
85371
85841
  (t) => t.status === "pending" || t.status === "active" || t.status === "blocked"
85372
85842
  );
@@ -86772,7 +87242,7 @@ var init_init = __esm({
86772
87242
  // packages/core/src/sessions/context-alert.ts
86773
87243
  import { existsSync as existsSync109, readFileSync as readFileSync80, writeFileSync as writeFileSync20 } from "node:fs";
86774
87244
  import { join as join109 } from "node:path";
86775
- function getCurrentSessionId(cwd) {
87245
+ function getCurrentSessionId2(cwd) {
86776
87246
  if (process.env.CLEO_SESSION) return process.env.CLEO_SESSION;
86777
87247
  const sessionFile = join109(getCleoDir(cwd), ".current-session");
86778
87248
  if (existsSync109(sessionFile)) {
@@ -86806,7 +87276,7 @@ function createCliMeta(operation, duration_ms = 0) {
86806
87276
  duration_ms,
86807
87277
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
86808
87278
  };
86809
- const sessionId = getCurrentSessionId();
87279
+ const sessionId = getCurrentSessionId2();
86810
87280
  if (sessionId) {
86811
87281
  meta3["sessionId"] = sessionId;
86812
87282
  }
@@ -90476,6 +90946,160 @@ var init_brain_backfill = __esm({
90476
90946
  }
90477
90947
  });
90478
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
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
+
90479
91103
  // packages/core/src/memory/brain-maintenance.ts
90480
91104
  async function runBrainMaintenance(projectRoot, options) {
90481
91105
  const {
@@ -90885,7 +91509,7 @@ Use --source <path> to specify a custom location.`
90885
91509
  }
90886
91510
  try {
90887
91511
  for (const row of batch) {
90888
- if (!row.learned || !row.learned.trim()) {
91512
+ if (!row.learned?.trim()) {
90889
91513
  continue;
90890
91514
  }
90891
91515
  const learnId = `CML-${row.id}`;
@@ -98265,7 +98889,7 @@ function measureDependencyDepth(taskId, taskMap, visited = /* @__PURE__ */ new S
98265
98889
  if (visited.has(taskId)) return 0;
98266
98890
  visited.add(taskId);
98267
98891
  const task = taskMap.get(taskId);
98268
- if (!task || !task.depends || task.depends.length === 0) return 0;
98892
+ if (!task?.depends || task.depends.length === 0) return 0;
98269
98893
  let maxDepth = 0;
98270
98894
  for (const depId of task.depends) {
98271
98895
  const depth = 1 + measureDependencyDepth(depId, taskMap, visited);
@@ -98619,7 +99243,7 @@ async function coreTaskUnarchive(projectRoot, taskId, params) {
98619
99243
  throw new Error(`Task '${taskId}' already exists in active tasks`);
98620
99244
  }
98621
99245
  const archive = await accessor.loadArchive();
98622
- if (!archive || !archive.archivedTasks) {
99246
+ if (!archive?.archivedTasks) {
98623
99247
  throw new Error("No archive file found");
98624
99248
  }
98625
99249
  const taskIndex = archive.archivedTasks.findIndex((t) => t.id === taskId);
@@ -112790,6 +113414,7 @@ __export(internal_exports, {
112790
113414
  analyzeDependencies: () => analyzeDependencies2,
112791
113415
  analyzeEpic: () => analyzeEpic,
112792
113416
  analyzeTaskImpact: () => analyzeTaskImpact,
113417
+ applyStdpPlasticity: () => applyStdpPlasticity,
112793
113418
  applyStrictnessPreset: () => applyStrictnessPreset,
112794
113419
  applyTemporalDecay: () => applyTemporalDecay,
112795
113420
  archiveSessions: () => archiveSessions,
@@ -113003,6 +113628,8 @@ __export(internal_exports, {
113003
113628
  ensureSqliteDb: () => ensureSqliteDb,
113004
113629
  estimateContext: () => estimateContext,
113005
113630
  executeTransfer: () => executeTransfer,
113631
+ exportBrainAsGexf: () => exportBrainAsGexf,
113632
+ exportBrainAsJson: () => exportBrainAsJson,
113006
113633
  exportSnapshot: () => exportSnapshot,
113007
113634
  exportTasks: () => exportTasks,
113008
113635
  exportTasksPackage: () => exportTasksPackage,
@@ -113072,7 +113699,7 @@ __export(internal_exports, {
113072
113699
  getConfigValue: () => getConfigValue,
113073
113700
  getContextDrift: () => getContextDrift,
113074
113701
  getCriticalPath: () => getCriticalPath,
113075
- getCurrentSessionId: () => getCurrentSessionId,
113702
+ getCurrentSessionId: () => getCurrentSessionId2,
113076
113703
  getDashboard: () => getDashboard,
113077
113704
  getDb: () => getDb,
113078
113705
  getDecisionLog: () => getDecisionLog,
@@ -113117,6 +113744,7 @@ __export(internal_exports, {
113117
113744
  getPackageRoot: () => getPackageRoot,
113118
113745
  getParallelStatus: () => getParallelStatus,
113119
113746
  getPipelineStageOrder: () => getPipelineStageOrder,
113747
+ getPlasticityStats: () => getPlasticityStats,
113120
113748
  getProjectAgentRef: () => getProjectAgentRef2,
113121
113749
  getProjectInfo: () => getProjectInfo,
113122
113750
  getProjectInfoSync: () => getProjectInfoSync,
@@ -113579,10 +114207,12 @@ var init_internal = __esm({
113579
114207
  init_stages();
113580
114208
  init_tessera_engine();
113581
114209
  init_brain_backfill();
114210
+ init_brain_export();
113582
114211
  init_brain_lifecycle();
113583
114212
  init_brain_maintenance();
113584
114213
  init_brain_purge();
113585
114214
  init_brain_retrieval();
114215
+ init_brain_stdp();
113586
114216
  init_claude_mem_migration();
113587
114217
  init_engine_compat();
113588
114218
  init_pipeline_manifest_sqlite();
@@ -135777,6 +136407,62 @@ function registerBrainCommand(program) {
135777
136407
  process.exit(1);
135778
136408
  }
135779
136409
  });
136410
+ const plasticity = brain.command("plasticity").description("STDP timing-dependent plasticity operations (T626 phase 5)");
136411
+ plasticity.command("stats").description(
136412
+ "Show recent STDP plasticity events: LTP/LTD counts, net weight delta, and most recent events."
136413
+ ).option("--limit <n>", "Maximum recent events to show (default 20)", "20").option("--json", "Output results as JSON").action(async (opts) => {
136414
+ const root = getProjectRoot();
136415
+ const isJson = !!opts.json;
136416
+ const limit = Number(opts.limit ?? "20") || 20;
136417
+ try {
136418
+ const stats2 = await getPlasticityStats(root, limit);
136419
+ if (isJson) {
136420
+ console.log(
136421
+ JSON.stringify(
136422
+ {
136423
+ success: true,
136424
+ data: stats2,
136425
+ meta: { operation: "brain.plasticity.stats", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
136426
+ },
136427
+ null,
136428
+ 2
136429
+ )
136430
+ );
136431
+ return;
136432
+ }
136433
+ console.log("\nBrain Plasticity Stats (STDP)");
136434
+ console.log("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
136435
+ console.log(` Total events: ${stats2.totalEvents}`);
136436
+ console.log(` LTP (potentiation): ${stats2.ltpCount}`);
136437
+ console.log(` LTD (depression): ${stats2.ltdCount}`);
136438
+ const sign = stats2.netDeltaW >= 0 ? "+" : "";
136439
+ console.log(` Net \u0394w: ${sign}${stats2.netDeltaW.toFixed(4)}`);
136440
+ console.log(` Last event: ${stats2.lastEventAt ?? "(none)"}`);
136441
+ if (stats2.recentEvents.length > 0) {
136442
+ console.log(`
136443
+ Recent Events (newest first, limit=${limit})`);
136444
+ for (const ev of stats2.recentEvents) {
136445
+ const evSign = ev.deltaW >= 0 ? "+" : "";
136446
+ const src = ev.sourceNode.slice(0, 30).padEnd(30);
136447
+ const tgt = ev.targetNode.slice(0, 30).padEnd(30);
136448
+ console.log(
136449
+ ` [${ev.kind.toUpperCase()}] ${src} \u2192 ${tgt} \u0394w=${evSign}${ev.deltaW.toFixed(4)} ${ev.timestamp}`
136450
+ );
136451
+ }
136452
+ } else {
136453
+ console.log("\n No plasticity events recorded yet.");
136454
+ console.log(" Run `cleo brain maintenance` or `cleo session end` to trigger STDP.");
136455
+ }
136456
+ } catch (err) {
136457
+ const message = err instanceof Error ? err.message : String(err);
136458
+ if (isJson) {
136459
+ console.log(JSON.stringify({ success: false, error: message }));
136460
+ } else {
136461
+ console.error(`Brain plasticity stats failed: ${message}`);
136462
+ }
136463
+ process.exit(1);
136464
+ }
136465
+ });
135780
136466
  brain.command("quality").description(
135781
136467
  "Show memory quality metrics: retrieval rates, top/never-retrieved entries, quality distribution, and noise ratio."
135782
136468
  ).option("--json", "Output results as JSON").action(async (opts) => {
@@ -135837,6 +136523,47 @@ function registerBrainCommand(program) {
135837
136523
  process.exit(1);
135838
136524
  }
135839
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
+ });
135840
136567
  }
135841
136568
 
135842
136569
  // packages/cleo/src/cli/commands/briefing.ts
@@ -139148,6 +139875,175 @@ var NODE_KIND_PRIORITY = {
139148
139875
  file: 40,
139149
139876
  folder: 41
139150
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
+ "<": "&lt;",
139934
+ ">": "&gt;",
139935
+ '"': "&quot;",
139936
+ "'": "&apos;",
139937
+ "&": "&amp;"
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
+ "<": "&lt;",
139985
+ ">": "&gt;",
139986
+ '"': "&quot;",
139987
+ "'": "&apos;",
139988
+ "&": "&amp;"
139989
+ };
139990
+ return map2[c];
139991
+ });
139992
+ const targetId = String(rel["targetId"]).replace(/[<>"'&]/g, (c) => {
139993
+ const map2 = {
139994
+ "<": "&lt;",
139995
+ ">": "&gt;",
139996
+ '"': "&quot;",
139997
+ "'": "&apos;",
139998
+ "&": "&amp;"
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
+ "<": "&lt;",
140031
+ ">": "&gt;",
140032
+ '"': "&quot;",
140033
+ "'": "&apos;",
140034
+ "&": "&amp;"
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
+ }
139151
140047
  function sortMatchingNodes(nodes, symbolName) {
139152
140048
  const lowerSymbol = symbolName.toLowerCase();
139153
140049
  return [...nodes].sort((a, b2) => {
@@ -140382,6 +141278,84 @@ function registerNexusCommand(program) {
140382
141278
  process.exitCode = 1;
140383
141279
  }
140384
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
+ });
140385
141359
  nexus.command("diff").description(
140386
141360
  "Compare NEXUS index state between two git commits \u2014 shows new/removed relations and broken call chains"
140387
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) => {