@velvetmonkey/flywheel-memory 2.0.156 → 2.0.158
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 +170 -36
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -557,6 +557,18 @@ function hasEmbeddingsIndex() {
|
|
|
557
557
|
const row = db4.prepare("SELECT COUNT(*) as count FROM note_embeddings").get();
|
|
558
558
|
return row.count > 0;
|
|
559
559
|
}
|
|
560
|
+
if (!isEmbeddingsBuilding()) {
|
|
561
|
+
const row = db4.prepare("SELECT COUNT(*) as count FROM note_embeddings").get();
|
|
562
|
+
if (row.count > 0) {
|
|
563
|
+
setEmbeddingsBuildState("complete");
|
|
564
|
+
console.error("[Semantic] Recovered stale embeddings_state \u2192 complete");
|
|
565
|
+
return true;
|
|
566
|
+
} else {
|
|
567
|
+
setEmbeddingsBuildState("none");
|
|
568
|
+
console.error("[Semantic] Recovered stale embeddings_state \u2192 none (no rows)");
|
|
569
|
+
return false;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
560
572
|
return false;
|
|
561
573
|
} catch {
|
|
562
574
|
return false;
|
|
@@ -575,7 +587,7 @@ function getStoredEmbeddingModel() {
|
|
|
575
587
|
function diagnoseEmbeddings(vaultPath2) {
|
|
576
588
|
const db4 = getDb();
|
|
577
589
|
const checks = [];
|
|
578
|
-
const counts = { embedded: 0, vaultNotes: 0, orphaned: 0, missing: 0 };
|
|
590
|
+
const counts = { embedded: 0, vaultNotes: 0, orphaned: 0, orphanedEntities: 0, missing: 0 };
|
|
579
591
|
if (!db4) {
|
|
580
592
|
checks.push({ name: "database", status: "stale", detail: "No database available" });
|
|
581
593
|
return { healthy: false, checks, counts };
|
|
@@ -649,6 +661,25 @@ function diagnoseEmbeddings(vaultPath2) {
|
|
|
649
661
|
} catch {
|
|
650
662
|
checks.push({ name: "orphans", status: "warning", detail: "Could not check" });
|
|
651
663
|
}
|
|
664
|
+
try {
|
|
665
|
+
const embNames = new Set(
|
|
666
|
+
db4.prepare("SELECT entity_name FROM entity_embeddings").all().map((r) => r.entity_name)
|
|
667
|
+
);
|
|
668
|
+
const entityNames = new Set(
|
|
669
|
+
db4.prepare("SELECT name FROM entities").all().map((r) => r.name)
|
|
670
|
+
);
|
|
671
|
+
counts.orphanedEntities = 0;
|
|
672
|
+
for (const n of embNames) {
|
|
673
|
+
if (!entityNames.has(n)) counts.orphanedEntities++;
|
|
674
|
+
}
|
|
675
|
+
if (counts.orphanedEntities > 0) {
|
|
676
|
+
checks.push({ name: "entity_orphans", status: "warning", detail: `${counts.orphanedEntities} orphaned entity embeddings` });
|
|
677
|
+
} else {
|
|
678
|
+
checks.push({ name: "entity_orphans", status: "ok", detail: "0 orphaned" });
|
|
679
|
+
}
|
|
680
|
+
} catch {
|
|
681
|
+
checks.push({ name: "entity_orphans", status: "warning", detail: "Could not check entity embeddings" });
|
|
682
|
+
}
|
|
652
683
|
try {
|
|
653
684
|
const samples = db4.prepare("SELECT embedding FROM note_embeddings ORDER BY RANDOM() LIMIT 3").all();
|
|
654
685
|
let corrupt = false;
|
|
@@ -732,6 +763,7 @@ async function buildEntityEmbeddingsIndex(vaultPath2, entities, onProgress) {
|
|
|
732
763
|
const total = entities.size;
|
|
733
764
|
let done = 0;
|
|
734
765
|
let updated = 0;
|
|
766
|
+
let skipped = 0;
|
|
735
767
|
for (const [name, entity] of entities) {
|
|
736
768
|
done++;
|
|
737
769
|
try {
|
|
@@ -745,7 +777,11 @@ async function buildEntityEmbeddingsIndex(vaultPath2, entities, onProgress) {
|
|
|
745
777
|
const buf = Buffer.from(embedding.buffer, embedding.byteOffset, embedding.byteLength);
|
|
746
778
|
upsert.run(name, buf, hash, activeModelConfig.id, Date.now());
|
|
747
779
|
updated++;
|
|
748
|
-
} catch {
|
|
780
|
+
} catch (err) {
|
|
781
|
+
skipped++;
|
|
782
|
+
if (skipped <= 3) {
|
|
783
|
+
console.error(`[Semantic] Failed to embed entity ${name}: ${err instanceof Error ? err.message : err}`);
|
|
784
|
+
}
|
|
749
785
|
}
|
|
750
786
|
if (onProgress) onProgress(done, total);
|
|
751
787
|
}
|
|
@@ -755,7 +791,7 @@ async function buildEntityEmbeddingsIndex(vaultPath2, entities, onProgress) {
|
|
|
755
791
|
deleteStmt.run(existingName);
|
|
756
792
|
}
|
|
757
793
|
}
|
|
758
|
-
console.error(`[Semantic] Entity embeddings: ${updated} updated, ${total - updated} unchanged`);
|
|
794
|
+
console.error(`[Semantic] Entity embeddings: ${updated} updated, ${total - updated - skipped} unchanged, ${skipped} failed`);
|
|
759
795
|
return updated;
|
|
760
796
|
}
|
|
761
797
|
async function updateEntityEmbedding(entityName, entity, vaultPath2) {
|
|
@@ -773,9 +809,34 @@ async function updateEntityEmbedding(entityName, entity, vaultPath2) {
|
|
|
773
809
|
VALUES (?, ?, ?, ?, ?)
|
|
774
810
|
`).run(entityName, buf, hash, activeModelConfig.id, Date.now());
|
|
775
811
|
entityEmbeddingsMap.set(entityName, embedding);
|
|
776
|
-
} catch {
|
|
812
|
+
} catch (err) {
|
|
813
|
+
console.error(`[Semantic] Failed to update entity embedding ${entityName}: ${err instanceof Error ? err.message : err}`);
|
|
777
814
|
}
|
|
778
815
|
}
|
|
816
|
+
function removeOrphanedNoteEmbeddings() {
|
|
817
|
+
const db4 = getDb();
|
|
818
|
+
if (!db4) return 0;
|
|
819
|
+
const result = db4.prepare(
|
|
820
|
+
"DELETE FROM note_embeddings WHERE path NOT IN (SELECT path FROM notes_fts)"
|
|
821
|
+
).run();
|
|
822
|
+
return result.changes;
|
|
823
|
+
}
|
|
824
|
+
function removeOrphanedEntityEmbeddings(currentEntityNames) {
|
|
825
|
+
const db4 = getDb();
|
|
826
|
+
if (!db4) return 0;
|
|
827
|
+
const rows = db4.prepare("SELECT entity_name FROM entity_embeddings").all();
|
|
828
|
+
const deleteStmt = db4.prepare("DELETE FROM entity_embeddings WHERE entity_name = ?");
|
|
829
|
+
const embMap = getEmbMap();
|
|
830
|
+
let removed = 0;
|
|
831
|
+
for (const row of rows) {
|
|
832
|
+
if (!currentEntityNames.has(row.entity_name)) {
|
|
833
|
+
deleteStmt.run(row.entity_name);
|
|
834
|
+
embMap.delete(row.entity_name);
|
|
835
|
+
removed++;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
return removed;
|
|
839
|
+
}
|
|
779
840
|
function findSemanticallySimilarEntities(queryEmbedding, limit, excludeEntities) {
|
|
780
841
|
const scored = [];
|
|
781
842
|
for (const [entityName, embedding] of getEmbMap()) {
|
|
@@ -7652,6 +7713,38 @@ function purgeOldNoteLinkHistory(stateDb2, retentionDays = 90) {
|
|
|
7652
7713
|
return result.changes;
|
|
7653
7714
|
}
|
|
7654
7715
|
|
|
7716
|
+
// src/core/read/identity.ts
|
|
7717
|
+
function normalizeResolvedPath(notePath) {
|
|
7718
|
+
return notePath.toLowerCase().replace(/\.md$/, "");
|
|
7719
|
+
}
|
|
7720
|
+
function getInboundTargetsForNote(stateDb2, notePath) {
|
|
7721
|
+
const targets = /* @__PURE__ */ new Set();
|
|
7722
|
+
const stem2 = notePath.replace(/\.md$/, "").split("/").pop()?.toLowerCase();
|
|
7723
|
+
const normalizedPath = normalizeResolvedPath(notePath);
|
|
7724
|
+
if (stateDb2) {
|
|
7725
|
+
try {
|
|
7726
|
+
const row = stateDb2.db.prepare(
|
|
7727
|
+
`SELECT name_lower, aliases_json FROM entities
|
|
7728
|
+
WHERE LOWER(REPLACE(path, '.md', '')) = ?`
|
|
7729
|
+
).get(normalizedPath);
|
|
7730
|
+
if (row) {
|
|
7731
|
+
targets.add(row.name_lower);
|
|
7732
|
+
if (row.aliases_json) {
|
|
7733
|
+
try {
|
|
7734
|
+
for (const alias of JSON.parse(row.aliases_json)) {
|
|
7735
|
+
targets.add(alias.toLowerCase());
|
|
7736
|
+
}
|
|
7737
|
+
} catch {
|
|
7738
|
+
}
|
|
7739
|
+
}
|
|
7740
|
+
}
|
|
7741
|
+
} catch {
|
|
7742
|
+
}
|
|
7743
|
+
}
|
|
7744
|
+
if (stem2) targets.add(stem2);
|
|
7745
|
+
return [...targets];
|
|
7746
|
+
}
|
|
7747
|
+
|
|
7655
7748
|
// src/core/shared/hubExport.ts
|
|
7656
7749
|
var EIGEN_ITERATIONS = 50;
|
|
7657
7750
|
function computeHubScores(index) {
|
|
@@ -7735,15 +7828,16 @@ function computeHubScores(index) {
|
|
|
7735
7828
|
}
|
|
7736
7829
|
return { scores: result, edgeCount };
|
|
7737
7830
|
}
|
|
7738
|
-
function updateHubScoresInDb(stateDb2, hubScores) {
|
|
7739
|
-
const
|
|
7740
|
-
|
|
7741
|
-
`);
|
|
7831
|
+
function updateHubScoresInDb(stateDb2, hubScores, pathToId) {
|
|
7832
|
+
const resetAll = stateDb2.db.prepare("UPDATE entities SET hub_score = 0");
|
|
7833
|
+
const updateById = stateDb2.db.prepare("UPDATE entities SET hub_score = ? WHERE id = ?");
|
|
7742
7834
|
let updated = 0;
|
|
7743
7835
|
const transaction = stateDb2.db.transaction(() => {
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7836
|
+
resetAll.run();
|
|
7837
|
+
for (const [normalizedPath, score] of hubScores) {
|
|
7838
|
+
const entityId = pathToId.get(normalizedPath);
|
|
7839
|
+
if (entityId !== void 0) {
|
|
7840
|
+
updateById.run(score, entityId);
|
|
7747
7841
|
updated++;
|
|
7748
7842
|
}
|
|
7749
7843
|
}
|
|
@@ -7758,9 +7852,14 @@ async function exportHubScores(vaultIndex2, stateDb2) {
|
|
|
7758
7852
|
}
|
|
7759
7853
|
const { scores: hubScores, edgeCount } = computeHubScores(vaultIndex2);
|
|
7760
7854
|
console.error(`[Flywheel] Computed hub scores for ${hubScores.size} notes (${edgeCount} edges in graph)`);
|
|
7855
|
+
const entityRows = stateDb2.db.prepare("SELECT id, path FROM entities").all();
|
|
7856
|
+
const pathToId = /* @__PURE__ */ new Map();
|
|
7857
|
+
for (const row of entityRows) {
|
|
7858
|
+
pathToId.set(normalizeResolvedPath(row.path), row.id);
|
|
7859
|
+
}
|
|
7761
7860
|
try {
|
|
7762
|
-
const updated = updateHubScoresInDb(stateDb2, hubScores);
|
|
7763
|
-
console.error(`[Flywheel]
|
|
7861
|
+
const updated = updateHubScoresInDb(stateDb2, hubScores, pathToId);
|
|
7862
|
+
console.error(`[Flywheel] Hub scores: ${updated}/${hubScores.size} scores mapped to entities (all others reset to 0)`);
|
|
7764
7863
|
return updated;
|
|
7765
7864
|
} catch (e) {
|
|
7766
7865
|
console.error("[Flywheel] Failed to update hub scores in StateDb:", e);
|
|
@@ -8703,8 +8802,14 @@ var PipelineRunner = class {
|
|
|
8703
8802
|
} catch {
|
|
8704
8803
|
}
|
|
8705
8804
|
}
|
|
8706
|
-
|
|
8707
|
-
|
|
8805
|
+
let orphansRemoved = 0;
|
|
8806
|
+
try {
|
|
8807
|
+
orphansRemoved = removeOrphanedNoteEmbeddings();
|
|
8808
|
+
} catch (e) {
|
|
8809
|
+
serverLog("watcher", `Note embedding orphan cleanup failed: ${e}`, "error");
|
|
8810
|
+
}
|
|
8811
|
+
tracker.end({ updated: embUpdated, removed: embRemoved, orphans_removed: orphansRemoved });
|
|
8812
|
+
serverLog("watcher", `Note embeddings: ${embUpdated} updated, ${embRemoved} removed, ${orphansRemoved} orphans cleaned`);
|
|
8708
8813
|
} else {
|
|
8709
8814
|
tracker.skip("note_embeddings", "not built");
|
|
8710
8815
|
}
|
|
@@ -8715,6 +8820,7 @@ var PipelineRunner = class {
|
|
|
8715
8820
|
if (hasEntityEmbeddingsIndex() && p.sd) {
|
|
8716
8821
|
tracker.start("entity_embeddings", { files: p.events.length });
|
|
8717
8822
|
let entEmbUpdated = 0;
|
|
8823
|
+
let entEmbOrphansRemoved = 0;
|
|
8718
8824
|
const entEmbNames = [];
|
|
8719
8825
|
try {
|
|
8720
8826
|
const allEntities = getAllEntitiesFromDb(p.sd);
|
|
@@ -8732,10 +8838,13 @@ var PipelineRunner = class {
|
|
|
8732
8838
|
entEmbNames.push(entity.name);
|
|
8733
8839
|
}
|
|
8734
8840
|
}
|
|
8735
|
-
|
|
8841
|
+
const currentNames = new Set(allEntities.map((e) => e.name));
|
|
8842
|
+
entEmbOrphansRemoved = removeOrphanedEntityEmbeddings(currentNames);
|
|
8843
|
+
} catch (e) {
|
|
8844
|
+
serverLog("watcher", `Entity embedding update/orphan cleanup failed: ${e}`, "error");
|
|
8736
8845
|
}
|
|
8737
|
-
tracker.end({ updated: entEmbUpdated, updated_entities: entEmbNames.slice(0, 10) });
|
|
8738
|
-
serverLog("watcher", `Entity embeddings: ${entEmbUpdated} updated`);
|
|
8846
|
+
tracker.end({ updated: entEmbUpdated, updated_entities: entEmbNames.slice(0, 10), orphans_removed: entEmbOrphansRemoved });
|
|
8847
|
+
serverLog("watcher", `Entity embeddings: ${entEmbUpdated} updated, ${entEmbOrphansRemoved} orphans cleaned`);
|
|
8739
8848
|
} else {
|
|
8740
8849
|
tracker.skip("entity_embeddings", !p.sd ? "no sd" : "not built");
|
|
8741
8850
|
}
|
|
@@ -9283,9 +9392,15 @@ var PipelineRunner = class {
|
|
|
9283
9392
|
return { skipped: true, reason: "vacuumed recently" };
|
|
9284
9393
|
}
|
|
9285
9394
|
p.sd.db.pragma("incremental_vacuum");
|
|
9286
|
-
p.sd.
|
|
9287
|
-
|
|
9288
|
-
|
|
9395
|
+
const walResult = p.sd.db.pragma("wal_checkpoint(TRUNCATE)");
|
|
9396
|
+
const checkpointed = walResult?.[0]?.busy === 0;
|
|
9397
|
+
if (checkpointed) {
|
|
9398
|
+
p.sd.setMetadataValue.run("last_incremental_vacuum", String(Date.now()));
|
|
9399
|
+
serverLog("watcher", "Incremental vacuum + WAL checkpoint completed");
|
|
9400
|
+
} else {
|
|
9401
|
+
serverLog("watcher", "Incremental vacuum done, WAL checkpoint skipped (busy readers)");
|
|
9402
|
+
}
|
|
9403
|
+
return { vacuumed: true, wal_checkpointed: checkpointed };
|
|
9289
9404
|
}
|
|
9290
9405
|
};
|
|
9291
9406
|
|
|
@@ -9788,11 +9903,9 @@ function computeMetrics(index, stateDb2) {
|
|
|
9788
9903
|
for (const bl of backlinks) {
|
|
9789
9904
|
connectedNotes.add(bl.source);
|
|
9790
9905
|
}
|
|
9791
|
-
|
|
9792
|
-
|
|
9793
|
-
|
|
9794
|
-
connectedNotes.add(note.path);
|
|
9795
|
-
}
|
|
9906
|
+
const targetPath = index.entities.get(target);
|
|
9907
|
+
if (targetPath && index.notes.has(targetPath)) {
|
|
9908
|
+
connectedNotes.add(targetPath);
|
|
9796
9909
|
}
|
|
9797
9910
|
}
|
|
9798
9911
|
let orphanCount = 0;
|
|
@@ -10824,14 +10937,18 @@ function rankOutlinks(outlinks, notePath, index, stateDb2, maxLinks = TOP_LINKS)
|
|
|
10824
10937
|
}).sort((a, b) => (b.weight ?? 1) - (a.weight ?? 1)).slice(0, maxLinks);
|
|
10825
10938
|
}
|
|
10826
10939
|
function rankBacklinks(backlinks, notePath, index, stateDb2, maxLinks = TOP_LINKS) {
|
|
10827
|
-
const
|
|
10940
|
+
const targets = getInboundTargetsForNote(stateDb2, notePath);
|
|
10828
10941
|
const weightMap = /* @__PURE__ */ new Map();
|
|
10829
|
-
if (stateDb2 &&
|
|
10942
|
+
if (stateDb2 && targets.length > 0) {
|
|
10830
10943
|
try {
|
|
10944
|
+
const placeholders = targets.map(() => "?").join(",");
|
|
10831
10945
|
const rows = stateDb2.db.prepare(
|
|
10832
|
-
|
|
10833
|
-
).all(
|
|
10834
|
-
for (const row of rows)
|
|
10946
|
+
`SELECT note_path, weight FROM note_links WHERE target IN (${placeholders})`
|
|
10947
|
+
).all(...targets);
|
|
10948
|
+
for (const row of rows) {
|
|
10949
|
+
const existing = weightMap.get(row.note_path) ?? 0;
|
|
10950
|
+
weightMap.set(row.note_path, Math.max(existing, row.weight));
|
|
10951
|
+
}
|
|
10835
10952
|
} catch {
|
|
10836
10953
|
}
|
|
10837
10954
|
}
|
|
@@ -12703,16 +12820,18 @@ function registerGraphTools(server2, getIndex, getVaultPath, getStateDb3) {
|
|
|
12703
12820
|
return { content: [{ type: "text", text: JSON.stringify({ error: "StateDb not initialized" }) }] };
|
|
12704
12821
|
}
|
|
12705
12822
|
const limit = Math.min(requestedLimit ?? 20, MAX_LIMIT);
|
|
12706
|
-
const
|
|
12823
|
+
const targets = getInboundTargetsForNote(stateDb2, notePath);
|
|
12824
|
+
const inPlaceholders = targets.map(() => "?").join(",");
|
|
12707
12825
|
const rows = stateDb2.db.prepare(`
|
|
12708
12826
|
SELECT target AS node, weight, 'outgoing' AS direction
|
|
12709
12827
|
FROM note_links WHERE note_path = ?
|
|
12710
12828
|
UNION ALL
|
|
12711
|
-
SELECT note_path AS node, weight, 'incoming' AS direction
|
|
12712
|
-
FROM note_links WHERE target
|
|
12829
|
+
SELECT note_path AS node, MAX(weight) AS weight, 'incoming' AS direction
|
|
12830
|
+
FROM note_links WHERE target IN (${inPlaceholders})
|
|
12831
|
+
GROUP BY note_path
|
|
12713
12832
|
ORDER BY weight DESC
|
|
12714
12833
|
LIMIT ?
|
|
12715
|
-
`).all(notePath,
|
|
12834
|
+
`).all(notePath, ...targets, limit);
|
|
12716
12835
|
return {
|
|
12717
12836
|
content: [{
|
|
12718
12837
|
type: "text",
|
|
@@ -22920,6 +23039,7 @@ function registerSemanticTools(server2, getVaultPath, getStateDb3) {
|
|
|
22920
23039
|
if (!force) {
|
|
22921
23040
|
const diagnosis2 = diagnoseEmbeddings(vaultPath2);
|
|
22922
23041
|
if (diagnosis2.healthy) {
|
|
23042
|
+
setEmbeddingsBuildState("complete");
|
|
22923
23043
|
return {
|
|
22924
23044
|
content: [{
|
|
22925
23045
|
type: "text",
|
|
@@ -24932,7 +25052,8 @@ function registerAllTools(targetServer, ctx) {
|
|
|
24932
25052
|
var __filename = fileURLToPath2(import.meta.url);
|
|
24933
25053
|
var __dirname = dirname7(__filename);
|
|
24934
25054
|
var pkg = JSON.parse(readFileSync6(join21(__dirname, "../package.json"), "utf-8"));
|
|
24935
|
-
var
|
|
25055
|
+
var _earlyVaultConfigs = parseVaultConfig();
|
|
25056
|
+
var vaultPath = _earlyVaultConfigs ? _earlyVaultConfigs[0].path : process.env.PROJECT_PATH || process.env.VAULT_PATH || findVaultRoot();
|
|
24936
25057
|
var resolvedVaultPath;
|
|
24937
25058
|
try {
|
|
24938
25059
|
resolvedVaultPath = realpathSync(vaultPath).replace(/\\/g, "/");
|
|
@@ -25727,6 +25848,19 @@ if (process.argv.includes("--init-semantic")) {
|
|
|
25727
25848
|
process.exit(1);
|
|
25728
25849
|
});
|
|
25729
25850
|
}
|
|
25851
|
+
function gracefulShutdown(signal) {
|
|
25852
|
+
console.error(`[Memory] Received ${signal}, shutting down...`);
|
|
25853
|
+
try {
|
|
25854
|
+
watcherInstance?.stop();
|
|
25855
|
+
} catch {
|
|
25856
|
+
}
|
|
25857
|
+
stopSweepTimer();
|
|
25858
|
+
flushLogs().catch(() => {
|
|
25859
|
+
}).finally(() => process.exit(0));
|
|
25860
|
+
setTimeout(() => process.exit(0), 2e3).unref();
|
|
25861
|
+
}
|
|
25862
|
+
process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
|
|
25863
|
+
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
|
|
25730
25864
|
process.on("beforeExit", async () => {
|
|
25731
25865
|
stopSweepTimer();
|
|
25732
25866
|
await flushLogs();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "MCP tools that search, write, and auto-link your Obsidian vault
|
|
3
|
+
"version": "2.0.158",
|
|
4
|
+
"description": "MCP tools that search, write, and auto-link your Obsidian vault \u2014 and learn from your edits.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@huggingface/transformers": "^3.8.1",
|
|
56
56
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
57
|
-
"@velvetmonkey/vault-core": "^2.0.
|
|
57
|
+
"@velvetmonkey/vault-core": "^2.0.158",
|
|
58
58
|
"better-sqlite3": "^12.0.0",
|
|
59
59
|
"chokidar": "^4.0.0",
|
|
60
60
|
"gray-matter": "^4.0.3",
|