@velvetmonkey/flywheel-memory 2.0.31 → 2.0.32

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 +45 -6
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -3216,6 +3216,7 @@ import {
3216
3216
  getEntityAliases,
3217
3217
  applyWikilinks,
3218
3218
  resolveAliasWikilinks,
3219
+ detectImplicitEntities,
3219
3220
  getEntityIndexFromDb,
3220
3221
  getStateDbMetadata,
3221
3222
  getEntityByName,
@@ -5060,6 +5061,42 @@ function processWikilinks(content, notePath) {
5060
5061
  firstOccurrenceOnly: true,
5061
5062
  caseInsensitive: true
5062
5063
  });
5064
+ const implicitMatches = detectImplicitEntities(result.content, {
5065
+ detectImplicit: true,
5066
+ implicitPatterns: ["proper-nouns", "single-caps", "quoted-terms", "camel-case", "acronyms"],
5067
+ minEntityLength: 3
5068
+ });
5069
+ const alreadyLinked = new Set(
5070
+ [...resolved.linkedEntities, ...result.linkedEntities].map((e) => e.toLowerCase())
5071
+ );
5072
+ for (const entity of sortedEntities) {
5073
+ const name = getEntityName2(entity);
5074
+ alreadyLinked.add(name.toLowerCase());
5075
+ const aliases = getEntityAliases(entity);
5076
+ for (const alias of aliases) {
5077
+ alreadyLinked.add(alias.toLowerCase());
5078
+ }
5079
+ }
5080
+ const currentNoteName = notePath ? notePath.replace(/\.md$/, "").split("/").pop()?.toLowerCase() : null;
5081
+ const newImplicits = implicitMatches.filter((m) => {
5082
+ const normalized = m.text.toLowerCase();
5083
+ if (alreadyLinked.has(normalized)) return false;
5084
+ if (currentNoteName && normalized === currentNoteName) return false;
5085
+ return true;
5086
+ });
5087
+ if (newImplicits.length > 0) {
5088
+ let processedContent = result.content;
5089
+ for (let i = newImplicits.length - 1; i >= 0; i--) {
5090
+ const m = newImplicits[i];
5091
+ processedContent = processedContent.slice(0, m.start) + `[[${m.text}]]` + processedContent.slice(m.end);
5092
+ }
5093
+ return {
5094
+ content: processedContent,
5095
+ linksAdded: resolved.linksAdded + result.linksAdded + newImplicits.length,
5096
+ linkedEntities: [...resolved.linkedEntities, ...result.linkedEntities],
5097
+ implicitEntities: newImplicits.map((m) => m.text)
5098
+ };
5099
+ }
5063
5100
  return {
5064
5101
  content: result.content,
5065
5102
  linksAdded: resolved.linksAdded + result.linksAdded,
@@ -5076,9 +5113,11 @@ function maybeApplyWikilinks(content, skipWikilinks, notePath) {
5076
5113
  if (moduleStateDb4 && notePath) {
5077
5114
  trackWikilinkApplications(moduleStateDb4, notePath, result.linkedEntities);
5078
5115
  }
5116
+ const implicitCount = result.implicitEntities?.length ?? 0;
5117
+ const implicitInfo = implicitCount > 0 ? ` + ${implicitCount} implicit: ${result.implicitEntities.join(", ")}` : "";
5079
5118
  return {
5080
5119
  content: result.content,
5081
- wikilinkInfo: `Applied ${result.linksAdded} wikilink(s): ${result.linkedEntities.join(", ")}`
5120
+ wikilinkInfo: `Applied ${result.linksAdded} wikilink(s): ${result.linkedEntities.join(", ")}${implicitInfo}`
5082
5121
  };
5083
5122
  }
5084
5123
  return { content: result.content };
@@ -7535,7 +7574,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
7535
7574
  recommendations.push(`Index is ${Math.floor(indexAge / 60)} minutes old. Consider running refresh_index.`);
7536
7575
  }
7537
7576
  const noteCount = indexBuilt ? index.notes.size : 0;
7538
- const entityCount2 = indexBuilt ? index.entities.size : 0;
7577
+ const entityCount = indexBuilt ? index.entities.size : 0;
7539
7578
  const tagCount = indexBuilt ? index.tags.size : 0;
7540
7579
  let linkCount = 0;
7541
7580
  if (indexBuilt) {
@@ -7615,7 +7654,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
7615
7654
  index_age_seconds: indexAge,
7616
7655
  index_stale: indexStale,
7617
7656
  note_count: noteCount,
7618
- entity_count: entityCount2,
7657
+ entity_count: entityCount,
7619
7658
  tag_count: tagCount,
7620
7659
  link_count: linkCount,
7621
7660
  periodic_notes: periodicNotes && periodicNotes.length > 0 ? periodicNotes : void 0,
@@ -14544,7 +14583,7 @@ function computeMetrics(index, stateDb2) {
14544
14583
  }
14545
14584
  }
14546
14585
  const tagCount = index.tags.size;
14547
- const entityCount2 = index.entities.size;
14586
+ const entityCount = index.entities.size;
14548
14587
  const avgLinksPerNote = noteCount > 0 ? linkCount / noteCount : 0;
14549
14588
  const possibleLinks = noteCount * (noteCount - 1);
14550
14589
  const linkDensity = possibleLinks > 0 ? linkCount / possibleLinks : 0;
@@ -14566,7 +14605,7 @@ function computeMetrics(index, stateDb2) {
14566
14605
  link_count: linkCount,
14567
14606
  orphan_count: orphanCount,
14568
14607
  tag_count: tagCount,
14569
- entity_count: entityCount2,
14608
+ entity_count: entityCount,
14570
14609
  avg_links_per_note: Math.round(avgLinksPerNote * 100) / 100,
14571
14610
  link_density: Math.round(linkDensity * 1e4) / 1e4,
14572
14611
  connected_ratio: Math.round(connectedRatio * 1e3) / 1e3,
@@ -16138,7 +16177,7 @@ async function runPostIndexWork(index) {
16138
16177
  const entityDiff = computeEntityDiff(entitiesBefore, entitiesAfter);
16139
16178
  tracker.end({ entity_count: entitiesAfter.length, ...entityDiff });
16140
16179
  serverLog("watcher", `Entity scan: ${entitiesAfter.length} entities`);
16141
- tracker.start("hub_scores", { entity_count: entityCount });
16180
+ tracker.start("hub_scores", { entity_count: entitiesAfter.length });
16142
16181
  const hubUpdated = await exportHubScores(vaultIndex, stateDb);
16143
16182
  tracker.end({ updated: hubUpdated ?? 0 });
16144
16183
  serverLog("watcher", `Hub scores: ${hubUpdated ?? 0} updated`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velvetmonkey/flywheel-memory",
3
- "version": "2.0.31",
3
+ "version": "2.0.32",
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",
@@ -50,7 +50,7 @@
50
50
  },
51
51
  "dependencies": {
52
52
  "@modelcontextprotocol/sdk": "^1.25.1",
53
- "@velvetmonkey/vault-core": "^2.0.31",
53
+ "@velvetmonkey/vault-core": "^2.0.32",
54
54
  "better-sqlite3": "^11.0.0",
55
55
  "chokidar": "^4.0.0",
56
56
  "gray-matter": "^4.0.3",