@velvetmonkey/flywheel-memory 2.0.23 → 2.0.24

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 +48 -10
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1839,6 +1839,7 @@ var MAX_FILE_SIZE2 = 5 * 1024 * 1024;
1839
1839
  var db = null;
1840
1840
  var pipeline = null;
1841
1841
  var initPromise = null;
1842
+ var embeddingsBuilding = false;
1842
1843
  var embeddingCache = /* @__PURE__ */ new Map();
1843
1844
  var EMBEDDING_CACHE_MAX = 500;
1844
1845
  var entityEmbeddingsMap = /* @__PURE__ */ new Map();
@@ -1894,6 +1895,7 @@ async function buildEmbeddingsIndex(vaultPath2, onProgress) {
1894
1895
  if (!db) {
1895
1896
  throw new Error("Embeddings database not initialized. Call setEmbeddingsDatabase() first.");
1896
1897
  }
1898
+ embeddingsBuilding = true;
1897
1899
  await initEmbeddings();
1898
1900
  const files = await scanVault(vaultPath2);
1899
1901
  const indexable = files.filter((f) => shouldIndexFile(f.path));
@@ -1937,6 +1939,7 @@ async function buildEmbeddingsIndex(vaultPath2, onProgress) {
1937
1939
  deleteStmt.run(existingPath);
1938
1940
  }
1939
1941
  }
1942
+ embeddingsBuilding = false;
1940
1943
  console.error(`[Semantic] Indexed ${progress.current - progress.skipped} notes, skipped ${progress.skipped}`);
1941
1944
  return progress;
1942
1945
  }
@@ -2034,6 +2037,12 @@ function reciprocalRankFusion(...lists) {
2034
2037
  }
2035
2038
  return scores;
2036
2039
  }
2040
+ function isEmbeddingsBuilding() {
2041
+ return embeddingsBuilding;
2042
+ }
2043
+ function setEmbeddingsBuilding(value) {
2044
+ embeddingsBuilding = value;
2045
+ }
2037
2046
  function hasEmbeddingsIndex() {
2038
2047
  if (!db) return false;
2039
2048
  try {
@@ -6934,6 +6943,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
6934
6943
  }).optional().describe("Most recent index rebuild event"),
6935
6944
  fts5_ready: z3.boolean().describe("Whether the FTS5 keyword search index is ready"),
6936
6945
  fts5_building: z3.boolean().describe("Whether the FTS5 keyword search index is currently building"),
6946
+ embeddings_building: z3.boolean().describe("Whether semantic embeddings are currently building"),
6937
6947
  embeddings_ready: z3.boolean().describe("Whether semantic embeddings have been built (enables hybrid keyword+semantic search)"),
6938
6948
  embeddings_count: z3.coerce.number().describe("Number of notes with semantic embeddings"),
6939
6949
  recommendations: z3.array(z3.string()).describe("Suggested actions if any issues detected")
@@ -7041,6 +7051,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
7041
7051
  last_rebuild: lastRebuild,
7042
7052
  fts5_ready: ftsState.ready,
7043
7053
  fts5_building: ftsState.building,
7054
+ embeddings_building: isEmbeddingsBuilding(),
7044
7055
  embeddings_ready: hasEmbeddingsIndex(),
7045
7056
  embeddings_count: getEmbeddingsCount(),
7046
7057
  recommendations
@@ -14871,7 +14882,6 @@ async function main() {
14871
14882
  setEmbeddingsDatabase(stateDb.db);
14872
14883
  loadEntityEmbeddingsToMemory();
14873
14884
  setWriteStateDb(stateDb);
14874
- await initializeEntityIndex(vaultPath);
14875
14885
  } catch (err) {
14876
14886
  const msg = err instanceof Error ? err.message : String(err);
14877
14887
  console.error(`[Memory] StateDb initialization failed: ${msg}`);
@@ -14890,6 +14900,15 @@ async function main() {
14890
14900
  initializeLogger2(vaultPath).catch((err) => {
14891
14901
  console.error(`[Memory] Write logger initialization failed: ${err}`);
14892
14902
  });
14903
+ if (isIndexStale(vaultPath)) {
14904
+ buildFTS5Index(vaultPath).then(() => {
14905
+ console.error("[Memory] FTS5 search index ready");
14906
+ }).catch((err) => {
14907
+ console.error("[Memory] FTS5 build failed:", err);
14908
+ });
14909
+ } else {
14910
+ console.error("[Memory] FTS5 search index already fresh, skipping rebuild");
14911
+ }
14893
14912
  let cachedIndex = null;
14894
14913
  if (stateDb) {
14895
14914
  try {
@@ -14987,6 +15006,7 @@ async function updateEntitiesInStateDb() {
14987
15006
  }
14988
15007
  async function runPostIndexWork(index) {
14989
15008
  await updateEntitiesInStateDb();
15009
+ await initializeEntityIndex(vaultPath);
14990
15010
  await exportHubScores(index, stateDb);
14991
15011
  if (stateDb) {
14992
15012
  try {
@@ -15016,15 +15036,6 @@ async function runPostIndexWork(index) {
15016
15036
  console.error("[Memory] Failed to update suppression list:", err);
15017
15037
  }
15018
15038
  }
15019
- if (isIndexStale(vaultPath)) {
15020
- buildFTS5Index(vaultPath).then(() => {
15021
- console.error("[Memory] FTS5 search index ready");
15022
- }).catch((err) => {
15023
- console.error("[Memory] FTS5 build failed:", err);
15024
- });
15025
- } else {
15026
- console.error("[Memory] FTS5 search index already fresh, skipping rebuild");
15027
- }
15028
15039
  const existing = loadConfig(stateDb);
15029
15040
  const inferred = inferConfig(index, vaultPath);
15030
15041
  if (stateDb) {
@@ -15034,6 +15045,33 @@ async function runPostIndexWork(index) {
15034
15045
  if (flywheelConfig.vault_name) {
15035
15046
  console.error(`[Memory] Vault: ${flywheelConfig.vault_name}`);
15036
15047
  }
15048
+ if (hasEmbeddingsIndex()) {
15049
+ console.error("[Memory] Embeddings already built, skipping full scan");
15050
+ } else {
15051
+ setEmbeddingsBuilding(true);
15052
+ buildEmbeddingsIndex(vaultPath, (p) => {
15053
+ if (p.current % 100 === 0 || p.current === p.total) {
15054
+ console.error(`[Semantic] ${p.current}/${p.total}`);
15055
+ }
15056
+ }).then(async () => {
15057
+ if (stateDb) {
15058
+ const entities = getAllEntitiesFromDb2(stateDb);
15059
+ if (entities.length > 0) {
15060
+ const entityMap = new Map(entities.map((e) => [e.name, {
15061
+ name: e.name,
15062
+ path: e.path,
15063
+ category: e.category,
15064
+ aliases: e.aliases
15065
+ }]));
15066
+ await buildEntityEmbeddingsIndex(vaultPath, entityMap);
15067
+ }
15068
+ }
15069
+ loadEntityEmbeddingsToMemory();
15070
+ console.error("[Memory] Embeddings refreshed");
15071
+ }).catch((err) => {
15072
+ console.error("[Memory] Embeddings refresh failed:", err);
15073
+ });
15074
+ }
15037
15075
  if (process.env.FLYWHEEL_WATCH !== "false") {
15038
15076
  const config = parseWatcherConfig();
15039
15077
  console.error(`[Memory] File watcher enabled (debounce: ${config.debounceMs}ms)`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velvetmonkey/flywheel-memory",
3
- "version": "2.0.23",
3
+ "version": "2.0.24",
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.23",
53
+ "@velvetmonkey/vault-core": "^2.0.24",
54
54
  "better-sqlite3": "^11.0.0",
55
55
  "chokidar": "^4.0.0",
56
56
  "gray-matter": "^4.0.3",