@velvetmonkey/flywheel-memory 2.0.31 → 2.0.33
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 +151 -36
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -2486,7 +2486,8 @@ import {
|
|
|
2486
2486
|
var DEFAULT_CONFIG = {
|
|
2487
2487
|
exclude_task_tags: [],
|
|
2488
2488
|
exclude_analysis_tags: [],
|
|
2489
|
-
exclude_entities: []
|
|
2489
|
+
exclude_entities: [],
|
|
2490
|
+
exclude_entity_folders: []
|
|
2490
2491
|
};
|
|
2491
2492
|
function loadConfig(stateDb2) {
|
|
2492
2493
|
if (stateDb2) {
|
|
@@ -3216,6 +3217,7 @@ import {
|
|
|
3216
3217
|
getEntityAliases,
|
|
3217
3218
|
applyWikilinks,
|
|
3218
3219
|
resolveAliasWikilinks,
|
|
3220
|
+
detectImplicitEntities,
|
|
3219
3221
|
getEntityIndexFromDb,
|
|
3220
3222
|
getStateDbMetadata,
|
|
3221
3223
|
getEntityByName,
|
|
@@ -3402,10 +3404,10 @@ function getAllFeedbackBoosts(stateDb2, folder) {
|
|
|
3402
3404
|
for (const row of globalRows) {
|
|
3403
3405
|
let accuracy;
|
|
3404
3406
|
let sampleCount;
|
|
3405
|
-
const
|
|
3406
|
-
if (
|
|
3407
|
-
accuracy =
|
|
3408
|
-
sampleCount =
|
|
3407
|
+
const fs31 = folderStats?.get(row.entity);
|
|
3408
|
+
if (fs31 && fs31.count >= FEEDBACK_BOOST_MIN_SAMPLES) {
|
|
3409
|
+
accuracy = fs31.accuracy;
|
|
3410
|
+
sampleCount = fs31.count;
|
|
3409
3411
|
} else {
|
|
3410
3412
|
accuracy = row.correct_count / row.total;
|
|
3411
3413
|
sampleCount = row.total;
|
|
@@ -5060,6 +5062,52 @@ function processWikilinks(content, notePath) {
|
|
|
5060
5062
|
firstOccurrenceOnly: true,
|
|
5061
5063
|
caseInsensitive: true
|
|
5062
5064
|
});
|
|
5065
|
+
const implicitMatches = detectImplicitEntities(result.content, {
|
|
5066
|
+
detectImplicit: true,
|
|
5067
|
+
implicitPatterns: ["proper-nouns", "single-caps", "quoted-terms", "camel-case", "acronyms"],
|
|
5068
|
+
minEntityLength: 3
|
|
5069
|
+
});
|
|
5070
|
+
const alreadyLinked = new Set(
|
|
5071
|
+
[...resolved.linkedEntities, ...result.linkedEntities].map((e) => e.toLowerCase())
|
|
5072
|
+
);
|
|
5073
|
+
for (const entity of sortedEntities) {
|
|
5074
|
+
const name = getEntityName2(entity);
|
|
5075
|
+
alreadyLinked.add(name.toLowerCase());
|
|
5076
|
+
const aliases = getEntityAliases(entity);
|
|
5077
|
+
for (const alias of aliases) {
|
|
5078
|
+
alreadyLinked.add(alias.toLowerCase());
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
const currentNoteName = notePath ? notePath.replace(/\.md$/, "").split("/").pop()?.toLowerCase() : null;
|
|
5082
|
+
let newImplicits = implicitMatches.filter((m) => {
|
|
5083
|
+
const normalized = m.text.toLowerCase();
|
|
5084
|
+
if (alreadyLinked.has(normalized)) return false;
|
|
5085
|
+
if (currentNoteName && normalized === currentNoteName) return false;
|
|
5086
|
+
return true;
|
|
5087
|
+
});
|
|
5088
|
+
const nonOverlapping = [];
|
|
5089
|
+
for (const match of newImplicits) {
|
|
5090
|
+
const overlaps = nonOverlapping.some(
|
|
5091
|
+
(existing) => match.start >= existing.start && match.start < existing.end || match.end > existing.start && match.end <= existing.end || match.start <= existing.start && match.end >= existing.end
|
|
5092
|
+
);
|
|
5093
|
+
if (!overlaps) {
|
|
5094
|
+
nonOverlapping.push(match);
|
|
5095
|
+
}
|
|
5096
|
+
}
|
|
5097
|
+
newImplicits = nonOverlapping;
|
|
5098
|
+
if (newImplicits.length > 0) {
|
|
5099
|
+
let processedContent = result.content;
|
|
5100
|
+
for (let i = newImplicits.length - 1; i >= 0; i--) {
|
|
5101
|
+
const m = newImplicits[i];
|
|
5102
|
+
processedContent = processedContent.slice(0, m.start) + `[[${m.text}]]` + processedContent.slice(m.end);
|
|
5103
|
+
}
|
|
5104
|
+
return {
|
|
5105
|
+
content: processedContent,
|
|
5106
|
+
linksAdded: resolved.linksAdded + result.linksAdded + newImplicits.length,
|
|
5107
|
+
linkedEntities: [...resolved.linkedEntities, ...result.linkedEntities],
|
|
5108
|
+
implicitEntities: newImplicits.map((m) => m.text)
|
|
5109
|
+
};
|
|
5110
|
+
}
|
|
5063
5111
|
return {
|
|
5064
5112
|
content: result.content,
|
|
5065
5113
|
linksAdded: resolved.linksAdded + result.linksAdded,
|
|
@@ -5076,9 +5124,11 @@ function maybeApplyWikilinks(content, skipWikilinks, notePath) {
|
|
|
5076
5124
|
if (moduleStateDb4 && notePath) {
|
|
5077
5125
|
trackWikilinkApplications(moduleStateDb4, notePath, result.linkedEntities);
|
|
5078
5126
|
}
|
|
5127
|
+
const implicitCount = result.implicitEntities?.length ?? 0;
|
|
5128
|
+
const implicitInfo = implicitCount > 0 ? ` + ${implicitCount} implicit: ${result.implicitEntities.join(", ")}` : "";
|
|
5079
5129
|
return {
|
|
5080
5130
|
content: result.content,
|
|
5081
|
-
wikilinkInfo: `Applied ${result.linksAdded} wikilink(s): ${result.linkedEntities.join(", ")}`
|
|
5131
|
+
wikilinkInfo: `Applied ${result.linksAdded} wikilink(s): ${result.linkedEntities.join(", ")}${implicitInfo}`
|
|
5082
5132
|
};
|
|
5083
5133
|
}
|
|
5084
5134
|
return { content: result.content };
|
|
@@ -7375,7 +7425,7 @@ function computeEntityDiff(before, after) {
|
|
|
7375
7425
|
const alias_changes = [];
|
|
7376
7426
|
for (const [key, entity] of afterMap) {
|
|
7377
7427
|
if (!beforeMap.has(key)) {
|
|
7378
|
-
added.push(entity.name);
|
|
7428
|
+
added.push({ name: entity.name, category: entity.category, path: entity.path });
|
|
7379
7429
|
} else {
|
|
7380
7430
|
const prev = beforeMap.get(key);
|
|
7381
7431
|
const prevAliases = JSON.stringify(prev.aliases.sort());
|
|
@@ -7387,7 +7437,7 @@ function computeEntityDiff(before, after) {
|
|
|
7387
7437
|
}
|
|
7388
7438
|
for (const [key, entity] of beforeMap) {
|
|
7389
7439
|
if (!afterMap.has(key)) {
|
|
7390
|
-
removed.push(entity.name);
|
|
7440
|
+
removed.push({ name: entity.name, category: entity.category, path: entity.path });
|
|
7391
7441
|
}
|
|
7392
7442
|
}
|
|
7393
7443
|
return { added, removed, alias_changes };
|
|
@@ -7481,6 +7531,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
|
|
|
7481
7531
|
trigger: z3.string(),
|
|
7482
7532
|
duration_ms: z3.number(),
|
|
7483
7533
|
files_changed: z3.number().nullable(),
|
|
7534
|
+
changed_paths: z3.array(z3.string()).nullable(),
|
|
7484
7535
|
steps: z3.array(z3.object({
|
|
7485
7536
|
name: z3.string(),
|
|
7486
7537
|
duration_ms: z3.number(),
|
|
@@ -7490,6 +7541,21 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
|
|
|
7490
7541
|
skip_reason: z3.string().optional()
|
|
7491
7542
|
}))
|
|
7492
7543
|
}).optional().describe("Most recent watcher pipeline run with per-step timing"),
|
|
7544
|
+
recent_pipelines: z3.array(z3.object({
|
|
7545
|
+
timestamp: z3.number(),
|
|
7546
|
+
trigger: z3.string(),
|
|
7547
|
+
duration_ms: z3.number(),
|
|
7548
|
+
files_changed: z3.number().nullable(),
|
|
7549
|
+
changed_paths: z3.array(z3.string()).nullable(),
|
|
7550
|
+
steps: z3.array(z3.object({
|
|
7551
|
+
name: z3.string(),
|
|
7552
|
+
duration_ms: z3.number(),
|
|
7553
|
+
input: z3.record(z3.unknown()),
|
|
7554
|
+
output: z3.record(z3.unknown()),
|
|
7555
|
+
skipped: z3.boolean().optional(),
|
|
7556
|
+
skip_reason: z3.string().optional()
|
|
7557
|
+
}))
|
|
7558
|
+
})).optional().describe("Up to 5 most recent pipeline runs with steps data"),
|
|
7493
7559
|
fts5_ready: z3.boolean().describe("Whether the FTS5 keyword search index is ready"),
|
|
7494
7560
|
fts5_building: z3.boolean().describe("Whether the FTS5 keyword search index is currently building"),
|
|
7495
7561
|
embeddings_building: z3.boolean().describe("Whether semantic embeddings are currently building"),
|
|
@@ -7535,7 +7601,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
|
|
|
7535
7601
|
recommendations.push(`Index is ${Math.floor(indexAge / 60)} minutes old. Consider running refresh_index.`);
|
|
7536
7602
|
}
|
|
7537
7603
|
const noteCount = indexBuilt ? index.notes.size : 0;
|
|
7538
|
-
const
|
|
7604
|
+
const entityCount = indexBuilt ? index.entities.size : 0;
|
|
7539
7605
|
const tagCount = indexBuilt ? index.tags.size : 0;
|
|
7540
7606
|
let linkCount = 0;
|
|
7541
7607
|
if (indexBuilt) {
|
|
@@ -7587,6 +7653,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
|
|
|
7587
7653
|
}
|
|
7588
7654
|
}
|
|
7589
7655
|
let lastPipeline;
|
|
7656
|
+
let recentPipelines;
|
|
7590
7657
|
if (stateDb2) {
|
|
7591
7658
|
try {
|
|
7592
7659
|
const evt = getRecentPipelineEvent(stateDb2);
|
|
@@ -7596,11 +7663,26 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
|
|
|
7596
7663
|
trigger: evt.trigger,
|
|
7597
7664
|
duration_ms: evt.duration_ms,
|
|
7598
7665
|
files_changed: evt.files_changed,
|
|
7666
|
+
changed_paths: evt.changed_paths,
|
|
7599
7667
|
steps: evt.steps
|
|
7600
7668
|
};
|
|
7601
7669
|
}
|
|
7602
7670
|
} catch {
|
|
7603
7671
|
}
|
|
7672
|
+
try {
|
|
7673
|
+
const events = getRecentIndexEvents(stateDb2, 10).filter((e) => e.steps && e.steps.length > 0).slice(0, 5);
|
|
7674
|
+
if (events.length > 0) {
|
|
7675
|
+
recentPipelines = events.map((e) => ({
|
|
7676
|
+
timestamp: e.timestamp,
|
|
7677
|
+
trigger: e.trigger,
|
|
7678
|
+
duration_ms: e.duration_ms,
|
|
7679
|
+
files_changed: e.files_changed,
|
|
7680
|
+
changed_paths: e.changed_paths,
|
|
7681
|
+
steps: e.steps
|
|
7682
|
+
}));
|
|
7683
|
+
}
|
|
7684
|
+
} catch {
|
|
7685
|
+
}
|
|
7604
7686
|
}
|
|
7605
7687
|
const ftsState = getFTS5State();
|
|
7606
7688
|
const output = {
|
|
@@ -7615,13 +7697,14 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
|
|
|
7615
7697
|
index_age_seconds: indexAge,
|
|
7616
7698
|
index_stale: indexStale,
|
|
7617
7699
|
note_count: noteCount,
|
|
7618
|
-
entity_count:
|
|
7700
|
+
entity_count: entityCount,
|
|
7619
7701
|
tag_count: tagCount,
|
|
7620
7702
|
link_count: linkCount,
|
|
7621
7703
|
periodic_notes: periodicNotes && periodicNotes.length > 0 ? periodicNotes : void 0,
|
|
7622
7704
|
config: configInfo,
|
|
7623
7705
|
last_rebuild: lastRebuild,
|
|
7624
7706
|
last_pipeline: lastPipeline,
|
|
7707
|
+
recent_pipelines: recentPipelines,
|
|
7625
7708
|
fts5_ready: ftsState.ready,
|
|
7626
7709
|
fts5_building: ftsState.building,
|
|
7627
7710
|
embeddings_building: isEmbeddingsBuilding(),
|
|
@@ -14544,7 +14627,7 @@ function computeMetrics(index, stateDb2) {
|
|
|
14544
14627
|
}
|
|
14545
14628
|
}
|
|
14546
14629
|
const tagCount = index.tags.size;
|
|
14547
|
-
const
|
|
14630
|
+
const entityCount = index.entities.size;
|
|
14548
14631
|
const avgLinksPerNote = noteCount > 0 ? linkCount / noteCount : 0;
|
|
14549
14632
|
const possibleLinks = noteCount * (noteCount - 1);
|
|
14550
14633
|
const linkDensity = possibleLinks > 0 ? linkCount / possibleLinks : 0;
|
|
@@ -14566,7 +14649,7 @@ function computeMetrics(index, stateDb2) {
|
|
|
14566
14649
|
link_count: linkCount,
|
|
14567
14650
|
orphan_count: orphanCount,
|
|
14568
14651
|
tag_count: tagCount,
|
|
14569
|
-
entity_count:
|
|
14652
|
+
entity_count: entityCount,
|
|
14570
14653
|
avg_links_per_note: Math.round(avgLinksPerNote * 100) / 100,
|
|
14571
14654
|
link_density: Math.round(linkDensity * 1e4) / 1e4,
|
|
14572
14655
|
connected_ratio: Math.round(connectedRatio * 1e3) / 1e3,
|
|
@@ -15503,6 +15586,9 @@ function registerMergeTools2(server2, getStateDb) {
|
|
|
15503
15586
|
);
|
|
15504
15587
|
}
|
|
15505
15588
|
|
|
15589
|
+
// src/index.ts
|
|
15590
|
+
import * as fs30 from "node:fs/promises";
|
|
15591
|
+
|
|
15506
15592
|
// src/resources/vault.ts
|
|
15507
15593
|
function registerVaultResources(server2, getIndex) {
|
|
15508
15594
|
server2.registerResource(
|
|
@@ -15995,31 +16081,14 @@ async function main() {
|
|
|
15995
16081
|
}
|
|
15996
16082
|
}
|
|
15997
16083
|
}
|
|
16084
|
+
var DEFAULT_ENTITY_EXCLUDE_FOLDERS = ["node_modules", "templates", "attachments", "tmp"];
|
|
15998
16085
|
async function updateEntitiesInStateDb() {
|
|
15999
16086
|
if (!stateDb) return;
|
|
16000
16087
|
try {
|
|
16088
|
+
const config = loadConfig(stateDb);
|
|
16089
|
+
const excludeFolders = config.exclude_entity_folders?.length ? config.exclude_entity_folders : DEFAULT_ENTITY_EXCLUDE_FOLDERS;
|
|
16001
16090
|
const entityIndex2 = await scanVaultEntities3(vaultPath, {
|
|
16002
|
-
excludeFolders
|
|
16003
|
-
"daily-notes",
|
|
16004
|
-
"daily",
|
|
16005
|
-
"weekly",
|
|
16006
|
-
"weekly-notes",
|
|
16007
|
-
"monthly",
|
|
16008
|
-
"monthly-notes",
|
|
16009
|
-
"quarterly",
|
|
16010
|
-
"yearly-notes",
|
|
16011
|
-
"periodic",
|
|
16012
|
-
"journal",
|
|
16013
|
-
"inbox",
|
|
16014
|
-
"templates",
|
|
16015
|
-
"attachments",
|
|
16016
|
-
"tmp",
|
|
16017
|
-
"clippings",
|
|
16018
|
-
"readwise",
|
|
16019
|
-
"articles",
|
|
16020
|
-
"bookmarks",
|
|
16021
|
-
"web-clips"
|
|
16022
|
-
]
|
|
16091
|
+
excludeFolders
|
|
16023
16092
|
});
|
|
16024
16093
|
stateDb.replaceAllEntities(entityIndex2);
|
|
16025
16094
|
serverLog("index", `Updated ${entityIndex2._metadata.total_entities} entities in StateDb`);
|
|
@@ -16138,9 +16207,22 @@ async function runPostIndexWork(index) {
|
|
|
16138
16207
|
const entityDiff = computeEntityDiff(entitiesBefore, entitiesAfter);
|
|
16139
16208
|
tracker.end({ entity_count: entitiesAfter.length, ...entityDiff });
|
|
16140
16209
|
serverLog("watcher", `Entity scan: ${entitiesAfter.length} entities`);
|
|
16141
|
-
|
|
16210
|
+
const hubBefore = /* @__PURE__ */ new Map();
|
|
16211
|
+
if (stateDb) {
|
|
16212
|
+
const rows = stateDb.db.prepare("SELECT name, hub_score FROM entities").all();
|
|
16213
|
+
for (const r of rows) hubBefore.set(r.name, r.hub_score);
|
|
16214
|
+
}
|
|
16215
|
+
tracker.start("hub_scores", { entity_count: entitiesAfter.length });
|
|
16142
16216
|
const hubUpdated = await exportHubScores(vaultIndex, stateDb);
|
|
16143
|
-
|
|
16217
|
+
const hubDiffs = [];
|
|
16218
|
+
if (stateDb) {
|
|
16219
|
+
const rows = stateDb.db.prepare("SELECT name, hub_score FROM entities").all();
|
|
16220
|
+
for (const r of rows) {
|
|
16221
|
+
const prev = hubBefore.get(r.name) ?? 0;
|
|
16222
|
+
if (prev !== r.hub_score) hubDiffs.push({ entity: r.name, before: prev, after: r.hub_score });
|
|
16223
|
+
}
|
|
16224
|
+
}
|
|
16225
|
+
tracker.end({ updated: hubUpdated ?? 0, diffs: hubDiffs.slice(0, 10) });
|
|
16144
16226
|
serverLog("watcher", `Hub scores: ${hubUpdated ?? 0} updated`);
|
|
16145
16227
|
if (hasEmbeddingsIndex()) {
|
|
16146
16228
|
tracker.start("note_embeddings", { files: batch.events.length });
|
|
@@ -16167,6 +16249,7 @@ async function runPostIndexWork(index) {
|
|
|
16167
16249
|
if (hasEntityEmbeddingsIndex() && stateDb) {
|
|
16168
16250
|
tracker.start("entity_embeddings", { files: batch.events.length });
|
|
16169
16251
|
let entEmbUpdated = 0;
|
|
16252
|
+
const entEmbNames = [];
|
|
16170
16253
|
try {
|
|
16171
16254
|
const allEntities = getAllEntitiesFromDb3(stateDb);
|
|
16172
16255
|
for (const event of batch.events) {
|
|
@@ -16180,11 +16263,12 @@ async function runPostIndexWork(index) {
|
|
|
16180
16263
|
aliases: entity.aliases
|
|
16181
16264
|
}, vaultPath);
|
|
16182
16265
|
entEmbUpdated++;
|
|
16266
|
+
entEmbNames.push(entity.name);
|
|
16183
16267
|
}
|
|
16184
16268
|
}
|
|
16185
16269
|
} catch {
|
|
16186
16270
|
}
|
|
16187
|
-
tracker.end({ updated: entEmbUpdated });
|
|
16271
|
+
tracker.end({ updated: entEmbUpdated, updated_entities: entEmbNames.slice(0, 10) });
|
|
16188
16272
|
serverLog("watcher", `Entity embeddings: ${entEmbUpdated} updated`);
|
|
16189
16273
|
} else {
|
|
16190
16274
|
tracker.skip("entity_embeddings", !stateDb ? "no stateDb" : "not built");
|
|
@@ -16219,6 +16303,37 @@ async function runPostIndexWork(index) {
|
|
|
16219
16303
|
}
|
|
16220
16304
|
tracker.end({ updated: taskUpdated, removed: taskRemoved });
|
|
16221
16305
|
serverLog("watcher", `Task cache: ${taskUpdated} updated, ${taskRemoved} removed`);
|
|
16306
|
+
tracker.start("wikilink_check", { files: batch.events.length });
|
|
16307
|
+
const trackedLinks = [];
|
|
16308
|
+
if (stateDb) {
|
|
16309
|
+
for (const event of batch.events) {
|
|
16310
|
+
if (event.type === "delete" || !event.path.endsWith(".md")) continue;
|
|
16311
|
+
try {
|
|
16312
|
+
const apps = getTrackedApplications(stateDb, event.path);
|
|
16313
|
+
if (apps.length > 0) trackedLinks.push({ file: event.path, entities: apps });
|
|
16314
|
+
} catch {
|
|
16315
|
+
}
|
|
16316
|
+
}
|
|
16317
|
+
}
|
|
16318
|
+
tracker.end({ tracked: trackedLinks });
|
|
16319
|
+
serverLog("watcher", `Wikilink check: ${trackedLinks.reduce((s, t) => s + t.entities.length, 0)} tracked links in ${trackedLinks.length} files`);
|
|
16320
|
+
tracker.start("implicit_feedback", { files: batch.events.length });
|
|
16321
|
+
const feedbackResults = [];
|
|
16322
|
+
if (stateDb) {
|
|
16323
|
+
for (const event of batch.events) {
|
|
16324
|
+
if (event.type === "delete" || !event.path.endsWith(".md")) continue;
|
|
16325
|
+
try {
|
|
16326
|
+
const content = await fs30.readFile(path29.join(vaultPath, event.path), "utf-8");
|
|
16327
|
+
const removed = processImplicitFeedback(stateDb, event.path, content);
|
|
16328
|
+
for (const entity of removed) feedbackResults.push({ entity, file: event.path });
|
|
16329
|
+
} catch {
|
|
16330
|
+
}
|
|
16331
|
+
}
|
|
16332
|
+
}
|
|
16333
|
+
tracker.end({ removals: feedbackResults });
|
|
16334
|
+
if (feedbackResults.length > 0) {
|
|
16335
|
+
serverLog("watcher", `Implicit feedback: ${feedbackResults.length} removals detected`);
|
|
16336
|
+
}
|
|
16222
16337
|
const duration = Date.now() - batchStart;
|
|
16223
16338
|
if (stateDb) {
|
|
16224
16339
|
recordIndexEvent(stateDb, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.33",
|
|
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.33",
|
|
54
54
|
"better-sqlite3": "^11.0.0",
|
|
55
55
|
"chokidar": "^4.0.0",
|
|
56
56
|
"gray-matter": "^4.0.3",
|