@velvetmonkey/flywheel-memory 2.0.42 → 2.0.43

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 (2) hide show
  1. package/dist/index.js +192 -152
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -3058,7 +3058,7 @@ var DEFAULT_WATCHER_CONFIG = {
3058
3058
  flushMs: 1e3,
3059
3059
  batchSize: 50,
3060
3060
  usePolling: false,
3061
- pollInterval: 3e4
3061
+ pollInterval: 1e4
3062
3062
  };
3063
3063
  function parseWatcherConfig() {
3064
3064
  const debounceMs = parseInt(process.env.FLYWHEEL_DEBOUNCE_MS || "");
@@ -5372,16 +5372,160 @@ function getCooccurrenceBoost(entityName, matchedEntities, cooccurrenceIndex2, r
5372
5372
  return Math.min(boost, MAX_COOCCURRENCE_BOOST);
5373
5373
  }
5374
5374
 
5375
- // src/core/write/wikilinks.ts
5375
+ // src/core/write/edgeWeights.ts
5376
5376
  var moduleStateDb4 = null;
5377
- function setWriteStateDb(stateDb2) {
5377
+ function setEdgeWeightStateDb(stateDb2) {
5378
5378
  moduleStateDb4 = stateDb2;
5379
+ }
5380
+ function buildPathToTargetsMap(stateDb2) {
5381
+ const map = /* @__PURE__ */ new Map();
5382
+ const rows = stateDb2.db.prepare(
5383
+ "SELECT path, name_lower, aliases_json FROM entities"
5384
+ ).all();
5385
+ for (const row of rows) {
5386
+ const targets = /* @__PURE__ */ new Set();
5387
+ targets.add(row.name_lower);
5388
+ if (row.aliases_json) {
5389
+ try {
5390
+ const aliases = JSON.parse(row.aliases_json);
5391
+ for (const alias of aliases) {
5392
+ targets.add(alias.toLowerCase());
5393
+ }
5394
+ } catch {
5395
+ }
5396
+ }
5397
+ map.set(row.path, targets);
5398
+ }
5399
+ return map;
5400
+ }
5401
+ function pathToFallbackTarget(filePath) {
5402
+ return filePath.replace(/\.md$/, "").split("/").pop()?.toLowerCase() ?? filePath.toLowerCase();
5403
+ }
5404
+ function recomputeEdgeWeights(stateDb2) {
5405
+ const start = Date.now();
5406
+ const edges = stateDb2.db.prepare(
5407
+ "SELECT note_path, target FROM note_links"
5408
+ ).all();
5409
+ if (edges.length === 0) {
5410
+ return { edges_updated: 0, duration_ms: Date.now() - start, total_weighted: 0, avg_weight: 0, strong_count: 0 };
5411
+ }
5412
+ const survivalMap = /* @__PURE__ */ new Map();
5413
+ const historyRows = stateDb2.db.prepare(
5414
+ "SELECT note_path, target, edits_survived FROM note_link_history"
5415
+ ).all();
5416
+ for (const row of historyRows) {
5417
+ survivalMap.set(`${row.note_path}\0${row.target}`, row.edits_survived);
5418
+ }
5419
+ const pathToTargets = buildPathToTargetsMap(stateDb2);
5420
+ const targetToPaths = /* @__PURE__ */ new Map();
5421
+ for (const [entityPath, targets] of pathToTargets) {
5422
+ for (const target of targets) {
5423
+ let paths = targetToPaths.get(target);
5424
+ if (!paths) {
5425
+ paths = /* @__PURE__ */ new Set();
5426
+ targetToPaths.set(target, paths);
5427
+ }
5428
+ paths.add(entityPath);
5429
+ }
5430
+ }
5431
+ const sessionRows = stateDb2.db.prepare(
5432
+ `SELECT session_id, note_paths FROM tool_invocations
5433
+ WHERE note_paths IS NOT NULL AND note_paths != '[]'`
5434
+ ).all();
5435
+ const sessionPaths = /* @__PURE__ */ new Map();
5436
+ for (const row of sessionRows) {
5437
+ try {
5438
+ const paths = JSON.parse(row.note_paths);
5439
+ if (!Array.isArray(paths) || paths.length === 0) continue;
5440
+ let existing = sessionPaths.get(row.session_id);
5441
+ if (!existing) {
5442
+ existing = /* @__PURE__ */ new Set();
5443
+ sessionPaths.set(row.session_id, existing);
5444
+ }
5445
+ for (const p of paths) {
5446
+ existing.add(p);
5447
+ }
5448
+ } catch {
5449
+ }
5450
+ }
5451
+ const coSessionCount = /* @__PURE__ */ new Map();
5452
+ const sourceActivityCount = /* @__PURE__ */ new Map();
5453
+ for (const [, paths] of sessionPaths) {
5454
+ const sessionTargets = /* @__PURE__ */ new Set();
5455
+ for (const p of paths) {
5456
+ const targets = pathToTargets.get(p);
5457
+ if (targets) {
5458
+ for (const t of targets) sessionTargets.add(t);
5459
+ } else {
5460
+ sessionTargets.add(pathToFallbackTarget(p));
5461
+ }
5462
+ }
5463
+ for (const edge of edges) {
5464
+ if (paths.has(edge.note_path)) {
5465
+ const srcKey = edge.note_path;
5466
+ sourceActivityCount.set(srcKey, (sourceActivityCount.get(srcKey) ?? 0) + 1);
5467
+ if (sessionTargets.has(edge.target)) {
5468
+ const edgeKey = `${edge.note_path}\0${edge.target}`;
5469
+ coSessionCount.set(edgeKey, (coSessionCount.get(edgeKey) ?? 0) + 1);
5470
+ }
5471
+ }
5472
+ }
5473
+ }
5474
+ const now = Date.now();
5475
+ const update = stateDb2.db.prepare(
5476
+ "UPDATE note_links SET weight = ?, weight_updated_at = ? WHERE note_path = ? AND target = ?"
5477
+ );
5478
+ const tx = stateDb2.db.transaction(() => {
5479
+ for (const edge of edges) {
5480
+ const edgeKey = `${edge.note_path}\0${edge.target}`;
5481
+ const editsSurvived = survivalMap.get(edgeKey) ?? 0;
5482
+ const coSessions = coSessionCount.get(edgeKey) ?? 0;
5483
+ const sourceAccess = sourceActivityCount.get(edge.note_path) ?? 0;
5484
+ const weight = 1 + editsSurvived * 0.5 + Math.min(coSessions * 0.5, 3) + Math.min(sourceAccess * 0.2, 2);
5485
+ update.run(Math.round(weight * 1e3) / 1e3, now, edge.note_path, edge.target);
5486
+ }
5487
+ });
5488
+ tx();
5489
+ const stats = stateDb2.db.prepare(`
5490
+ SELECT
5491
+ COUNT(*) as total_weighted,
5492
+ AVG(weight) as avg_weight,
5493
+ SUM(CASE WHEN weight > 3.0 THEN 1 ELSE 0 END) as strong_count
5494
+ FROM note_links
5495
+ WHERE weight > 1.0
5496
+ `).get();
5497
+ return {
5498
+ edges_updated: edges.length,
5499
+ duration_ms: Date.now() - start,
5500
+ total_weighted: stats?.total_weighted ?? 0,
5501
+ avg_weight: Math.round((stats?.avg_weight ?? 0) * 100) / 100,
5502
+ strong_count: stats?.strong_count ?? 0
5503
+ };
5504
+ }
5505
+ function getEntityEdgeWeightMap(stateDb2) {
5506
+ const rows = stateDb2.db.prepare(`
5507
+ SELECT LOWER(target) as target_lower, AVG(weight) as avg_weight
5508
+ FROM note_links
5509
+ WHERE weight > 1.0
5510
+ GROUP BY LOWER(target)
5511
+ `).all();
5512
+ const map = /* @__PURE__ */ new Map();
5513
+ for (const row of rows) {
5514
+ map.set(row.target_lower, row.avg_weight);
5515
+ }
5516
+ return map;
5517
+ }
5518
+
5519
+ // src/core/write/wikilinks.ts
5520
+ var moduleStateDb5 = null;
5521
+ function setWriteStateDb(stateDb2) {
5522
+ moduleStateDb5 = stateDb2;
5379
5523
  setGitStateDb(stateDb2);
5380
5524
  setHintsStateDb(stateDb2);
5381
5525
  setRecencyStateDb(stateDb2);
5382
5526
  }
5383
5527
  function getWriteStateDb() {
5384
- return moduleStateDb4;
5528
+ return moduleStateDb5;
5385
5529
  }
5386
5530
  var moduleConfig = null;
5387
5531
  var ALL_IMPLICIT_PATTERNS = ["proper-nouns", "single-caps", "quoted-terms", "camel-case", "acronyms"];
@@ -5436,9 +5580,9 @@ var DEFAULT_EXCLUDE_FOLDERS = [
5436
5580
  ];
5437
5581
  async function initializeEntityIndex(vaultPath2) {
5438
5582
  try {
5439
- if (moduleStateDb4) {
5583
+ if (moduleStateDb5) {
5440
5584
  try {
5441
- const dbIndex = getEntityIndexFromDb(moduleStateDb4);
5585
+ const dbIndex = getEntityIndexFromDb(moduleStateDb5);
5442
5586
  if (dbIndex._metadata.total_entities > 0) {
5443
5587
  entityIndex = dbIndex;
5444
5588
  indexReady = true;
@@ -5466,9 +5610,9 @@ async function rebuildIndex(vaultPath2) {
5466
5610
  lastLoadedAt = Date.now();
5467
5611
  const entityDuration = Date.now() - startTime;
5468
5612
  console.error(`[Flywheel] Entity index built: ${entityIndex._metadata.total_entities} entities in ${entityDuration}ms`);
5469
- if (moduleStateDb4) {
5613
+ if (moduleStateDb5) {
5470
5614
  try {
5471
- moduleStateDb4.replaceAllEntities(entityIndex);
5615
+ moduleStateDb5.replaceAllEntities(entityIndex);
5472
5616
  console.error(`[Flywheel] Saved entities to StateDb`);
5473
5617
  } catch (e) {
5474
5618
  console.error(`[Flywheel] Failed to save entities to StateDb: ${e}`);
@@ -5505,14 +5649,14 @@ function isEntityIndexReady() {
5505
5649
  return indexReady && entityIndex !== null;
5506
5650
  }
5507
5651
  function checkAndRefreshIfStale() {
5508
- if (!moduleStateDb4 || !indexReady) return;
5652
+ if (!moduleStateDb5 || !indexReady) return;
5509
5653
  try {
5510
- const metadata = getStateDbMetadata(moduleStateDb4);
5654
+ const metadata = getStateDbMetadata(moduleStateDb5);
5511
5655
  if (!metadata.entitiesBuiltAt) return;
5512
5656
  const dbBuiltAt = new Date(metadata.entitiesBuiltAt).getTime();
5513
5657
  if (dbBuiltAt > lastLoadedAt) {
5514
5658
  console.error("[Flywheel] Entity index stale, reloading from StateDb...");
5515
- const dbIndex = getEntityIndexFromDb(moduleStateDb4);
5659
+ const dbIndex = getEntityIndexFromDb(moduleStateDb5);
5516
5660
  if (dbIndex._metadata.total_entities > 0) {
5517
5661
  entityIndex = dbIndex;
5518
5662
  lastLoadedAt = Date.now();
@@ -5554,11 +5698,11 @@ function processWikilinks(content, notePath, existingContent) {
5554
5698
  }
5555
5699
  let entities = getAllEntities(entityIndex);
5556
5700
  console.error(`[Flywheel:DEBUG] Processing wikilinks with ${entities.length} entities`);
5557
- if (moduleStateDb4) {
5701
+ if (moduleStateDb5) {
5558
5702
  const folder = notePath ? notePath.split("/")[0] : void 0;
5559
5703
  entities = entities.filter((e) => {
5560
5704
  const name = getEntityName2(e);
5561
- return !isSuppressed(moduleStateDb4, name, folder);
5705
+ return !isSuppressed(moduleStateDb5, name, folder);
5562
5706
  });
5563
5707
  }
5564
5708
  const sortedEntities = sortEntitiesByPriority(entities, notePath);
@@ -5638,8 +5782,8 @@ function maybeApplyWikilinks(content, skipWikilinks, notePath, existingContent)
5638
5782
  checkAndRefreshIfStale();
5639
5783
  const result = processWikilinks(content, notePath, existingContent);
5640
5784
  if (result.linksAdded > 0) {
5641
- if (moduleStateDb4 && notePath) {
5642
- trackWikilinkApplications(moduleStateDb4, notePath, result.linkedEntities);
5785
+ if (moduleStateDb5 && notePath) {
5786
+ trackWikilinkApplications(moduleStateDb5, notePath, result.linkedEntities);
5643
5787
  }
5644
5788
  const implicitCount = result.implicitEntities?.length ?? 0;
5645
5789
  const implicitInfo = implicitCount > 0 ? ` + ${implicitCount} implicit: ${result.implicitEntities.join(", ")}` : "";
@@ -5993,6 +6137,11 @@ function scoreEntity(entity, contentTokens, contentStems, config) {
5993
6137
  }
5994
6138
  return score;
5995
6139
  }
6140
+ function getEdgeWeightBoostScore(entityName, map) {
6141
+ const avgWeight = map.get(entityName.toLowerCase());
6142
+ if (!avgWeight) return 0;
6143
+ return Math.min((avgWeight - 1) * 2, 4);
6144
+ }
5996
6145
  async function suggestRelatedLinks(content, options = {}) {
5997
6146
  const {
5998
6147
  maxSuggestions = 3,
@@ -6036,7 +6185,8 @@ async function suggestRelatedLinks(content, options = {}) {
6036
6185
  }
6037
6186
  const linkedEntities = excludeLinked ? extractLinkedEntities(content) : /* @__PURE__ */ new Set();
6038
6187
  const noteFolder = notePath ? notePath.split("/")[0] : void 0;
6039
- const feedbackBoosts = moduleStateDb4 ? getAllFeedbackBoosts(moduleStateDb4, noteFolder) : /* @__PURE__ */ new Map();
6188
+ const feedbackBoosts = moduleStateDb5 ? getAllFeedbackBoosts(moduleStateDb5, noteFolder) : /* @__PURE__ */ new Map();
6189
+ const edgeWeightMap = moduleStateDb5 ? getEntityEdgeWeightMap(moduleStateDb5) : /* @__PURE__ */ new Map();
6040
6190
  const scoredEntities = [];
6041
6191
  const directlyMatchedEntities = /* @__PURE__ */ new Set();
6042
6192
  const entitiesWithContentMatch = /* @__PURE__ */ new Set();
@@ -6069,6 +6219,8 @@ async function suggestRelatedLinks(content, options = {}) {
6069
6219
  score += layerHubBoost;
6070
6220
  const layerFeedbackAdj = disabled.has("feedback") ? 0 : feedbackBoosts.get(entityName) ?? 0;
6071
6221
  score += layerFeedbackAdj;
6222
+ const layerEdgeWeightBoost = disabled.has("edge_weight") ? 0 : getEdgeWeightBoostScore(entityName, edgeWeightMap);
6223
+ score += layerEdgeWeightBoost;
6072
6224
  if (score > 0) {
6073
6225
  directlyMatchedEntities.add(entityName);
6074
6226
  }
@@ -6086,7 +6238,8 @@ async function suggestRelatedLinks(content, options = {}) {
6086
6238
  recencyBoost: layerRecencyBoost,
6087
6239
  crossFolderBoost: layerCrossFolderBoost,
6088
6240
  hubBoost: layerHubBoost,
6089
- feedbackAdjustment: layerFeedbackAdj
6241
+ feedbackAdjustment: layerFeedbackAdj,
6242
+ edgeWeightBoost: layerEdgeWeightBoost
6090
6243
  }
6091
6244
  });
6092
6245
  }
@@ -6119,7 +6272,8 @@ async function suggestRelatedLinks(content, options = {}) {
6119
6272
  const crossFolderBoost = disabled.has("cross_folder") ? 0 : notePath && entity.path ? getCrossFolderBoost(entity.path, notePath) : 0;
6120
6273
  const hubBoost = disabled.has("hub_boost") ? 0 : getHubBoost(entity);
6121
6274
  const feedbackAdj = disabled.has("feedback") ? 0 : feedbackBoosts.get(entityName) ?? 0;
6122
- const totalBoost = boost + typeBoost + contextBoost + recencyBoostVal + crossFolderBoost + hubBoost + feedbackAdj;
6275
+ const edgeWeightBoost = disabled.has("edge_weight") ? 0 : getEdgeWeightBoostScore(entityName, edgeWeightMap);
6276
+ const totalBoost = boost + typeBoost + contextBoost + recencyBoostVal + crossFolderBoost + hubBoost + feedbackAdj + edgeWeightBoost;
6123
6277
  if (totalBoost >= adaptiveMinScore) {
6124
6278
  scoredEntities.push({
6125
6279
  name: entityName,
@@ -6134,7 +6288,8 @@ async function suggestRelatedLinks(content, options = {}) {
6134
6288
  recencyBoost: recencyBoostVal,
6135
6289
  crossFolderBoost,
6136
6290
  hubBoost,
6137
- feedbackAdjustment: feedbackAdj
6291
+ feedbackAdjustment: feedbackAdj,
6292
+ edgeWeightBoost
6138
6293
  }
6139
6294
  });
6140
6295
  }
@@ -6172,7 +6327,8 @@ async function suggestRelatedLinks(content, options = {}) {
6172
6327
  const layerHubBoost = disabled.has("hub_boost") ? 0 : getHubBoost(entity);
6173
6328
  const layerCrossFolderBoost = disabled.has("cross_folder") ? 0 : notePath && entity.path ? getCrossFolderBoost(entity.path, notePath) : 0;
6174
6329
  const layerFeedbackAdj = disabled.has("feedback") ? 0 : feedbackBoosts.get(match.entityName) ?? 0;
6175
- const totalScore = boost + layerTypeBoost + layerContextBoost + layerHubBoost + layerCrossFolderBoost + layerFeedbackAdj;
6330
+ const layerEdgeWeightBoost = disabled.has("edge_weight") ? 0 : getEdgeWeightBoostScore(match.entityName, edgeWeightMap);
6331
+ const totalScore = boost + layerTypeBoost + layerContextBoost + layerHubBoost + layerCrossFolderBoost + layerFeedbackAdj + layerEdgeWeightBoost;
6176
6332
  if (totalScore >= adaptiveMinScore) {
6177
6333
  scoredEntities.push({
6178
6334
  name: match.entityName,
@@ -6188,7 +6344,8 @@ async function suggestRelatedLinks(content, options = {}) {
6188
6344
  crossFolderBoost: layerCrossFolderBoost,
6189
6345
  hubBoost: layerHubBoost,
6190
6346
  feedbackAdjustment: layerFeedbackAdj,
6191
- semanticBoost: boost
6347
+ semanticBoost: boost,
6348
+ edgeWeightBoost: layerEdgeWeightBoost
6192
6349
  }
6193
6350
  });
6194
6351
  entitiesWithContentMatch.add(match.entityName);
@@ -6213,15 +6370,15 @@ async function suggestRelatedLinks(content, options = {}) {
6213
6370
  }
6214
6371
  return 0;
6215
6372
  });
6216
- if (moduleStateDb4 && notePath) {
6373
+ if (moduleStateDb5 && notePath) {
6217
6374
  try {
6218
6375
  const now = Date.now();
6219
- const insertStmt = moduleStateDb4.db.prepare(`
6376
+ const insertStmt = moduleStateDb5.db.prepare(`
6220
6377
  INSERT OR IGNORE INTO suggestion_events
6221
6378
  (timestamp, note_path, entity, total_score, breakdown_json, threshold, passed, strictness, applied, pipeline_event_id)
6222
6379
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, NULL)
6223
6380
  `);
6224
- const persistTransaction = moduleStateDb4.db.transaction(() => {
6381
+ const persistTransaction = moduleStateDb5.db.transaction(() => {
6225
6382
  for (const e of relevantEntities) {
6226
6383
  insertStmt.run(
6227
6384
  now,
@@ -6268,7 +6425,7 @@ async function suggestRelatedLinks(content, options = {}) {
6268
6425
  suffix
6269
6426
  };
6270
6427
  if (detail) {
6271
- const feedbackStats = moduleStateDb4 ? getEntityStats(moduleStateDb4) : [];
6428
+ const feedbackStats = moduleStateDb5 ? getEntityStats(moduleStateDb5) : [];
6272
6429
  const feedbackMap = new Map(feedbackStats.map((s) => [s.entity, s]));
6273
6430
  result.detailed = topEntries.map((e) => {
6274
6431
  const fb = feedbackMap.get(e.name);
@@ -6287,9 +6444,9 @@ async function suggestRelatedLinks(content, options = {}) {
6287
6444
  return result;
6288
6445
  }
6289
6446
  function detectAliasCollisions(noteName, aliases = []) {
6290
- if (!moduleStateDb4) return [];
6447
+ if (!moduleStateDb5) return [];
6291
6448
  const collisions = [];
6292
- const nameAsAlias = getEntitiesByAlias(moduleStateDb4, noteName);
6449
+ const nameAsAlias = getEntitiesByAlias(moduleStateDb5, noteName);
6293
6450
  for (const entity of nameAsAlias) {
6294
6451
  if (entity.name.toLowerCase() === noteName.toLowerCase()) continue;
6295
6452
  collisions.push({
@@ -6303,7 +6460,7 @@ function detectAliasCollisions(noteName, aliases = []) {
6303
6460
  });
6304
6461
  }
6305
6462
  for (const alias of aliases) {
6306
- const existingByName = getEntityByName(moduleStateDb4, alias);
6463
+ const existingByName = getEntityByName(moduleStateDb5, alias);
6307
6464
  if (existingByName && existingByName.name.toLowerCase() !== noteName.toLowerCase()) {
6308
6465
  collisions.push({
6309
6466
  term: alias,
@@ -6315,7 +6472,7 @@ function detectAliasCollisions(noteName, aliases = []) {
6315
6472
  }
6316
6473
  });
6317
6474
  }
6318
- const existingByAlias = getEntitiesByAlias(moduleStateDb4, alias);
6475
+ const existingByAlias = getEntitiesByAlias(moduleStateDb5, alias);
6319
6476
  for (const entity of existingByAlias) {
6320
6477
  if (entity.name.toLowerCase() === noteName.toLowerCase()) continue;
6321
6478
  if (existingByName && existingByName.name.toLowerCase() === entity.name.toLowerCase()) continue;
@@ -6339,8 +6496,8 @@ function suggestAliases(noteName, existingAliases = [], category) {
6339
6496
  function isSafe(alias) {
6340
6497
  if (existingLower.has(alias.toLowerCase())) return false;
6341
6498
  if (alias.toLowerCase() === noteName.toLowerCase()) return false;
6342
- if (!moduleStateDb4) return true;
6343
- const existing = getEntityByName(moduleStateDb4, alias);
6499
+ if (!moduleStateDb5) return true;
6500
+ const existing = getEntityByName(moduleStateDb5, alias);
6344
6501
  return !existing;
6345
6502
  }
6346
6503
  const inferredCategory = category || inferCategoryFromName(noteName);
@@ -6382,8 +6539,8 @@ function inferCategoryFromName(name) {
6382
6539
  }
6383
6540
  async function checkPreflightSimilarity(noteName) {
6384
6541
  const result = { similarEntities: [] };
6385
- if (!moduleStateDb4) return result;
6386
- const exact = getEntityByName(moduleStateDb4, noteName);
6542
+ if (!moduleStateDb5) return result;
6543
+ const exact = getEntityByName(moduleStateDb5, noteName);
6387
6544
  if (exact) {
6388
6545
  result.existingEntity = {
6389
6546
  name: exact.name,
@@ -6393,7 +6550,7 @@ async function checkPreflightSimilarity(noteName) {
6393
6550
  }
6394
6551
  const ftsNames = /* @__PURE__ */ new Set();
6395
6552
  try {
6396
- const searchResults = searchEntitiesDb(moduleStateDb4, noteName, 5);
6553
+ const searchResults = searchEntitiesDb(moduleStateDb5, noteName, 5);
6397
6554
  for (const sr of searchResults) {
6398
6555
  if (sr.name.toLowerCase() === noteName.toLowerCase()) continue;
6399
6556
  ftsNames.add(sr.name.toLowerCase());
@@ -6414,7 +6571,7 @@ async function checkPreflightSimilarity(noteName) {
6414
6571
  if (match.similarity < 0.85) continue;
6415
6572
  if (match.entityName.toLowerCase() === noteName.toLowerCase()) continue;
6416
6573
  if (ftsNames.has(match.entityName.toLowerCase())) continue;
6417
- const entity = getEntityByName(moduleStateDb4, match.entityName);
6574
+ const entity = getEntityByName(moduleStateDb5, match.entityName);
6418
6575
  if (entity) {
6419
6576
  result.similarEntities.push({
6420
6577
  name: entity.name,
@@ -9202,123 +9359,6 @@ function suggestEntityAliases(stateDb2, folder) {
9202
9359
  return suggestions;
9203
9360
  }
9204
9361
 
9205
- // src/core/write/edgeWeights.ts
9206
- var moduleStateDb5 = null;
9207
- function setEdgeWeightStateDb(stateDb2) {
9208
- moduleStateDb5 = stateDb2;
9209
- }
9210
- function buildPathToTargetsMap(stateDb2) {
9211
- const map = /* @__PURE__ */ new Map();
9212
- const rows = stateDb2.db.prepare(
9213
- "SELECT path, name_lower, aliases_json FROM entities"
9214
- ).all();
9215
- for (const row of rows) {
9216
- const targets = /* @__PURE__ */ new Set();
9217
- targets.add(row.name_lower);
9218
- if (row.aliases_json) {
9219
- try {
9220
- const aliases = JSON.parse(row.aliases_json);
9221
- for (const alias of aliases) {
9222
- targets.add(alias.toLowerCase());
9223
- }
9224
- } catch {
9225
- }
9226
- }
9227
- map.set(row.path, targets);
9228
- }
9229
- return map;
9230
- }
9231
- function pathToFallbackTarget(filePath) {
9232
- return filePath.replace(/\.md$/, "").split("/").pop()?.toLowerCase() ?? filePath.toLowerCase();
9233
- }
9234
- function recomputeEdgeWeights(stateDb2) {
9235
- const start = Date.now();
9236
- const edges = stateDb2.db.prepare(
9237
- "SELECT note_path, target FROM note_links"
9238
- ).all();
9239
- if (edges.length === 0) {
9240
- return { edges_updated: 0, duration_ms: Date.now() - start };
9241
- }
9242
- const survivalMap = /* @__PURE__ */ new Map();
9243
- const historyRows = stateDb2.db.prepare(
9244
- "SELECT note_path, target, edits_survived FROM note_link_history"
9245
- ).all();
9246
- for (const row of historyRows) {
9247
- survivalMap.set(`${row.note_path}\0${row.target}`, row.edits_survived);
9248
- }
9249
- const pathToTargets = buildPathToTargetsMap(stateDb2);
9250
- const targetToPaths = /* @__PURE__ */ new Map();
9251
- for (const [entityPath, targets] of pathToTargets) {
9252
- for (const target of targets) {
9253
- let paths = targetToPaths.get(target);
9254
- if (!paths) {
9255
- paths = /* @__PURE__ */ new Set();
9256
- targetToPaths.set(target, paths);
9257
- }
9258
- paths.add(entityPath);
9259
- }
9260
- }
9261
- const sessionRows = stateDb2.db.prepare(
9262
- `SELECT session_id, note_paths FROM tool_invocations
9263
- WHERE note_paths IS NOT NULL AND note_paths != '[]'`
9264
- ).all();
9265
- const sessionPaths = /* @__PURE__ */ new Map();
9266
- for (const row of sessionRows) {
9267
- try {
9268
- const paths = JSON.parse(row.note_paths);
9269
- if (!Array.isArray(paths) || paths.length === 0) continue;
9270
- let existing = sessionPaths.get(row.session_id);
9271
- if (!existing) {
9272
- existing = /* @__PURE__ */ new Set();
9273
- sessionPaths.set(row.session_id, existing);
9274
- }
9275
- for (const p of paths) {
9276
- existing.add(p);
9277
- }
9278
- } catch {
9279
- }
9280
- }
9281
- const coSessionCount = /* @__PURE__ */ new Map();
9282
- const sourceActivityCount = /* @__PURE__ */ new Map();
9283
- for (const [, paths] of sessionPaths) {
9284
- const sessionTargets = /* @__PURE__ */ new Set();
9285
- for (const p of paths) {
9286
- const targets = pathToTargets.get(p);
9287
- if (targets) {
9288
- for (const t of targets) sessionTargets.add(t);
9289
- } else {
9290
- sessionTargets.add(pathToFallbackTarget(p));
9291
- }
9292
- }
9293
- for (const edge of edges) {
9294
- if (paths.has(edge.note_path)) {
9295
- const srcKey = edge.note_path;
9296
- sourceActivityCount.set(srcKey, (sourceActivityCount.get(srcKey) ?? 0) + 1);
9297
- if (sessionTargets.has(edge.target)) {
9298
- const edgeKey = `${edge.note_path}\0${edge.target}`;
9299
- coSessionCount.set(edgeKey, (coSessionCount.get(edgeKey) ?? 0) + 1);
9300
- }
9301
- }
9302
- }
9303
- }
9304
- const now = Date.now();
9305
- const update = stateDb2.db.prepare(
9306
- "UPDATE note_links SET weight = ?, weight_updated_at = ? WHERE note_path = ? AND target = ?"
9307
- );
9308
- const tx = stateDb2.db.transaction(() => {
9309
- for (const edge of edges) {
9310
- const edgeKey = `${edge.note_path}\0${edge.target}`;
9311
- const editsSurvived = survivalMap.get(edgeKey) ?? 0;
9312
- const coSessions = coSessionCount.get(edgeKey) ?? 0;
9313
- const sourceAccess = sourceActivityCount.get(edge.note_path) ?? 0;
9314
- const weight = 1 + editsSurvived * 0.5 + Math.min(coSessions * 0.5, 3) + Math.min(sourceAccess * 0.2, 2);
9315
- update.run(Math.round(weight * 1e3) / 1e3, now, edge.note_path, edge.target);
9316
- }
9317
- });
9318
- tx();
9319
- return { edges_updated: edges.length, duration_ms: Date.now() - start };
9320
- }
9321
-
9322
9362
  // src/tools/read/system.ts
9323
9363
  function registerSystemTools(server2, getIndex, setIndex, getVaultPath, setConfig, getStateDb) {
9324
9364
  const RefreshIndexOutputSchema = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@velvetmonkey/flywheel-memory",
3
- "version": "2.0.42",
4
- "description": "MCP server that gives Claude full read/write access to your Obsidian vault. 42 tools for search, backlinks, graph queries, mutations, and hybrid semantic search.",
3
+ "version": "2.0.43",
4
+ "description": "MCP server that gives Claude full read/write access to your Obsidian vault. Select from 42 tools for search, backlinks, graph queries, mutations, and hybrid semantic search.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "bin": {
@@ -52,7 +52,7 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@modelcontextprotocol/sdk": "^1.25.1",
55
- "@velvetmonkey/vault-core": "^2.0.42",
55
+ "@velvetmonkey/vault-core": "^2.0.43",
56
56
  "better-sqlite3": "^11.0.0",
57
57
  "chokidar": "^4.0.0",
58
58
  "gray-matter": "^4.0.3",