@velvetmonkey/flywheel-memory 2.0.37 → 2.0.38

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 +55 -4
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -6986,7 +6986,7 @@ function refreshIfStale(vaultPath2, index, excludeTags) {
6986
6986
  }
6987
6987
 
6988
6988
  // src/index.ts
6989
- import { openStateDb, scanVaultEntities as scanVaultEntities3, getSessionId, getAllEntitiesFromDb as getAllEntitiesFromDb3 } from "@velvetmonkey/vault-core";
6989
+ import { openStateDb, scanVaultEntities as scanVaultEntities3, getSessionId, getAllEntitiesFromDb as getAllEntitiesFromDb3, findEntityMatches as findEntityMatches2, getProtectedZones as getProtectedZones2, rangeOverlapsProtectedZone } from "@velvetmonkey/vault-core";
6990
6990
 
6991
6991
  // src/tools/read/graph.ts
6992
6992
  import * as fs9 from "fs";
@@ -17533,6 +17533,10 @@ async function runPostIndexWork(index) {
17533
17533
  const markPositive = stateDb.db.prepare(`
17534
17534
  UPDATE note_link_history SET last_positive_at = datetime('now') WHERE note_path = ? AND target = ?
17535
17535
  `);
17536
+ const getEdgeCount = stateDb.db.prepare(
17537
+ "SELECT edits_survived FROM note_link_history WHERE note_path=? AND target=?"
17538
+ );
17539
+ const survivedLinks2 = [];
17536
17540
  for (const entry of forwardLinkResults) {
17537
17541
  const currentSet = /* @__PURE__ */ new Set([
17538
17542
  ...entry.resolved.map((n) => n.toLowerCase()),
@@ -17551,6 +17555,10 @@ async function runPostIndexWork(index) {
17551
17555
  for (const link of currentSet) {
17552
17556
  if (!previousSet.has(link)) continue;
17553
17557
  upsertHistory.run(entry.file, link);
17558
+ const countRow = getEdgeCount.get(entry.file, link);
17559
+ if (countRow) {
17560
+ survivedLinks2.push({ entity: link, file: entry.file, count: countRow.edits_survived });
17561
+ }
17554
17562
  const hit = checkThreshold.get(entry.file, link);
17555
17563
  if (hit) {
17556
17564
  const entity = entitiesAfter.find(
@@ -17572,12 +17580,23 @@ async function runPostIndexWork(index) {
17572
17580
  }
17573
17581
  }
17574
17582
  }
17583
+ const processedFiles = new Set(forwardLinkResults.map((r) => r.file));
17584
+ for (const event of filteredEvents) {
17585
+ if (event.type === "delete" || !event.path.endsWith(".md")) continue;
17586
+ if (processedFiles.has(event.path)) continue;
17587
+ const previousSet = getStoredNoteLinks(stateDb, event.path);
17588
+ if (previousSet.size > 0) {
17589
+ linkDiffs.push({ file: event.path, added: [], removed: [...previousSet] });
17590
+ updateStoredNoteLinks(stateDb, event.path, /* @__PURE__ */ new Set());
17591
+ }
17592
+ }
17575
17593
  }
17576
17594
  tracker.end({
17577
17595
  total_resolved: totalResolved,
17578
17596
  total_dead: totalDead,
17579
17597
  links: forwardLinkResults,
17580
- link_diffs: linkDiffs
17598
+ link_diffs: linkDiffs,
17599
+ survived: survivedLinks
17581
17600
  });
17582
17601
  serverLog("watcher", `Forward links: ${totalResolved} resolved, ${totalDead} dead`);
17583
17602
  tracker.start("wikilink_check", { files: filteredEvents.length });
@@ -17607,8 +17626,40 @@ async function runPostIndexWork(index) {
17607
17626
  trackedLinks.push({ file: diff.file, entities: diff.added });
17608
17627
  }
17609
17628
  }
17610
- tracker.end({ tracked: trackedLinks });
17611
- serverLog("watcher", `Wikilink check: ${trackedLinks.reduce((s, t) => s + t.entities.length, 0)} tracked links in ${trackedLinks.length} files`);
17629
+ const mentionResults = [];
17630
+ for (const event of filteredEvents) {
17631
+ if (event.type === "delete" || !event.path.endsWith(".md")) continue;
17632
+ try {
17633
+ const content = await fs30.readFile(path31.join(vaultPath, event.path), "utf-8");
17634
+ const zones = getProtectedZones2(content);
17635
+ const linked = new Set(
17636
+ (forwardLinkResults.find((r) => r.file === event.path)?.resolved ?? []).map((n) => n.toLowerCase())
17637
+ );
17638
+ const mentions = [];
17639
+ for (const entity of entitiesAfter) {
17640
+ if (linked.has(entity.nameLower)) continue;
17641
+ const matches = findEntityMatches2(content, entity.name, true);
17642
+ const valid = matches.some((m) => !rangeOverlapsProtectedZone(m.start, m.end, zones));
17643
+ if (valid) {
17644
+ mentions.push(entity.name);
17645
+ continue;
17646
+ }
17647
+ for (const alias of entity.aliases ?? []) {
17648
+ const aliasMatches = findEntityMatches2(content, alias, true);
17649
+ if (aliasMatches.some((m) => !rangeOverlapsProtectedZone(m.start, m.end, zones))) {
17650
+ mentions.push(entity.name);
17651
+ break;
17652
+ }
17653
+ }
17654
+ }
17655
+ if (mentions.length > 0) {
17656
+ mentionResults.push({ file: event.path, entities: mentions });
17657
+ }
17658
+ } catch {
17659
+ }
17660
+ }
17661
+ tracker.end({ tracked: trackedLinks, mentions: mentionResults });
17662
+ serverLog("watcher", `Wikilink check: ${trackedLinks.reduce((s, t) => s + t.entities.length, 0)} tracked links in ${trackedLinks.length} files, ${mentionResults.reduce((s, m) => s + m.entities.length, 0)} unwikified mentions`);
17612
17663
  tracker.start("implicit_feedback", { files: filteredEvents.length });
17613
17664
  const feedbackResults = [];
17614
17665
  if (stateDb) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velvetmonkey/flywheel-memory",
3
- "version": "2.0.37",
3
+ "version": "2.0.38",
4
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.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -52,7 +52,7 @@
52
52
  },
53
53
  "dependencies": {
54
54
  "@modelcontextprotocol/sdk": "^1.25.1",
55
- "@velvetmonkey/vault-core": "^2.0.37",
55
+ "@velvetmonkey/vault-core": "^2.0.38",
56
56
  "better-sqlite3": "^11.0.0",
57
57
  "chokidar": "^4.0.0",
58
58
  "gray-matter": "^4.0.3",