@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.
- package/dist/index.js +48 -10
- 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.
|
|
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.
|
|
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",
|