@cleocode/cleo 2026.4.37 → 2026.4.39

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.
Files changed (33) hide show
  1. package/dist/cli/commands/brain.d.ts.map +1 -1
  2. package/dist/cli/commands/brain.js +60 -1
  3. package/dist/cli/commands/brain.js.map +1 -1
  4. package/dist/cli/commands/memory-brain.d.ts.map +1 -1
  5. package/dist/cli/commands/memory-brain.js +38 -0
  6. package/dist/cli/commands/memory-brain.js.map +1 -1
  7. package/dist/cli/commands/nexus.d.ts.map +1 -1
  8. package/dist/cli/commands/nexus.js +75 -2
  9. package/dist/cli/commands/nexus.js.map +1 -1
  10. package/dist/cli/index.js +1309 -40
  11. package/dist/cli/index.js.map +4 -4
  12. package/dist/dispatch/domains/memory.d.ts.map +1 -1
  13. package/dist/dispatch/domains/memory.js +6 -1
  14. package/dist/dispatch/domains/memory.js.map +1 -1
  15. package/dist/dispatch/engines/memory-engine.d.ts +1 -1
  16. package/dist/dispatch/engines/memory-engine.d.ts.map +1 -1
  17. package/dist/dispatch/engines/memory-engine.js +1 -1
  18. package/dist/dispatch/engines/memory-engine.js.map +1 -1
  19. package/dist/dispatch/engines/orchestrate-engine.d.ts.map +1 -1
  20. package/dist/dispatch/engines/orchestrate-engine.js +50 -0
  21. package/dist/dispatch/engines/orchestrate-engine.js.map +1 -1
  22. package/dist/dispatch/lib/engine.d.ts +1 -1
  23. package/dist/dispatch/lib/engine.d.ts.map +1 -1
  24. package/dist/dispatch/lib/engine.js +1 -1
  25. package/dist/dispatch/lib/engine.js.map +1 -1
  26. package/dist/dispatch/registry.d.ts.map +1 -1
  27. package/dist/dispatch/registry.js +89 -0
  28. package/dist/dispatch/registry.js.map +1 -1
  29. package/dist/dispatch/types.d.ts +1 -1
  30. package/dist/dispatch/types.d.ts.map +1 -1
  31. package/dist/dispatch/types.js +1 -0
  32. package/dist/dispatch/types.js.map +1 -1
  33. package/package.json +8 -8
package/dist/cli/index.js CHANGED
@@ -22109,6 +22109,210 @@ var init_quality_scoring = __esm({
22109
22109
  }
22110
22110
  });
22111
22111
 
22112
+ // packages/core/src/store/typed-query.ts
22113
+ function typedAll(stmt, ...params) {
22114
+ return stmt.all(...params);
22115
+ }
22116
+ function typedGet(stmt, ...params) {
22117
+ return stmt.get(...params);
22118
+ }
22119
+ var init_typed_query = __esm({
22120
+ "packages/core/src/store/typed-query.ts"() {
22121
+ "use strict";
22122
+ }
22123
+ });
22124
+
22125
+ // packages/core/src/memory/temporal-supersession.ts
22126
+ function extractKeywords(text3) {
22127
+ const words = text3.toLowerCase().replace(/[^a-z0-9\s\-_]/g, " ").split(/\s+/);
22128
+ const keywords = /* @__PURE__ */ new Set();
22129
+ for (const w2 of words) {
22130
+ if (w2.length >= 4 && !STOP_WORDS.has(w2)) {
22131
+ keywords.add(w2);
22132
+ }
22133
+ }
22134
+ return keywords;
22135
+ }
22136
+ function keywordSimilarity(textA, textB) {
22137
+ const kwA = extractKeywords(textA);
22138
+ const kwB = extractKeywords(textB);
22139
+ if (kwA.size === 0 || kwB.size === 0) return { similarity: 0, shared: [] };
22140
+ const shared = [];
22141
+ for (const w2 of kwA) {
22142
+ if (kwB.has(w2)) shared.push(w2);
22143
+ }
22144
+ if (shared.length < MIN_SHARED_KEYWORDS) return { similarity: 0, shared: [] };
22145
+ const union3 = kwA.size + kwB.size - shared.length;
22146
+ const similarity = union3 > 0 ? shared.length / union3 : 0;
22147
+ return { similarity, shared };
22148
+ }
22149
+ function buildNodeId(type, entryId) {
22150
+ return `${type}:${entryId}`;
22151
+ }
22152
+ async function locateEntry(projectRoot, entryId) {
22153
+ await getBrainDb(projectRoot);
22154
+ const nativeDb = getBrainNativeDb();
22155
+ if (!nativeDb) return null;
22156
+ for (const tc of SUPERSEDABLE_TABLES) {
22157
+ const row = typedGet(
22158
+ nativeDb.prepare(`SELECT id FROM ${tc.table} WHERE id = ? LIMIT 1`),
22159
+ entryId
22160
+ );
22161
+ if (row) {
22162
+ return { tableConfig: tc, nodeId: buildNodeId(tc.type, entryId) };
22163
+ }
22164
+ }
22165
+ return null;
22166
+ }
22167
+ async function supersedeMemory(projectRoot, oldId, newId, reason) {
22168
+ if (!oldId?.trim()) throw new Error("oldId is required");
22169
+ if (!newId?.trim()) throw new Error("newId is required");
22170
+ if (!reason?.trim()) throw new Error("reason is required");
22171
+ if (oldId === newId) throw new Error("oldId and newId must be different entries");
22172
+ await getBrainDb(projectRoot);
22173
+ const nativeDb = getBrainNativeDb();
22174
+ if (!nativeDb) throw new Error("brain.db is unavailable");
22175
+ const now2 = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
22176
+ const oldLocation = await locateEntry(projectRoot, oldId);
22177
+ if (!oldLocation) throw new Error(`Entry not found: ${oldId}`);
22178
+ const newLocation = await locateEntry(projectRoot, newId);
22179
+ if (!newLocation) throw new Error(`Entry not found: ${newId}`);
22180
+ const { tableConfig: oldTc, nodeId: oldNodeId } = oldLocation;
22181
+ const { nodeId: newNodeId } = newLocation;
22182
+ try {
22183
+ nativeDb.prepare(
22184
+ `UPDATE ${oldTc.table} SET invalid_at = ?, updated_at = ? WHERE id = ? AND invalid_at IS NULL`
22185
+ ).run(now2, now2, oldId);
22186
+ } catch {
22187
+ }
22188
+ const provenanceText = reason.substring(0, 500);
22189
+ try {
22190
+ nativeDb.prepare(
22191
+ `INSERT OR IGNORE INTO brain_page_edges
22192
+ (from_id, to_id, edge_type, weight, provenance, created_at)
22193
+ VALUES (?, ?, 'supersedes', 1.0, ?, ?)`
22194
+ ).run(newNodeId, oldNodeId, provenanceText, now2);
22195
+ } catch (err) {
22196
+ const message = err instanceof Error ? err.message : String(err);
22197
+ throw new Error(`Failed to create supersedes edge: ${message}`);
22198
+ }
22199
+ return {
22200
+ success: true,
22201
+ oldId,
22202
+ newId,
22203
+ edgeType: "supersedes"
22204
+ };
22205
+ }
22206
+ async function detectSupersession(projectRoot, newEntry) {
22207
+ try {
22208
+ await getBrainDb(projectRoot);
22209
+ const nativeDb = getBrainNativeDb();
22210
+ if (!nativeDb) return [];
22211
+ const newLocation = await locateEntry(projectRoot, newEntry.id);
22212
+ if (!newLocation) return [];
22213
+ const { tableConfig } = newLocation;
22214
+ const existing = typedAll(
22215
+ nativeDb.prepare(`
22216
+ SELECT id, COALESCE(${tableConfig.textCol}, '') AS text,
22217
+ created_at, quality_score
22218
+ FROM ${tableConfig.table}
22219
+ WHERE invalid_at IS NULL
22220
+ AND id != ?
22221
+ ORDER BY created_at DESC
22222
+ LIMIT 200
22223
+ `),
22224
+ newEntry.id
22225
+ );
22226
+ if (existing.length === 0) return [];
22227
+ const candidates = [];
22228
+ for (const row of existing) {
22229
+ if (row.created_at >= newEntry.createdAt) continue;
22230
+ const { similarity, shared } = keywordSimilarity(newEntry.text, row.text);
22231
+ if (similarity >= KEYWORD_OVERLAP_THRESHOLD) {
22232
+ candidates.push({
22233
+ existingId: row.id,
22234
+ similarity,
22235
+ table: tableConfig.table,
22236
+ sharedKeywords: shared.slice(0, 10)
22237
+ });
22238
+ }
22239
+ }
22240
+ candidates.sort((a, b2) => b2.similarity - a.similarity);
22241
+ return candidates;
22242
+ } catch (err) {
22243
+ console.warn("[temporal-supersession] detectSupersession failed:", err);
22244
+ return [];
22245
+ }
22246
+ }
22247
+ var KEYWORD_OVERLAP_THRESHOLD, MIN_SHARED_KEYWORDS, STOP_WORDS, SUPERSEDABLE_TABLES;
22248
+ var init_temporal_supersession = __esm({
22249
+ "packages/core/src/memory/temporal-supersession.ts"() {
22250
+ "use strict";
22251
+ init_brain_sqlite();
22252
+ init_typed_query();
22253
+ KEYWORD_OVERLAP_THRESHOLD = 0.8;
22254
+ MIN_SHARED_KEYWORDS = 3;
22255
+ STOP_WORDS = /* @__PURE__ */ new Set([
22256
+ "the",
22257
+ "a",
22258
+ "an",
22259
+ "is",
22260
+ "are",
22261
+ "was",
22262
+ "were",
22263
+ "be",
22264
+ "been",
22265
+ "being",
22266
+ "have",
22267
+ "has",
22268
+ "had",
22269
+ "do",
22270
+ "does",
22271
+ "did",
22272
+ "will",
22273
+ "would",
22274
+ "could",
22275
+ "should",
22276
+ "may",
22277
+ "might",
22278
+ "shall",
22279
+ "can",
22280
+ "to",
22281
+ "of",
22282
+ "in",
22283
+ "for",
22284
+ "on",
22285
+ "with",
22286
+ "at",
22287
+ "by",
22288
+ "from",
22289
+ "as",
22290
+ "into",
22291
+ "through",
22292
+ "and",
22293
+ "but",
22294
+ "or",
22295
+ "nor",
22296
+ "so",
22297
+ "yet",
22298
+ "this",
22299
+ "that",
22300
+ "these",
22301
+ "those",
22302
+ "it",
22303
+ "its",
22304
+ "not",
22305
+ "no"
22306
+ ]);
22307
+ SUPERSEDABLE_TABLES = [
22308
+ { table: "brain_decisions", textCol: "decision", type: "decision" },
22309
+ { table: "brain_learnings", textCol: "insight", type: "learning" },
22310
+ { table: "brain_patterns", textCol: "pattern", type: "pattern" },
22311
+ { table: "brain_observations", textCol: "narrative", type: "observation" }
22312
+ ];
22313
+ }
22314
+ });
22315
+
22112
22316
  // packages/core/src/memory/patterns.ts
22113
22317
  var patterns_exports = {};
22114
22318
  __export(patterns_exports, {
@@ -22206,6 +22410,22 @@ async function storePattern(projectRoot, params) {
22206
22410
  { type: saved.type, impact: saved.impact ?? void 0 }
22207
22411
  ).catch(() => {
22208
22412
  });
22413
+ detectSupersession(projectRoot, {
22414
+ id: saved.id,
22415
+ text: saved.pattern + " " + saved.context,
22416
+ createdAt: saved.extractedAt ?? (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19)
22417
+ }).then((candidates) => {
22418
+ for (const candidate of candidates) {
22419
+ supersedeMemory(
22420
+ projectRoot,
22421
+ candidate.existingId,
22422
+ saved.id,
22423
+ "auto:pattern-supersedes \u2014 high overlap detected at store time"
22424
+ ).catch(() => {
22425
+ });
22426
+ }
22427
+ }).catch(() => {
22428
+ });
22209
22429
  return {
22210
22430
  ...saved,
22211
22431
  examples: JSON.parse(saved.examplesJson || "[]")
@@ -22262,6 +22482,7 @@ var init_patterns = __esm({
22262
22482
  init_brain_accessor();
22263
22483
  init_graph_auto_populate();
22264
22484
  init_quality_scoring();
22485
+ init_temporal_supersession();
22265
22486
  }
22266
22487
  });
22267
22488
 
@@ -22353,6 +22574,22 @@ async function storeLearning(projectRoot, params) {
22353
22574
  { source: saved.source, confidence: saved.confidence, actionable: saved.actionable }
22354
22575
  ).catch(() => {
22355
22576
  });
22577
+ detectSupersession(projectRoot, {
22578
+ id: saved.id,
22579
+ text: saved.insight,
22580
+ createdAt: saved.createdAt ?? (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19)
22581
+ }).then((candidates) => {
22582
+ for (const candidate of candidates) {
22583
+ supersedeMemory(
22584
+ projectRoot,
22585
+ candidate.existingId,
22586
+ saved.id,
22587
+ "auto:learning-supersedes \u2014 high overlap detected at store time"
22588
+ ).catch(() => {
22589
+ });
22590
+ }
22591
+ }).catch(() => {
22592
+ });
22356
22593
  return {
22357
22594
  ...saved,
22358
22595
  applicableTypes: JSON.parse(saved.applicableTypesJson || "[]")
@@ -22413,6 +22650,7 @@ var init_learnings = __esm({
22413
22650
  init_brain_accessor();
22414
22651
  init_graph_auto_populate();
22415
22652
  init_quality_scoring();
22653
+ init_temporal_supersession();
22416
22654
  }
22417
22655
  });
22418
22656
 
@@ -22451,16 +22689,6 @@ var init_mvi_helpers = __esm({
22451
22689
  }
22452
22690
  });
22453
22691
 
22454
- // packages/core/src/store/typed-query.ts
22455
- function typedAll(stmt, ...params) {
22456
- return stmt.all(...params);
22457
- }
22458
- var init_typed_query = __esm({
22459
- "packages/core/src/store/typed-query.ts"() {
22460
- "use strict";
22461
- }
22462
- });
22463
-
22464
22692
  // packages/core/src/memory/brain-similarity.ts
22465
22693
  function parseIdPrefix(id) {
22466
22694
  if (id.startsWith("D-") || /^D\d/.test(id)) return "decision";
@@ -24245,6 +24473,22 @@ async function storeDecision(projectRoot, params) {
24245
24473
  }
24246
24474
  } catch {
24247
24475
  }
24476
+ detectSupersession(projectRoot, {
24477
+ id: saved.id,
24478
+ text: saved.decision + " " + saved.rationale,
24479
+ createdAt: saved.createdAt ?? (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19)
24480
+ }).then((candidates) => {
24481
+ for (const candidate of candidates) {
24482
+ supersedeMemory(
24483
+ projectRoot,
24484
+ candidate.existingId,
24485
+ saved.id,
24486
+ "auto:decision-supersedes \u2014 high overlap detected at store time"
24487
+ ).catch(() => {
24488
+ });
24489
+ }
24490
+ }).catch(() => {
24491
+ });
24248
24492
  return saved;
24249
24493
  }
24250
24494
  async function recallDecision(projectRoot, id) {
@@ -24297,6 +24541,7 @@ var init_decisions2 = __esm({
24297
24541
  init_sqlite2();
24298
24542
  init_graph_auto_populate();
24299
24543
  init_quality_scoring();
24544
+ init_temporal_supersession();
24300
24545
  }
24301
24546
  });
24302
24547
 
@@ -45369,11 +45614,11 @@ var brain_consolidator_exports = {};
45369
45614
  __export(brain_consolidator_exports, {
45370
45615
  detectContradictions: () => detectContradictions
45371
45616
  });
45372
- function extractKeywords(text3) {
45617
+ function extractKeywords2(text3) {
45373
45618
  const words = text3.toLowerCase().replace(/[^a-z0-9\s\-_]/g, " ").split(/\s+/);
45374
45619
  const keywords = /* @__PURE__ */ new Set();
45375
45620
  for (const w2 of words) {
45376
- if (w2.length >= 4 && !STOP_WORDS.has(w2)) {
45621
+ if (w2.length >= 4 && !STOP_WORDS2.has(w2)) {
45377
45622
  keywords.add(w2);
45378
45623
  }
45379
45624
  }
@@ -45422,7 +45667,7 @@ async function detectContradictions(projectRoot) {
45422
45667
  const keywordMap = /* @__PURE__ */ new Map();
45423
45668
  const negationMap = /* @__PURE__ */ new Map();
45424
45669
  for (const entry of entries) {
45425
- keywordMap.set(entry.id, extractKeywords(entry.text));
45670
+ keywordMap.set(entry.id, extractKeywords2(entry.text));
45426
45671
  negationMap.set(entry.id, findNegationMarkers(entry.text));
45427
45672
  }
45428
45673
  for (let i = 0; i < entries.length; i++) {
@@ -45436,7 +45681,7 @@ async function detectContradictions(projectRoot) {
45436
45681
  const keywordsB = keywordMap.get(entryB.id);
45437
45682
  const negationsB = negationMap.get(entryB.id);
45438
45683
  const shared = keywordIntersection(keywordsA, keywordsB);
45439
- if (shared.length < MIN_SHARED_KEYWORDS) continue;
45684
+ if (shared.length < MIN_SHARED_KEYWORDS2) continue;
45440
45685
  const negationsOnlyInA = negationsA.filter((m2) => !negationsB.includes(m2));
45441
45686
  const negationsOnlyInB = negationsB.filter((m2) => !negationsA.includes(m2));
45442
45687
  const negationFlip = negationsOnlyInA.length > 0 || negationsOnlyInB.length > 0;
@@ -45454,8 +45699,8 @@ async function detectContradictions(projectRoot) {
45454
45699
  sharedKeywords: shared.slice(0, 10),
45455
45700
  negationMarkers: negMarkers.slice(0, 5)
45456
45701
  });
45457
- const nodeA = buildNodeId(table, entryA.id);
45458
- const nodeB = buildNodeId(table, entryB.id);
45702
+ const nodeA = buildNodeId2(table, entryA.id);
45703
+ const nodeB = buildNodeId2(table, entryB.id);
45459
45704
  try {
45460
45705
  nativeDb.prepare(`
45461
45706
  INSERT OR IGNORE INTO brain_page_edges
@@ -45484,7 +45729,7 @@ async function detectContradictions(projectRoot) {
45484
45729
  }
45485
45730
  return results;
45486
45731
  }
45487
- function buildNodeId(table, entryId) {
45732
+ function buildNodeId2(table, entryId) {
45488
45733
  const typeMap = {
45489
45734
  brain_observations: "observation",
45490
45735
  brain_learnings: "learning",
@@ -45494,13 +45739,13 @@ function buildNodeId(table, entryId) {
45494
45739
  const type = typeMap[table] ?? "entry";
45495
45740
  return `${type}:${entryId}`;
45496
45741
  }
45497
- var MIN_SHARED_KEYWORDS, NEGATION_MARKERS, STOP_WORDS;
45742
+ var MIN_SHARED_KEYWORDS2, NEGATION_MARKERS, STOP_WORDS2;
45498
45743
  var init_brain_consolidator = __esm({
45499
45744
  "packages/core/src/memory/brain-consolidator.ts"() {
45500
45745
  "use strict";
45501
45746
  init_brain_sqlite();
45502
45747
  init_typed_query();
45503
- MIN_SHARED_KEYWORDS = 3;
45748
+ MIN_SHARED_KEYWORDS2 = 3;
45504
45749
  NEGATION_MARKERS = [
45505
45750
  "not",
45506
45751
  "never",
@@ -45521,7 +45766,7 @@ var init_brain_consolidator = __esm({
45521
45766
  "undone",
45522
45767
  "superseded"
45523
45768
  ];
45524
- STOP_WORDS = /* @__PURE__ */ new Set([
45769
+ STOP_WORDS2 = /* @__PURE__ */ new Set([
45525
45770
  "the",
45526
45771
  "a",
45527
45772
  "an",
@@ -45576,6 +45821,390 @@ var init_brain_consolidator = __esm({
45576
45821
  }
45577
45822
  });
45578
45823
 
45824
+ // packages/core/src/memory/graph-memory-bridge.ts
45825
+ var graph_memory_bridge_exports = {};
45826
+ __export(graph_memory_bridge_exports, {
45827
+ autoLinkMemories: () => autoLinkMemories,
45828
+ linkMemoryToCode: () => linkMemoryToCode,
45829
+ listCodeLinks: () => listCodeLinks,
45830
+ queryCodeForMemory: () => queryCodeForMemory,
45831
+ queryMemoriesForCode: () => queryMemoriesForCode
45832
+ });
45833
+ function extractFilePaths(text3) {
45834
+ const paths = /* @__PURE__ */ new Set();
45835
+ for (const m2 of text3.matchAll(FILE_PATH_PATTERN)) {
45836
+ const p2 = m2[1];
45837
+ if (p2) paths.add(p2);
45838
+ }
45839
+ return Array.from(paths);
45840
+ }
45841
+ function extractSymbolCandidates(text3) {
45842
+ const syms = /* @__PURE__ */ new Set();
45843
+ for (const m2 of text3.matchAll(SYMBOL_PATTERN)) {
45844
+ const s3 = m2[1];
45845
+ if (s3 && s3.length >= 4 && !SYMBOL_STOP_WORDS.has(s3.toLowerCase())) {
45846
+ syms.add(s3);
45847
+ }
45848
+ }
45849
+ return Array.from(syms);
45850
+ }
45851
+ function metadataText(metaJson) {
45852
+ if (!metaJson) return "";
45853
+ try {
45854
+ const obj = JSON.parse(metaJson);
45855
+ return Object.values(obj).filter((v2) => typeof v2 === "string").join(" ");
45856
+ } catch {
45857
+ return "";
45858
+ }
45859
+ }
45860
+ async function linkMemoryToCode(projectRoot, memoryId, codeSymbol) {
45861
+ try {
45862
+ const brainDb = await getBrainDb(projectRoot);
45863
+ await getNexusDb();
45864
+ const nexusNative = getNexusNativeDb();
45865
+ if (!nexusNative) return false;
45866
+ const nexusNode = nexusNative.prepare("SELECT id, label, file_path, kind FROM nexus_nodes WHERE id = ? LIMIT 1").get(codeSymbol);
45867
+ if (!nexusNode) return false;
45868
+ const now2 = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
45869
+ const idParts = memoryId.split(":");
45870
+ const nodeType = idParts[0] ?? "observation";
45871
+ await brainDb.insert(brainPageNodes).values({
45872
+ id: memoryId,
45873
+ nodeType,
45874
+ label: memoryId,
45875
+ qualityScore: 0.5,
45876
+ contentHash: null,
45877
+ lastActivityAt: now2,
45878
+ createdAt: now2,
45879
+ updatedAt: now2
45880
+ }).onConflictDoUpdate({
45881
+ target: brainPageNodes.id,
45882
+ set: { lastActivityAt: now2, updatedAt: now2 }
45883
+ });
45884
+ await brainDb.insert(brainPageEdges).values({
45885
+ fromId: memoryId,
45886
+ toId: codeSymbol,
45887
+ edgeType: "code_reference",
45888
+ weight: 1,
45889
+ provenance: "manual",
45890
+ createdAt: now2
45891
+ }).onConflictDoNothing();
45892
+ return true;
45893
+ } catch (err) {
45894
+ console.warn("[graph-memory-bridge] linkMemoryToCode failed:", err);
45895
+ return false;
45896
+ }
45897
+ }
45898
+ async function autoLinkMemories(projectRoot) {
45899
+ const result = { scanned: 0, linked: 0, alreadyLinked: 0, links: [] };
45900
+ try {
45901
+ await getBrainDb(projectRoot);
45902
+ const brainNative = getBrainNativeDb();
45903
+ await getNexusDb();
45904
+ const nexusNative = getNexusNativeDb();
45905
+ if (!brainNative || !nexusNative) return result;
45906
+ const brainNodes = typedAll(
45907
+ brainNative.prepare(`
45908
+ SELECT id, node_type, label, quality_score, metadata_json
45909
+ FROM brain_page_nodes
45910
+ WHERE node_type IN ('observation', 'decision', 'pattern', 'learning')
45911
+ AND quality_score >= 0.3
45912
+ ORDER BY quality_score DESC
45913
+ LIMIT 500
45914
+ `)
45915
+ );
45916
+ result.scanned = brainNodes.length;
45917
+ if (brainNodes.length === 0) return result;
45918
+ const nexusNodes2 = typedAll(
45919
+ nexusNative.prepare(`
45920
+ SELECT id, label, name, file_path, kind
45921
+ FROM nexus_nodes
45922
+ WHERE kind NOT IN ('community', 'process', 'folder')
45923
+ LIMIT 20000
45924
+ `)
45925
+ );
45926
+ if (nexusNodes2.length === 0) return result;
45927
+ const byFilePath = /* @__PURE__ */ new Map();
45928
+ const byNameExact = /* @__PURE__ */ new Map();
45929
+ const byNameLower = /* @__PURE__ */ new Map();
45930
+ for (const node of nexusNodes2) {
45931
+ if (node.file_path) {
45932
+ const fp = node.file_path.toLowerCase();
45933
+ const existing = byFilePath.get(fp) ?? [];
45934
+ existing.push(node);
45935
+ byFilePath.set(fp, existing);
45936
+ }
45937
+ if (node.name) {
45938
+ const exact = byNameExact.get(node.name) ?? [];
45939
+ exact.push(node);
45940
+ byNameExact.set(node.name, exact);
45941
+ const lower = node.name.toLowerCase();
45942
+ const fuzzy = byNameLower.get(lower) ?? [];
45943
+ fuzzy.push(node);
45944
+ byNameLower.set(lower, fuzzy);
45945
+ }
45946
+ }
45947
+ const now2 = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
45948
+ const existingEdges = /* @__PURE__ */ new Set();
45949
+ const rawEdges = typedAll(
45950
+ brainNative.prepare(`
45951
+ SELECT from_id, to_id FROM brain_page_edges WHERE edge_type = 'code_reference'
45952
+ `)
45953
+ );
45954
+ for (const e of rawEdges) {
45955
+ existingEdges.add(`${e.from_id}|${e.to_id}`);
45956
+ }
45957
+ for (const brainNode of brainNodes) {
45958
+ const corpus = `${brainNode.label} ${metadataText(brainNode.metadata_json)}`;
45959
+ const filePaths = extractFilePaths(corpus);
45960
+ const symbolCandidates = extractSymbolCandidates(corpus);
45961
+ const candidates = [];
45962
+ for (const fp of filePaths) {
45963
+ const matches = byFilePath.get(fp.toLowerCase());
45964
+ if (matches) {
45965
+ for (const n of matches) {
45966
+ candidates.push({ nexusNode: n, strategy: "exact-file", weight: 1 });
45967
+ }
45968
+ }
45969
+ }
45970
+ for (const sym of symbolCandidates) {
45971
+ const exactMatches = byNameExact.get(sym);
45972
+ if (exactMatches) {
45973
+ for (const n of exactMatches) {
45974
+ candidates.push({ nexusNode: n, strategy: "exact-symbol", weight: 1 });
45975
+ }
45976
+ }
45977
+ }
45978
+ const exactSymSet = new Set(
45979
+ symbolCandidates.flatMap((s3) => byNameExact.get(s3) ?? []).map((n) => n.id)
45980
+ );
45981
+ for (const sym of symbolCandidates) {
45982
+ if (sym.length < 5) continue;
45983
+ const lower = sym.toLowerCase();
45984
+ const fuzzyMatches = byNameLower.get(lower);
45985
+ if (fuzzyMatches) {
45986
+ for (const n of fuzzyMatches) {
45987
+ if (!exactSymSet.has(n.id)) {
45988
+ candidates.push({ nexusNode: n, strategy: "fuzzy-symbol", weight: 0.6 });
45989
+ }
45990
+ }
45991
+ }
45992
+ }
45993
+ const bestByNexusId = /* @__PURE__ */ new Map();
45994
+ for (const c of candidates) {
45995
+ const existing = bestByNexusId.get(c.nexusNode.id);
45996
+ if (!existing || c.weight > existing.weight) {
45997
+ bestByNexusId.set(c.nexusNode.id, c);
45998
+ }
45999
+ }
46000
+ const sortedCandidates = Array.from(bestByNexusId.values()).sort((a, b2) => b2.weight - a.weight).slice(0, 10);
46001
+ for (const { nexusNode, strategy, weight } of sortedCandidates) {
46002
+ const edgeKey = `${brainNode.id}|${nexusNode.id}`;
46003
+ if (existingEdges.has(edgeKey)) {
46004
+ result.alreadyLinked++;
46005
+ continue;
46006
+ }
46007
+ brainNative.prepare(`
46008
+ INSERT OR IGNORE INTO brain_page_nodes
46009
+ (id, node_type, label, quality_score, content_hash, metadata_json, last_activity_at, created_at, updated_at)
46010
+ VALUES (?, ?, ?, ?, NULL, NULL, ?, ?, ?)
46011
+ `).run(
46012
+ brainNode.id,
46013
+ brainNode.node_type,
46014
+ brainNode.label,
46015
+ brainNode.quality_score,
46016
+ now2,
46017
+ now2,
46018
+ now2
46019
+ );
46020
+ try {
46021
+ brainNative.prepare(`
46022
+ INSERT OR IGNORE INTO brain_page_edges
46023
+ (from_id, to_id, edge_type, weight, provenance, created_at)
46024
+ VALUES (?, ?, 'code_reference', ?, ?, ?)
46025
+ `).run(brainNode.id, nexusNode.id, weight, `auto:${strategy}`, now2);
46026
+ existingEdges.add(edgeKey);
46027
+ result.linked++;
46028
+ result.links.push({
46029
+ brainNodeId: brainNode.id,
46030
+ nexusNodeId: nexusNode.id,
46031
+ nexusLabel: nexusNode.label,
46032
+ matchStrategy: strategy,
46033
+ weight
46034
+ });
46035
+ } catch (edgeErr) {
46036
+ console.warn("[graph-memory-bridge] edge insert failed:", edgeErr);
46037
+ }
46038
+ }
46039
+ }
46040
+ } catch (err) {
46041
+ console.warn("[graph-memory-bridge] autoLinkMemories failed:", err);
46042
+ }
46043
+ return result;
46044
+ }
46045
+ async function queryMemoriesForCode(projectRoot, symbol2) {
46046
+ const result = { nexusNodeId: symbol2, memories: [] };
46047
+ try {
46048
+ await getBrainDb(projectRoot);
46049
+ const brainNative = getBrainNativeDb();
46050
+ if (!brainNative) return result;
46051
+ const rows = typedAll(
46052
+ brainNative.prepare(`
46053
+ SELECT n.id, n.node_type, n.label, n.quality_score,
46054
+ e.weight, e.provenance
46055
+ FROM brain_page_edges e
46056
+ JOIN brain_page_nodes n ON n.id = e.from_id
46057
+ WHERE e.to_id = ?
46058
+ AND e.edge_type = 'code_reference'
46059
+ ORDER BY e.weight DESC, n.quality_score DESC
46060
+ LIMIT 50
46061
+ `),
46062
+ symbol2
46063
+ );
46064
+ result.memories = rows.map((r) => ({
46065
+ nodeId: r.id,
46066
+ nodeType: r.node_type,
46067
+ label: r.label,
46068
+ qualityScore: r.quality_score,
46069
+ edgeWeight: r.weight,
46070
+ matchStrategy: r.provenance?.replace("auto:", "") ?? "manual"
46071
+ }));
46072
+ } catch (err) {
46073
+ console.warn("[graph-memory-bridge] queryMemoriesForCode failed:", err);
46074
+ }
46075
+ return result;
46076
+ }
46077
+ async function queryCodeForMemory(projectRoot, memoryId) {
46078
+ const result = { brainNodeId: memoryId, codeNodes: [] };
46079
+ try {
46080
+ await getBrainDb(projectRoot);
46081
+ const brainNative = getBrainNativeDb();
46082
+ await getNexusDb();
46083
+ const nexusNative = getNexusNativeDb();
46084
+ if (!brainNative || !nexusNative) return result;
46085
+ const brainEdges = typedAll(
46086
+ brainNative.prepare(`
46087
+ SELECT to_id, weight, provenance
46088
+ FROM brain_page_edges
46089
+ WHERE from_id = ?
46090
+ AND edge_type = 'code_reference'
46091
+ ORDER BY weight DESC
46092
+ LIMIT 50
46093
+ `),
46094
+ memoryId
46095
+ );
46096
+ if (brainEdges.length === 0) return result;
46097
+ for (const edge of brainEdges) {
46098
+ const nexusNode = nexusNative.prepare("SELECT id, label, file_path, kind FROM nexus_nodes WHERE id = ? LIMIT 1").get(edge.to_id);
46099
+ if (nexusNode) {
46100
+ result.codeNodes.push({
46101
+ nexusNodeId: nexusNode.id,
46102
+ label: nexusNode.label,
46103
+ filePath: nexusNode.file_path,
46104
+ kind: nexusNode.kind,
46105
+ edgeWeight: edge.weight,
46106
+ matchStrategy: edge.provenance?.replace("auto:", "") ?? "manual"
46107
+ });
46108
+ }
46109
+ }
46110
+ } catch (err) {
46111
+ console.warn("[graph-memory-bridge] queryCodeForMemory failed:", err);
46112
+ }
46113
+ return result;
46114
+ }
46115
+ async function listCodeLinks(projectRoot, limit = 100) {
46116
+ const entries = [];
46117
+ try {
46118
+ await getBrainDb(projectRoot);
46119
+ const brainNative = getBrainNativeDb();
46120
+ await getNexusDb();
46121
+ const nexusNative = getNexusNativeDb();
46122
+ if (!brainNative || !nexusNative) return entries;
46123
+ const rows = typedAll(
46124
+ brainNative.prepare(`
46125
+ SELECT e.from_id, e.to_id, e.weight, e.created_at,
46126
+ n.node_type, n.label
46127
+ FROM brain_page_edges e
46128
+ JOIN brain_page_nodes n ON n.id = e.from_id
46129
+ WHERE e.edge_type = 'code_reference'
46130
+ ORDER BY e.weight DESC, e.created_at DESC
46131
+ LIMIT ?
46132
+ `),
46133
+ limit
46134
+ );
46135
+ for (const row of rows) {
46136
+ const nexusNode = nexusNative.prepare("SELECT id, label, file_path, kind FROM nexus_nodes WHERE id = ? LIMIT 1").get(row.to_id);
46137
+ entries.push({
46138
+ brainNodeId: row.from_id,
46139
+ brainNodeType: row.node_type,
46140
+ brainNodeLabel: row.label,
46141
+ nexusNodeId: row.to_id,
46142
+ nexusNodeLabel: nexusNode?.label ?? row.to_id,
46143
+ filePath: nexusNode?.file_path ?? null,
46144
+ kind: nexusNode?.kind ?? "unknown",
46145
+ weight: row.weight,
46146
+ createdAt: row.created_at
46147
+ });
46148
+ }
46149
+ } catch (err) {
46150
+ console.warn("[graph-memory-bridge] listCodeLinks failed:", err);
46151
+ }
46152
+ return entries;
46153
+ }
46154
+ var FILE_PATH_PATTERN, SYMBOL_PATTERN, SYMBOL_STOP_WORDS;
46155
+ var init_graph_memory_bridge = __esm({
46156
+ "packages/core/src/memory/graph-memory-bridge.ts"() {
46157
+ "use strict";
46158
+ init_brain_schema();
46159
+ init_brain_sqlite();
46160
+ init_nexus_sqlite();
46161
+ init_typed_query();
46162
+ FILE_PATH_PATTERN = /(?:^|\s|['"`(])([a-zA-Z0-9_\-./]+\.(?:ts|tsx|js|jsx|rs|go|py|mjs|cjs))(?:$|\s|['"`)])/g;
46163
+ 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;
46164
+ SYMBOL_STOP_WORDS = /* @__PURE__ */ new Set([
46165
+ "true",
46166
+ "false",
46167
+ "null",
46168
+ "undefined",
46169
+ "const",
46170
+ "async",
46171
+ "await",
46172
+ "return",
46173
+ "export",
46174
+ "import",
46175
+ "from",
46176
+ "type",
46177
+ "interface",
46178
+ "function",
46179
+ "class",
46180
+ "this",
46181
+ "super",
46182
+ "extends",
46183
+ "implements",
46184
+ "with",
46185
+ "that",
46186
+ "then",
46187
+ "when",
46188
+ "have",
46189
+ "been",
46190
+ "will",
46191
+ "should",
46192
+ "could",
46193
+ "would",
46194
+ "error",
46195
+ "result",
46196
+ "value",
46197
+ "data",
46198
+ "info",
46199
+ "note",
46200
+ "todo",
46201
+ "done",
46202
+ "fail",
46203
+ "pass"
46204
+ ]);
46205
+ }
46206
+ });
46207
+
45579
46208
  // packages/core/src/memory/brain-lifecycle.ts
45580
46209
  var brain_lifecycle_exports = {};
45581
46210
  __export(brain_lifecycle_exports, {
@@ -45609,11 +46238,11 @@ async function applyTemporalDecay(projectRoot, options) {
45609
46238
  tablesProcessed: ["brain_learnings"]
45610
46239
  };
45611
46240
  }
45612
- function extractKeywords2(text3) {
46241
+ function extractKeywords3(text3) {
45613
46242
  const words = text3.toLowerCase().replace(/[^a-z0-9\s-]/g, "").split(/\s+/);
45614
46243
  const keywords = /* @__PURE__ */ new Set();
45615
46244
  for (const w2 of words) {
45616
- if (w2.length >= 4 && !STOP_WORDS2.has(w2)) {
46245
+ if (w2.length >= 4 && !STOP_WORDS3.has(w2)) {
45617
46246
  keywords.add(w2);
45618
46247
  }
45619
46248
  }
@@ -45651,7 +46280,7 @@ async function consolidateMemories(projectRoot, options) {
45651
46280
  }
45652
46281
  const entries = oldObservations.map((obs) => ({
45653
46282
  ...obs,
45654
- keywords: extractKeywords2(`${obs.title} ${obs.narrative ?? ""}`),
46283
+ keywords: extractKeywords3(`${obs.title} ${obs.narrative ?? ""}`),
45655
46284
  clustered: false
45656
46285
  }));
45657
46286
  const clusters = [];
@@ -45875,6 +46504,13 @@ async function runConsolidation(projectRoot) {
45875
46504
  } catch (err) {
45876
46505
  console.warn("[consolidation] Step 7 summary generation failed:", err);
45877
46506
  }
46507
+ try {
46508
+ const { autoLinkMemories: autoLinkMemories2 } = await Promise.resolve().then(() => (init_graph_memory_bridge(), graph_memory_bridge_exports));
46509
+ const bridgeResult = await autoLinkMemories2(projectRoot);
46510
+ result.graphLinksCreated = bridgeResult.linked;
46511
+ } catch (err) {
46512
+ console.warn("[consolidation] Step 8 graph memory bridge failed:", err);
46513
+ }
45878
46514
  return result;
45879
46515
  }
45880
46516
  async function deduplicateByEmbedding(projectRoot) {
@@ -46053,13 +46689,13 @@ async function strengthenCoRetrievedEdges(projectRoot) {
46053
46689
  }
46054
46690
  return strengthened;
46055
46691
  }
46056
- var STOP_WORDS2;
46692
+ var STOP_WORDS3;
46057
46693
  var init_brain_lifecycle = __esm({
46058
46694
  "packages/core/src/memory/brain-lifecycle.ts"() {
46059
46695
  "use strict";
46060
46696
  init_brain_accessor();
46061
46697
  init_typed_query();
46062
- STOP_WORDS2 = /* @__PURE__ */ new Set([
46698
+ STOP_WORDS3 = /* @__PURE__ */ new Set([
46063
46699
  "the",
46064
46700
  "a",
46065
46701
  "an",
@@ -46664,6 +47300,358 @@ var init_session_hooks = __esm({
46664
47300
  }
46665
47301
  });
46666
47302
 
47303
+ // packages/core/src/memory/quality-feedback.ts
47304
+ var quality_feedback_exports = {};
47305
+ __export(quality_feedback_exports, {
47306
+ correlateOutcomes: () => correlateOutcomes,
47307
+ getMemoryQualityReport: () => getMemoryQualityReport,
47308
+ trackMemoryUsage: () => trackMemoryUsage
47309
+ });
47310
+ async function ensureUsageLogTable(projectRoot) {
47311
+ const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
47312
+ await getBrainDb2(projectRoot);
47313
+ const nativeDb = getBrainNativeDb2();
47314
+ if (!nativeDb) return;
47315
+ try {
47316
+ nativeDb.prepare(
47317
+ `CREATE TABLE IF NOT EXISTS brain_usage_log (
47318
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
47319
+ entry_id TEXT NOT NULL,
47320
+ task_id TEXT,
47321
+ used INTEGER NOT NULL DEFAULT 0,
47322
+ outcome TEXT NOT NULL DEFAULT 'unknown',
47323
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
47324
+ )`
47325
+ ).run();
47326
+ nativeDb.prepare(
47327
+ `CREATE INDEX IF NOT EXISTS idx_brain_usage_log_entry_id
47328
+ ON brain_usage_log(entry_id)`
47329
+ ).run();
47330
+ nativeDb.prepare(
47331
+ `CREATE INDEX IF NOT EXISTS idx_brain_usage_log_task_id
47332
+ ON brain_usage_log(task_id)`
47333
+ ).run();
47334
+ } catch {
47335
+ }
47336
+ }
47337
+ async function ensurePruneCandidateColumn(projectRoot) {
47338
+ const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
47339
+ await getBrainDb2(projectRoot);
47340
+ const nativeDb = getBrainNativeDb2();
47341
+ if (!nativeDb) return;
47342
+ const tables = [
47343
+ "brain_decisions",
47344
+ "brain_patterns",
47345
+ "brain_learnings",
47346
+ "brain_observations"
47347
+ ];
47348
+ for (const tbl of tables) {
47349
+ try {
47350
+ nativeDb.prepare(`ALTER TABLE ${tbl} ADD COLUMN prune_candidate INTEGER DEFAULT 0`).run();
47351
+ } catch {
47352
+ }
47353
+ }
47354
+ }
47355
+ async function trackMemoryUsage(projectRoot, memoryId, used, taskId, outcome = "unknown") {
47356
+ if (!memoryId?.trim()) return;
47357
+ await ensureUsageLogTable(projectRoot);
47358
+ const { getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
47359
+ const nativeDb = getBrainNativeDb2();
47360
+ if (!nativeDb) return;
47361
+ const now2 = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
47362
+ try {
47363
+ nativeDb.prepare(
47364
+ `INSERT INTO brain_usage_log (entry_id, task_id, used, outcome, created_at)
47365
+ VALUES (?, ?, ?, ?, ?)`
47366
+ ).run(memoryId, taskId ?? null, used ? 1 : 0, outcome, now2);
47367
+ } catch {
47368
+ }
47369
+ }
47370
+ function tableForId(id) {
47371
+ if (id.startsWith("D-") || /^D\d/.test(id)) return "brain_decisions";
47372
+ if (id.startsWith("P-") || /^P\d/.test(id)) return "brain_patterns";
47373
+ if (id.startsWith("L-") || /^L\d/.test(id)) return "brain_learnings";
47374
+ if (id.startsWith("O-") || id.startsWith("CM-") || /^O/.test(id)) return "brain_observations";
47375
+ return null;
47376
+ }
47377
+ function applyQualityDelta(nativeDb, table, id, delta, now2) {
47378
+ if (!nativeDb) return;
47379
+ try {
47380
+ nativeDb.prepare(
47381
+ `UPDATE ${table}
47382
+ SET quality_score = MAX(0.0, MIN(1.0, COALESCE(quality_score, 0.5) + ?)),
47383
+ updated_at = ?
47384
+ WHERE id = ?`
47385
+ ).run(delta, now2, id);
47386
+ } catch {
47387
+ }
47388
+ }
47389
+ async function correlateOutcomes(projectRoot) {
47390
+ await ensureUsageLogTable(projectRoot);
47391
+ await ensurePruneCandidateColumn(projectRoot);
47392
+ const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
47393
+ await getBrainDb2(projectRoot);
47394
+ const nativeDb = getBrainNativeDb2();
47395
+ const ranAt = (/* @__PURE__ */ new Date()).toISOString();
47396
+ if (!nativeDb) {
47397
+ return { boosted: 0, penalized: 0, flaggedForPruning: 0, ranAt };
47398
+ }
47399
+ const now2 = ranAt.replace("T", " ").slice(0, 19);
47400
+ let boosted = 0;
47401
+ let penalized = 0;
47402
+ let usageRows = [];
47403
+ try {
47404
+ usageRows = typedAll(
47405
+ nativeDb.prepare(
47406
+ `SELECT entry_id, outcome, SUM(used) AS used_count
47407
+ FROM brain_usage_log
47408
+ WHERE outcome IN ('success', 'failure')
47409
+ GROUP BY entry_id, outcome`
47410
+ )
47411
+ );
47412
+ } catch {
47413
+ usageRows = [];
47414
+ }
47415
+ for (const row of usageRows) {
47416
+ const table = tableForId(row.entry_id);
47417
+ if (!table) continue;
47418
+ if (row.outcome === "success" && row.used_count > 0) {
47419
+ applyQualityDelta(nativeDb, table, row.entry_id, 0.05, now2);
47420
+ boosted++;
47421
+ } else if (row.outcome === "failure" && row.used_count > 0) {
47422
+ applyQualityDelta(nativeDb, table, row.entry_id, -0.05, now2);
47423
+ penalized++;
47424
+ }
47425
+ }
47426
+ const cutoffDate = new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3).toISOString().replace("T", " ").slice(0, 19);
47427
+ let flaggedForPruning = 0;
47428
+ const pruneTargetTables = [
47429
+ { table: "brain_decisions", dateCol: "created_at" },
47430
+ { table: "brain_patterns", dateCol: "extracted_at" },
47431
+ { table: "brain_learnings", dateCol: "created_at" },
47432
+ { table: "brain_observations", dateCol: "created_at" }
47433
+ ];
47434
+ for (const { table, dateCol } of pruneTargetTables) {
47435
+ try {
47436
+ const result = nativeDb.prepare(
47437
+ `UPDATE ${table}
47438
+ SET prune_candidate = 1
47439
+ WHERE COALESCE(citation_count, 0) = 0
47440
+ AND ${dateCol} < ?`
47441
+ ).run(cutoffDate);
47442
+ flaggedForPruning += result.changes ?? 0;
47443
+ } catch {
47444
+ }
47445
+ }
47446
+ return { boosted, penalized, flaggedForPruning, ranAt };
47447
+ }
47448
+ async function getMemoryQualityReport(projectRoot) {
47449
+ await ensureUsageLogTable(projectRoot);
47450
+ const { getBrainDb: getBrainDb2, getBrainNativeDb: getBrainNativeDb2 } = await Promise.resolve().then(() => (init_brain_sqlite(), brain_sqlite_exports));
47451
+ await getBrainDb2(projectRoot);
47452
+ const nativeDb = getBrainNativeDb2();
47453
+ const emptyReport = {
47454
+ totalRetrievals: 0,
47455
+ uniqueEntriesRetrieved: 0,
47456
+ usageRate: 0,
47457
+ topRetrieved: [],
47458
+ neverRetrieved: [],
47459
+ qualityDistribution: { low: 0, medium: 0, high: 0 },
47460
+ tierDistribution: { short: 0, medium: 0, long: 0, unknown: 0 },
47461
+ noiseRatio: 0
47462
+ };
47463
+ if (!nativeDb) return emptyReport;
47464
+ let totalRetrievals = 0;
47465
+ let uniqueEntriesRetrieved = 0;
47466
+ try {
47467
+ const logCount = typedAll(
47468
+ nativeDb.prepare("SELECT COUNT(*) AS cnt FROM brain_retrieval_log")
47469
+ );
47470
+ totalRetrievals = logCount[0]?.cnt ?? 0;
47471
+ const uniqueCount = typedAll(
47472
+ nativeDb.prepare(
47473
+ `SELECT COUNT(DISTINCT value) AS cnt
47474
+ FROM brain_retrieval_log,
47475
+ json_each('["' || replace(entry_ids, ',', '","') || '"]')`
47476
+ )
47477
+ );
47478
+ uniqueEntriesRetrieved = uniqueCount[0]?.cnt ?? 0;
47479
+ } catch {
47480
+ }
47481
+ let usageRate = 0;
47482
+ try {
47483
+ const totalUsage = typedAll(
47484
+ nativeDb.prepare("SELECT COUNT(*) AS cnt FROM brain_usage_log")
47485
+ );
47486
+ const usedCount = typedAll(
47487
+ nativeDb.prepare("SELECT COUNT(*) AS cnt FROM brain_usage_log WHERE used = 1")
47488
+ );
47489
+ const total = totalUsage[0]?.cnt ?? 0;
47490
+ const used = usedCount[0]?.cnt ?? 0;
47491
+ usageRate = total > 0 ? used / total : 0;
47492
+ } catch {
47493
+ }
47494
+ const topRetrieved = [];
47495
+ try {
47496
+ const rows = typedAll(
47497
+ nativeDb.prepare(
47498
+ `SELECT id,
47499
+ 'decision' AS type,
47500
+ decision AS title,
47501
+ COALESCE(citation_count, 0) AS citation_count
47502
+ FROM brain_decisions
47503
+ UNION ALL
47504
+ SELECT id,
47505
+ 'pattern' AS type,
47506
+ pattern AS title,
47507
+ COALESCE(citation_count, 0) AS citation_count
47508
+ FROM brain_patterns
47509
+ UNION ALL
47510
+ SELECT id,
47511
+ 'learning' AS type,
47512
+ insight AS title,
47513
+ COALESCE(citation_count, 0) AS citation_count
47514
+ FROM brain_learnings
47515
+ UNION ALL
47516
+ SELECT id,
47517
+ 'observation' AS type,
47518
+ title AS title,
47519
+ COALESCE(citation_count, 0) AS citation_count
47520
+ FROM brain_observations
47521
+ ORDER BY citation_count DESC
47522
+ LIMIT 10`
47523
+ )
47524
+ );
47525
+ for (const r of rows) {
47526
+ topRetrieved.push({
47527
+ id: r.id,
47528
+ type: r.type,
47529
+ title: String(r.title ?? "").slice(0, 120),
47530
+ citationCount: r.citation_count
47531
+ });
47532
+ }
47533
+ } catch {
47534
+ }
47535
+ const neverRetrieved = [];
47536
+ try {
47537
+ const rows = typedAll(
47538
+ nativeDb.prepare(
47539
+ `SELECT id,
47540
+ 'decision' AS type,
47541
+ decision AS title,
47542
+ COALESCE(quality_score, 0.5) AS quality_score
47543
+ FROM brain_decisions
47544
+ WHERE COALESCE(citation_count, 0) = 0
47545
+ UNION ALL
47546
+ SELECT id,
47547
+ 'pattern' AS type,
47548
+ pattern AS title,
47549
+ COALESCE(quality_score, 0.5) AS quality_score
47550
+ FROM brain_patterns
47551
+ WHERE COALESCE(citation_count, 0) = 0
47552
+ UNION ALL
47553
+ SELECT id,
47554
+ 'learning' AS type,
47555
+ insight AS title,
47556
+ COALESCE(quality_score, 0.5) AS quality_score
47557
+ FROM brain_learnings
47558
+ WHERE COALESCE(citation_count, 0) = 0
47559
+ UNION ALL
47560
+ SELECT id,
47561
+ 'observation' AS type,
47562
+ title AS title,
47563
+ COALESCE(quality_score, 0.5) AS quality_score
47564
+ FROM brain_observations
47565
+ WHERE COALESCE(citation_count, 0) = 0
47566
+ ORDER BY quality_score ASC
47567
+ LIMIT 10`
47568
+ )
47569
+ );
47570
+ for (const r of rows) {
47571
+ neverRetrieved.push({
47572
+ id: r.id,
47573
+ type: r.type,
47574
+ title: String(r.title ?? "").slice(0, 120),
47575
+ qualityScore: r.quality_score
47576
+ });
47577
+ }
47578
+ } catch {
47579
+ }
47580
+ let qualityDistribution = { low: 0, medium: 0, high: 0 };
47581
+ try {
47582
+ const rows = typedAll(
47583
+ nativeDb.prepare(
47584
+ `SELECT
47585
+ SUM(CASE WHEN qs < 0.3 THEN 1 ELSE 0 END) AS low,
47586
+ SUM(CASE WHEN qs >= 0.3 AND qs <= 0.6 THEN 1 ELSE 0 END) AS medium,
47587
+ SUM(CASE WHEN qs > 0.6 THEN 1 ELSE 0 END) AS high
47588
+ FROM (
47589
+ SELECT COALESCE(quality_score, 0.5) AS qs FROM brain_decisions
47590
+ UNION ALL
47591
+ SELECT COALESCE(quality_score, 0.5) AS qs FROM brain_patterns
47592
+ UNION ALL
47593
+ SELECT COALESCE(quality_score, 0.5) AS qs FROM brain_learnings
47594
+ UNION ALL
47595
+ SELECT COALESCE(quality_score, 0.5) AS qs FROM brain_observations
47596
+ )`
47597
+ )
47598
+ );
47599
+ if (rows[0]) {
47600
+ qualityDistribution = {
47601
+ low: rows[0].low ?? 0,
47602
+ medium: rows[0].medium ?? 0,
47603
+ high: rows[0].high ?? 0
47604
+ };
47605
+ }
47606
+ } catch {
47607
+ }
47608
+ const tierDistribution = { short: 0, medium: 0, long: 0, unknown: 0 };
47609
+ try {
47610
+ const rows = typedAll(
47611
+ nativeDb.prepare(
47612
+ `SELECT memory_tier AS tier, COUNT(*) AS cnt
47613
+ FROM (
47614
+ SELECT memory_tier FROM brain_decisions
47615
+ UNION ALL
47616
+ SELECT memory_tier FROM brain_patterns
47617
+ UNION ALL
47618
+ SELECT memory_tier FROM brain_learnings
47619
+ UNION ALL
47620
+ SELECT memory_tier FROM brain_observations
47621
+ )
47622
+ GROUP BY memory_tier`
47623
+ )
47624
+ );
47625
+ for (const r of rows) {
47626
+ const tier = r.tier?.toLowerCase() ?? "unknown";
47627
+ if (tier === "short" || tier === "medium" || tier === "long") {
47628
+ tierDistribution[tier] += r.cnt;
47629
+ } else {
47630
+ tierDistribution.unknown += r.cnt;
47631
+ }
47632
+ }
47633
+ } catch {
47634
+ }
47635
+ const totalEntries = qualityDistribution.low + qualityDistribution.medium + qualityDistribution.high;
47636
+ const noiseRatio = totalEntries > 0 ? qualityDistribution.low / totalEntries : 0;
47637
+ return {
47638
+ totalRetrievals,
47639
+ uniqueEntriesRetrieved,
47640
+ usageRate,
47641
+ topRetrieved,
47642
+ neverRetrieved,
47643
+ qualityDistribution,
47644
+ tierDistribution,
47645
+ noiseRatio
47646
+ };
47647
+ }
47648
+ var init_quality_feedback = __esm({
47649
+ "packages/core/src/memory/quality-feedback.ts"() {
47650
+ "use strict";
47651
+ init_typed_query();
47652
+ }
47653
+ });
47654
+
46667
47655
  // packages/core/src/hooks/handlers/task-hooks.ts
46668
47656
  async function handleToolStart(projectRoot, payload) {
46669
47657
  const { observeBrain: observeBrain2 } = await Promise.resolve().then(() => (init_brain_retrieval(), brain_retrieval_exports));
@@ -46697,6 +47685,13 @@ async function handleToolComplete(projectRoot, payload) {
46697
47685
  } catch {
46698
47686
  }
46699
47687
  });
47688
+ setImmediate(async () => {
47689
+ try {
47690
+ const { correlateOutcomes: correlateOutcomes2 } = await Promise.resolve().then(() => (init_quality_feedback(), quality_feedback_exports));
47691
+ await correlateOutcomes2(projectRoot);
47692
+ } catch {
47693
+ }
47694
+ });
46700
47695
  await maybeRefreshMemoryBridge(projectRoot);
46701
47696
  }
46702
47697
  var init_task_hooks = __esm({
@@ -53194,7 +54189,7 @@ function generateRecommendation2(changeType, affectedCount, cascadeDepth, taskId
53194
54189
  }
53195
54190
  }
53196
54191
  function scoreTaskMatch(change, task) {
53197
- const STOP_WORDS4 = /* @__PURE__ */ new Set([
54192
+ const STOP_WORDS5 = /* @__PURE__ */ new Set([
53198
54193
  "a",
53199
54194
  "an",
53200
54195
  "the",
@@ -53216,7 +54211,7 @@ function scoreTaskMatch(change, task) {
53216
54211
  "do",
53217
54212
  "not"
53218
54213
  ]);
53219
- const tokenise = (text3) => text3.toLowerCase().split(/\W+/).filter((t) => t.length > 2 && !STOP_WORDS4.has(t));
54214
+ const tokenise = (text3) => text3.toLowerCase().split(/\W+/).filter((t) => t.length > 2 && !STOP_WORDS5.has(t));
53220
54215
  const changeTokens = new Set(tokenise(change));
53221
54216
  if (changeTokens.size === 0) return 0;
53222
54217
  const taskText = `${task.title ?? ""} ${task.description ?? ""}`;
@@ -59666,6 +60661,7 @@ __export(memory_exports, {
59666
60661
  bulkLink: () => bulkLink,
59667
60662
  compactManifest: () => compactManifest,
59668
60663
  consolidateMemories: () => consolidateMemories,
60664
+ correlateOutcomes: () => correlateOutcomes,
59669
60665
  detectContradictions: () => detectContradictions,
59670
60666
  ensureFts5Tables: () => ensureFts5Tables,
59671
60667
  fetchBrainEntries: () => fetchBrainEntries,
@@ -59676,6 +60672,7 @@ __export(memory_exports, {
59676
60672
  getLinkedLearnings: () => getLinkedLearnings,
59677
60673
  getLinkedPatterns: () => getLinkedPatterns,
59678
60674
  getMemoryLinks: () => getMemoryLinks,
60675
+ getMemoryQualityReport: () => getMemoryQualityReport,
59679
60676
  getTaskLinks: () => getTaskLinks,
59680
60677
  hybridSearch: () => hybridSearch,
59681
60678
  learningStats: () => learningStats,
@@ -59717,6 +60714,7 @@ __export(memory_exports, {
59717
60714
  storePattern: () => storePattern,
59718
60715
  storeVerifiedCandidate: () => storeVerifiedCandidate,
59719
60716
  timelineBrain: () => timelineBrain,
60717
+ trackMemoryUsage: () => trackMemoryUsage,
59720
60718
  unlinkMemoryFromTask: () => unlinkMemoryFromTask,
59721
60719
  updateDecisionOutcome: () => updateDecisionOutcome,
59722
60720
  updateResearch: () => updateResearch,
@@ -60339,6 +61337,7 @@ var init_memory = __esm({
60339
61337
  init_extraction_gate();
60340
61338
  init_learnings();
60341
61339
  init_patterns();
61340
+ init_quality_feedback();
60342
61341
  }
60343
61342
  });
60344
61343
 
@@ -64053,8 +65052,8 @@ var init_deps = __esm({
64053
65052
  });
64054
65053
 
64055
65054
  // packages/core/src/nexus/discover.ts
64056
- function extractKeywords3(text3) {
64057
- return text3.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w2) => w2.length > 2 && !STOP_WORDS3.has(w2));
65055
+ function extractKeywords4(text3) {
65056
+ return text3.toLowerCase().replace(/[^a-z0-9\s-]/g, " ").split(/\s+/).filter((w2) => w2.length > 2 && !STOP_WORDS4.has(w2));
64058
65057
  }
64059
65058
  async function discoverRelated(taskQuery, method = "auto", limit = 10) {
64060
65059
  if (!validateSyntax(taskQuery)) {
@@ -64077,7 +65076,7 @@ async function discoverRelated(taskQuery, method = "auto", limit = 10) {
64077
65076
  const sourceLabels = new Set(sourceTask.labels ?? []);
64078
65077
  const sourceDesc = (sourceTask.description ?? "").toLowerCase();
64079
65078
  const sourceTitle = (sourceTask.title ?? "").toLowerCase();
64080
- const sourceWords = extractKeywords3(sourceTitle + " " + sourceDesc);
65079
+ const sourceWords = extractKeywords4(sourceTitle + " " + sourceDesc);
64081
65080
  const parsed = parseQuery(taskQuery);
64082
65081
  const registry2 = await readRegistry();
64083
65082
  if (!registry2) {
@@ -64112,7 +65111,7 @@ async function discoverRelated(taskQuery, method = "auto", limit = 10) {
64112
65111
  }
64113
65112
  if (method === "description" || method === "auto") {
64114
65113
  const taskDesc = ((task.description ?? "") + " " + (task.title ?? "")).toLowerCase();
64115
- const taskWords = extractKeywords3(taskDesc);
65114
+ const taskWords = extractKeywords4(taskDesc);
64116
65115
  const commonWords = sourceWords.filter((w2) => taskWords.includes(w2));
64117
65116
  if (commonWords.length > 0) {
64118
65117
  const descScore = commonWords.length / Math.max(sourceWords.length, taskWords.length, 1);
@@ -64211,14 +65210,14 @@ async function searchAcrossProjects(pattern, projectFilter, limit = 20) {
64211
65210
  const sliced = results.slice(0, limit);
64212
65211
  return { pattern, results: sliced, resultCount: sliced.length };
64213
65212
  }
64214
- var STOP_WORDS3;
65213
+ var STOP_WORDS4;
64215
65214
  var init_discover = __esm({
64216
65215
  "packages/core/src/nexus/discover.ts"() {
64217
65216
  "use strict";
64218
65217
  init_data_accessor();
64219
65218
  init_query4();
64220
65219
  init_registry3();
64221
- STOP_WORDS3 = /* @__PURE__ */ new Set([
65220
+ STOP_WORDS4 = /* @__PURE__ */ new Set([
64222
65221
  "the",
64223
65222
  "a",
64224
65223
  "an",
@@ -65201,6 +66200,19 @@ async function completeTask(options, cwd, accessor) {
65201
66200
  })()
65202
66201
  ).catch(() => {
65203
66202
  });
66203
+ try {
66204
+ const { hooks: hooks2 } = await Promise.resolve().then(() => (init_registry(), registry_exports));
66205
+ await hooks2.dispatch("PostToolUse", cwd ?? process.cwd(), {
66206
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
66207
+ taskId: options.taskId,
66208
+ taskTitle: task.title,
66209
+ previousStatus: before.status,
66210
+ newStatus: "done",
66211
+ unblockedCount: unblockedTasks.length
66212
+ }).catch(() => {
66213
+ });
66214
+ } catch {
66215
+ }
65204
66216
  return {
65205
66217
  task,
65206
66218
  ...autoCompleted.length > 0 && { autoCompleted },
@@ -66038,7 +67050,7 @@ __export(nexus_exports, {
66038
67050
  criticalPath: () => criticalPath,
66039
67051
  discoverRelated: () => discoverRelated,
66040
67052
  executeTransfer: () => executeTransfer,
66041
- extractKeywords: () => extractKeywords3,
67053
+ extractKeywords: () => extractKeywords4,
66042
67054
  generateProjectHash: () => generateProjectHash,
66043
67055
  getCurrentProject: () => getCurrentProject,
66044
67056
  getNexusCacheDir: () => getNexusCacheDir,
@@ -70464,6 +71476,7 @@ __export(research_exports, {
70464
71476
  bulkLink: () => bulkLink,
70465
71477
  compactManifest: () => compactManifest,
70466
71478
  consolidateMemories: () => consolidateMemories,
71479
+ correlateOutcomes: () => correlateOutcomes,
70467
71480
  detectContradictions: () => detectContradictions,
70468
71481
  ensureFts5Tables: () => ensureFts5Tables,
70469
71482
  fetchBrainEntries: () => fetchBrainEntries,
@@ -70474,6 +71487,7 @@ __export(research_exports, {
70474
71487
  getLinkedLearnings: () => getLinkedLearnings,
70475
71488
  getLinkedPatterns: () => getLinkedPatterns,
70476
71489
  getMemoryLinks: () => getMemoryLinks,
71490
+ getMemoryQualityReport: () => getMemoryQualityReport,
70477
71491
  getTaskLinks: () => getTaskLinks,
70478
71492
  hybridSearch: () => hybridSearch,
70479
71493
  learningStats: () => learningStats,
@@ -70515,6 +71529,7 @@ __export(research_exports, {
70515
71529
  storePattern: () => storePattern,
70516
71530
  storeVerifiedCandidate: () => storeVerifiedCandidate,
70517
71531
  timelineBrain: () => timelineBrain,
71532
+ trackMemoryUsage: () => trackMemoryUsage,
70518
71533
  unlinkMemoryFromTask: () => unlinkMemoryFromTask,
70519
71534
  updateDecisionOutcome: () => updateDecisionOutcome,
70520
71535
  updateResearch: () => updateResearch,
@@ -90884,6 +91899,22 @@ async function memoryGraphRemove(params, projectRoot) {
90884
91899
  };
90885
91900
  }
90886
91901
  }
91902
+ async function memoryQualityReport(projectRoot) {
91903
+ try {
91904
+ const root = resolveRoot(projectRoot);
91905
+ const { getMemoryQualityReport: getMemoryQualityReport2 } = await Promise.resolve().then(() => (init_quality_feedback(), quality_feedback_exports));
91906
+ const report = await getMemoryQualityReport2(root);
91907
+ return { success: true, data: report };
91908
+ } catch (error48) {
91909
+ return {
91910
+ success: false,
91911
+ error: {
91912
+ code: "E_QUALITY_REPORT",
91913
+ message: error48 instanceof Error ? error48.message : String(error48)
91914
+ }
91915
+ };
91916
+ }
91917
+ }
90887
91918
  var init_engine_compat = __esm({
90888
91919
  "packages/core/src/memory/engine-compat.ts"() {
90889
91920
  "use strict";
@@ -109522,12 +110553,12 @@ import { join as join119 } from "node:path";
109522
110553
  function typedAll4(db, sql14, ...params) {
109523
110554
  return db.prepare(sql14).all(...params);
109524
110555
  }
109525
- function typedGet(db, sql14, ...params) {
110556
+ function typedGet2(db, sql14, ...params) {
109526
110557
  return db.prepare(sql14).get(...params);
109527
110558
  }
109528
110559
  function queryIndexMeta(db, projectId) {
109529
110560
  try {
109530
- const nodeMeta = typedGet(
110561
+ const nodeMeta = typedGet2(
109531
110562
  db,
109532
110563
  `SELECT COUNT(*) as total_nodes,
109533
110564
  COUNT(CASE WHEN kind = 'file' THEN 1 END) as file_count,
@@ -109536,7 +110567,7 @@ function queryIndexMeta(db, projectId) {
109536
110567
  WHERE project_id = ?`,
109537
110568
  projectId
109538
110569
  );
109539
- const relMeta = typedGet(
110570
+ const relMeta = typedGet2(
109540
110571
  db,
109541
110572
  `SELECT COUNT(*) as total_relations
109542
110573
  FROM nexus_relations
@@ -109631,7 +110662,7 @@ function queryCommunities(db, projectId, limit = 6) {
109631
110662
  }
109632
110663
  function queryProcessCount(db, projectId) {
109633
110664
  try {
109634
- const row = typedGet(
110665
+ const row = typedGet2(
109635
110666
  db,
109636
110667
  `SELECT COUNT(*) as count
109637
110668
  FROM nexus_nodes
@@ -111232,6 +112263,7 @@ __export(internal_exports, {
111232
112263
  coreValidateProtocol: () => coreValidateProtocol,
111233
112264
  coreValidateSchema: () => coreValidateSchema,
111234
112265
  coreValidateTask: () => coreValidateTask,
112266
+ correlateOutcomes: () => correlateOutcomes,
111235
112267
  createBackup: () => createBackup,
111236
112268
  createConduit: () => createConduit,
111237
112269
  createDataAccessor: () => createDataAccessor,
@@ -111384,6 +112416,7 @@ __export(internal_exports, {
111384
112416
  getLinksByTaskId: () => getLinksByTaskId,
111385
112417
  getLogDir: () => getLogDir,
111386
112418
  getLogger: () => getLogger,
112419
+ getMemoryQualityReport: () => getMemoryQualityReport,
111387
112420
  getMigrationStatus: () => getMigrationStatus2,
111388
112421
  getNativeDb: () => getNativeDb,
111389
112422
  getNativeOperations: () => getNativeOperations,
@@ -111554,6 +112587,7 @@ __export(internal_exports, {
111554
112587
  memoryPatternFind: () => memoryPatternFind,
111555
112588
  memoryPatternStats: () => memoryPatternStats,
111556
112589
  memoryPatternStore: () => memoryPatternStore,
112590
+ memoryQualityReport: () => memoryQualityReport,
111557
112591
  memoryReasonSimilar: () => memoryReasonSimilar,
111558
112592
  memoryReasonWhy: () => memoryReasonWhy,
111559
112593
  memorySearchHybrid: () => memorySearchHybrid,
@@ -111766,6 +112800,7 @@ __export(internal_exports, {
111766
112800
  tokenUsageMethodSchema: () => tokenUsageMethodSchema,
111767
112801
  tokenUsageTransportSchema: () => tokenUsageTransportSchema,
111768
112802
  touchLink: () => touchLink,
112803
+ trackMemoryUsage: () => trackMemoryUsage,
111769
112804
  ui: () => ui_exports,
111770
112805
  uncancelTask: () => uncancelTask,
111771
112806
  unpackBundle: () => unpackBundle,
@@ -111853,6 +112888,7 @@ var init_internal = __esm({
111853
112888
  init_claude_mem_migration();
111854
112889
  init_engine_compat();
111855
112890
  init_pipeline_manifest_sqlite();
112891
+ init_quality_feedback();
111856
112892
  init_token_service();
111857
112893
  init_deps();
111858
112894
  init_discover();
@@ -115018,6 +116054,66 @@ var init_registry5 = __esm({
115018
116054
  sessionRequired: false,
115019
116055
  requiredParams: []
115020
116056
  },
116057
+ {
116058
+ gateway: "query",
116059
+ domain: "memory",
116060
+ operation: "quality",
116061
+ description: "Memory quality report: retrieval stats, noise ratio, tier distribution",
116062
+ tier: 1,
116063
+ idempotent: true,
116064
+ sessionRequired: false,
116065
+ requiredParams: []
116066
+ },
116067
+ {
116068
+ gateway: "query",
116069
+ domain: "memory",
116070
+ operation: "code.links",
116071
+ description: "List code_reference edges connecting memories to nexus code symbols",
116072
+ tier: 1,
116073
+ idempotent: true,
116074
+ sessionRequired: false,
116075
+ requiredParams: []
116076
+ },
116077
+ {
116078
+ gateway: "query",
116079
+ domain: "memory",
116080
+ operation: "code.memories-for-code",
116081
+ description: "Find memories linked to a code symbol via code_reference edges",
116082
+ tier: 1,
116083
+ idempotent: true,
116084
+ sessionRequired: false,
116085
+ requiredParams: ["symbol"]
116086
+ },
116087
+ {
116088
+ gateway: "query",
116089
+ domain: "memory",
116090
+ operation: "code.for-memory",
116091
+ description: "Find code symbols linked to a memory entry via code_reference edges",
116092
+ tier: 1,
116093
+ idempotent: true,
116094
+ sessionRequired: false,
116095
+ requiredParams: ["memoryId"]
116096
+ },
116097
+ {
116098
+ gateway: "mutate",
116099
+ domain: "memory",
116100
+ operation: "code.link",
116101
+ description: "Create code_reference edge from memory to nexus symbol",
116102
+ tier: 1,
116103
+ idempotent: false,
116104
+ sessionRequired: false,
116105
+ requiredParams: ["memoryId", "codeSymbol"]
116106
+ },
116107
+ {
116108
+ gateway: "mutate",
116109
+ domain: "memory",
116110
+ operation: "code.auto-link",
116111
+ description: "Scan memories for entity references and auto-link to nexus nodes",
116112
+ tier: 1,
116113
+ idempotent: true,
116114
+ sessionRequired: false,
116115
+ requiredParams: []
116116
+ },
115021
116117
  {
115022
116118
  gateway: "mutate",
115023
116119
  domain: "pipeline",
@@ -118039,7 +119135,31 @@ async function orchestrateSpawnExecute(taskId, adapterId, protocolType, projectR
118039
119135
  }
118040
119136
  } catch {
118041
119137
  }
119138
+ try {
119139
+ const { hooks: hooks2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
119140
+ await hooks2.dispatch("SubagentStart", cwd, {
119141
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
119142
+ taskId,
119143
+ agentId: cleoSpawnContext.options?.preferredAgent ?? `worker-${taskId}`,
119144
+ role: protocolType || "worker",
119145
+ providerId: adapter.providerId
119146
+ }).catch(() => {
119147
+ });
119148
+ } catch {
119149
+ }
118042
119150
  const result = await adapter.spawn(cleoSpawnContext);
119151
+ try {
119152
+ const { hooks: hooks2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
119153
+ await hooks2.dispatch("SubagentStop", cwd, {
119154
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
119155
+ taskId,
119156
+ agentId: cleoSpawnContext.options?.preferredAgent ?? `worker-${taskId}`,
119157
+ status: result.status,
119158
+ instanceId: result.instanceId
119159
+ }).catch(() => {
119160
+ });
119161
+ } catch {
119162
+ }
118043
119163
  void sendConduitEvent(cwd, "cleo-core", {
118044
119164
  event: "agent.spawned",
118045
119165
  taskId,
@@ -123521,6 +124641,33 @@ var init_memory2 = __esm({
123521
124641
  );
123522
124642
  return wrapResult(result, "query", "memory", operation, startTime);
123523
124643
  }
124644
+ case "quality": {
124645
+ const result = await memoryQualityReport(projectRoot);
124646
+ return wrapResult(result, "query", "memory", operation, startTime);
124647
+ }
124648
+ case "code.links": {
124649
+ const { listCodeLinks: listCodeLinks2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
124650
+ const result = await listCodeLinks2(projectRoot);
124651
+ return wrapResult(result, "query", "memory", operation, startTime);
124652
+ }
124653
+ case "code.memories-for-code": {
124654
+ const symbol2 = params?.symbol;
124655
+ if (!symbol2) {
124656
+ return errorResult("query", "memory", operation, "E_INVALID_INPUT", "symbol is required", startTime);
124657
+ }
124658
+ const { queryMemoriesForCode: queryMemoriesForCode2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
124659
+ const result = await queryMemoriesForCode2(projectRoot, symbol2);
124660
+ return wrapResult({ success: true, data: result }, "query", "memory", operation, startTime);
124661
+ }
124662
+ case "code.for-memory": {
124663
+ const memoryId = params?.memoryId;
124664
+ if (!memoryId) {
124665
+ return errorResult("query", "memory", operation, "E_INVALID_INPUT", "memoryId is required", startTime);
124666
+ }
124667
+ const { queryCodeForMemory: queryCodeForMemory2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
124668
+ const result = await queryCodeForMemory2(projectRoot, memoryId);
124669
+ return wrapResult({ success: true, data: result }, "query", "memory", operation, startTime);
124670
+ }
123524
124671
  default:
123525
124672
  return unsupportedOp("query", "memory", operation, startTime);
123526
124673
  }
@@ -123691,6 +124838,21 @@ var init_memory2 = __esm({
123691
124838
  );
123692
124839
  return wrapResult(result, "mutate", "memory", operation, startTime);
123693
124840
  }
124841
+ case "code.link": {
124842
+ const memoryId = params?.memoryId;
124843
+ const codeSymbol = params?.codeSymbol;
124844
+ if (!memoryId || !codeSymbol) {
124845
+ return errorResult("mutate", "memory", operation, "E_INVALID_INPUT", "memoryId and codeSymbol are required", startTime);
124846
+ }
124847
+ const { linkMemoryToCode: linkMemoryToCode2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
124848
+ const linked = await linkMemoryToCode2(projectRoot, memoryId, codeSymbol);
124849
+ return wrapResult({ success: true, data: { linked } }, "mutate", "memory", operation, startTime);
124850
+ }
124851
+ case "code.auto-link": {
124852
+ const { autoLinkMemories: autoLinkMemories2 } = await Promise.resolve().then(() => (init_internal(), internal_exports));
124853
+ const result = await autoLinkMemories2(projectRoot);
124854
+ return wrapResult({ success: true, data: result }, "mutate", "memory", operation, startTime);
124855
+ }
123694
124856
  default:
123695
124857
  return unsupportedOp("mutate", "memory", operation, startTime);
123696
124858
  }
@@ -123723,7 +124885,11 @@ var init_memory2 = __esm({
123723
124885
  "graph.stats",
123724
124886
  "reason.why",
123725
124887
  "reason.similar",
123726
- "search.hybrid"
124888
+ "search.hybrid",
124889
+ "quality",
124890
+ "code.links",
124891
+ "code.memories-for-code",
124892
+ "code.for-memory"
123727
124893
  ],
123728
124894
  mutate: [
123729
124895
  "observe",
@@ -123732,7 +124898,9 @@ var init_memory2 = __esm({
123732
124898
  "learning.store",
123733
124899
  "link",
123734
124900
  "graph.add",
123735
- "graph.remove"
124901
+ "graph.remove",
124902
+ "code.link",
124903
+ "code.auto-link"
123736
124904
  ]
123737
124905
  };
123738
124906
  }
@@ -133450,6 +134618,66 @@ function registerBrainCommand(program) {
133450
134618
  process.exit(1);
133451
134619
  }
133452
134620
  });
134621
+ brain.command("quality").description(
134622
+ "Show memory quality metrics: retrieval rates, top/never-retrieved entries, quality distribution, and noise ratio."
134623
+ ).option("--json", "Output results as JSON").action(async (opts) => {
134624
+ const root = getProjectRoot();
134625
+ const isJson = !!opts.json;
134626
+ try {
134627
+ const report = await getMemoryQualityReport(root);
134628
+ if (isJson) {
134629
+ console.log(
134630
+ JSON.stringify(
134631
+ {
134632
+ success: true,
134633
+ data: report,
134634
+ meta: { operation: "brain.quality", timestamp: (/* @__PURE__ */ new Date()).toISOString() }
134635
+ },
134636
+ null,
134637
+ 2
134638
+ )
134639
+ );
134640
+ return;
134641
+ }
134642
+ console.log("\nBrain Memory Quality Report");
134643
+ 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\u2550");
134644
+ console.log(` Total retrievals: ${report.totalRetrievals}`);
134645
+ console.log(` Unique entries hit: ${report.uniqueEntriesRetrieved}`);
134646
+ console.log(` Usage rate: ${(report.usageRate * 100).toFixed(1)}%`);
134647
+ console.log(` Noise ratio: ${(report.noiseRatio * 100).toFixed(1)}%`);
134648
+ console.log("\nQuality Distribution");
134649
+ console.log(` Low (<0.3): ${report.qualityDistribution.low}`);
134650
+ console.log(` Med (0.3-0.6): ${report.qualityDistribution.medium}`);
134651
+ console.log(` High (>0.6): ${report.qualityDistribution.high}`);
134652
+ console.log("\nTier Distribution");
134653
+ console.log(` Short: ${report.tierDistribution.short}`);
134654
+ console.log(` Medium: ${report.tierDistribution.medium}`);
134655
+ console.log(` Long: ${report.tierDistribution.long}`);
134656
+ if (report.tierDistribution.unknown > 0) {
134657
+ console.log(` Unknown: ${report.tierDistribution.unknown}`);
134658
+ }
134659
+ if (report.topRetrieved.length > 0) {
134660
+ console.log("\nTop 10 Most Retrieved");
134661
+ for (const e of report.topRetrieved) {
134662
+ console.log(` [${e.citationCount}x] ${e.id} ${e.title.slice(0, 60)}`);
134663
+ }
134664
+ }
134665
+ if (report.neverRetrieved.length > 0) {
134666
+ console.log("\nNever Retrieved (pruning candidates)");
134667
+ for (const e of report.neverRetrieved) {
134668
+ console.log(` q=${e.qualityScore.toFixed(2)} ${e.id} ${e.title.slice(0, 60)}`);
134669
+ }
134670
+ }
134671
+ } catch (err) {
134672
+ const message = err instanceof Error ? err.message : String(err);
134673
+ if (isJson) {
134674
+ console.log(JSON.stringify({ success: false, error: message }));
134675
+ } else {
134676
+ console.error(`Brain quality report failed: ${message}`);
134677
+ }
134678
+ process.exit(1);
134679
+ }
134680
+ });
133453
134681
  }
133454
134682
 
133455
134683
  // packages/cleo/src/cli/commands/briefing.ts
@@ -136519,6 +137747,47 @@ function registerMemoryBrainCommand(program) {
136519
137747
  { command: "memory", operation: "memory.search.hybrid" }
136520
137748
  );
136521
137749
  });
137750
+ memory.command("code-links").description("Show code \u2194 memory connections (code_reference edges between brain and nexus)").option("--limit <n>", "Maximum entries to return (default 100)", parseInt).option("--json", "Output as JSON").action(async (opts) => {
137751
+ await dispatchFromCli(
137752
+ "query",
137753
+ "memory",
137754
+ "code.links",
137755
+ {
137756
+ ...opts["limit"] !== void 0 && { limit: opts["limit"] }
137757
+ },
137758
+ { command: "memory", operation: "memory.code.links" }
137759
+ );
137760
+ });
137761
+ memory.command("code-auto-link").description("Scan brain memory nodes for entity references and auto-link to nexus code nodes").option("--json", "Output as JSON").action(async () => {
137762
+ await dispatchFromCli(
137763
+ "mutate",
137764
+ "memory",
137765
+ "code.auto-link",
137766
+ {},
137767
+ {
137768
+ command: "memory",
137769
+ operation: "memory.code.auto-link"
137770
+ }
137771
+ );
137772
+ });
137773
+ memory.command("code-memories-for-code <symbol>").description("Find brain memory nodes that reference a given nexus code symbol").option("--json", "Output as JSON").action(async (symbol2) => {
137774
+ await dispatchFromCli(
137775
+ "query",
137776
+ "memory",
137777
+ "code.memories-for-code",
137778
+ { symbol: symbol2 },
137779
+ { command: "memory", operation: "memory.code.memories-for-code" }
137780
+ );
137781
+ });
137782
+ memory.command("code-for-memory <memoryId>").description("Find nexus code nodes referenced by a given brain memory entry").option("--json", "Output as JSON").action(async (memoryId) => {
137783
+ await dispatchFromCli(
137784
+ "query",
137785
+ "memory",
137786
+ "code.for-memory",
137787
+ { memoryId },
137788
+ { command: "memory", operation: "memory.code.for-memory" }
137789
+ );
137790
+ });
136522
137791
  }
136523
137792
 
136524
137793
  // packages/cleo/src/cli/commands/migrate-claude-mem.ts