@velvetmonkey/flywheel-memory 2.0.75 → 2.0.77
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/README.md +37 -32
- package/dist/index.js +79 -84
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
[](https://github.com/velvetmonkey/flywheel-memory/blob/main/docs/TESTING.md#performance-benchmarks)
|
|
13
13
|
[](https://github.com/velvetmonkey/flywheel-memory/blob/main/docs/TESTING.md)
|
|
14
14
|
|
|
15
|
-
| |
|
|
15
|
+
| | Grep approach | Flywheel |
|
|
16
16
|
|---|---|---|
|
|
17
|
-
| "What's overdue?" |
|
|
18
|
-
| "What links here?" |
|
|
19
|
-
| "Add a meeting note" | Raw write, no linking |
|
|
20
|
-
| "What should I link?" |
|
|
21
|
-
|
|
|
17
|
+
| "What's overdue?" | Grep + read matches (~500-2,000 tokens) | Indexed metadata query (~50-200 tokens) |
|
|
18
|
+
| "What links here?" | Grep for note name (flat list, no graph) | Pre-indexed backlink graph (<10ms) |
|
|
19
|
+
| "Add a meeting note" | Raw write, no linking | Structured write + auto-wikilink |
|
|
20
|
+
| "What should I link?" | Not possible | 10-dimension scoring + semantic search |
|
|
21
|
+
| Hubs, orphans, paths? | Not possible | Pre-indexed graph analysis |
|
|
22
22
|
|
|
23
23
|
51 tools across 17 categories. 6-line config. Zero cloud dependencies.
|
|
24
24
|
|
|
@@ -35,8 +35,6 @@ Then ask: *"How much have I billed Acme Corp?"*
|
|
|
35
35
|
|
|
36
36
|
## See It Work
|
|
37
37
|
|
|
38
|
-

|
|
39
|
-
|
|
40
38
|
### Read: "How much have I billed Acme Corp?"
|
|
41
39
|
|
|
42
40
|
From the [carter-strategy](https://github.com/velvetmonkey/flywheel-memory/tree/main/demos/carter-strategy/) demo -- a solo consultant with 3 clients, 5 projects, and $27K in invoices.
|
|
@@ -44,49 +42,47 @@ From the [carter-strategy](https://github.com/velvetmonkey/flywheel-memory/tree/
|
|
|
44
42
|
```
|
|
45
43
|
❯ How much have I billed Acme Corp?
|
|
46
44
|
|
|
47
|
-
● flywheel ›
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
invoices/INV-2025-048.md
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
● flywheel › search
|
|
46
|
+
query: "Acme Corp"
|
|
47
|
+
→ clients/Acme Corp.md, invoices/INV-2025-047.md,
|
|
48
|
+
invoices/INV-2025-048.md, projects/Acme Data Migration.md
|
|
49
|
+
|
|
50
|
+
● read_file("clients/Acme Corp.md")
|
|
51
|
+
← total_billed: 156000, rate: $300/hr, status: active
|
|
53
52
|
|
|
54
|
-
●
|
|
55
|
-
path: "invoices/INV-2025-047.md"
|
|
53
|
+
● read_file("invoices/INV-2025-047.md")
|
|
56
54
|
← amount: 15000, status: paid, period: November 2025
|
|
57
55
|
|
|
58
|
-
●
|
|
59
|
-
path: "invoices/INV-2025-048.md"
|
|
56
|
+
● read_file("invoices/INV-2025-048.md")
|
|
60
57
|
← amount: 12000, status: pending, period: December 2025
|
|
61
58
|
|
|
62
59
|
┌─ RESULT ──────────────────────────────────────────────┐
|
|
63
|
-
│ Acme Corp
|
|
60
|
+
│ Acme Corp: $156K total billed │
|
|
64
61
|
│ │
|
|
65
62
|
│ Paid: $15,000 — Acme Data Migration (Nov 2025) │
|
|
66
63
|
│ Pending: $12,000 — Acme Data Migration (Dec 2025) │
|
|
67
64
|
│ │
|
|
68
|
-
│
|
|
65
|
+
│ Also: $35K pending proposal (Analytics Add-on) │
|
|
69
66
|
└───────────────────────────────────────────────────────┘
|
|
70
67
|
```
|
|
71
68
|
|
|
72
|
-
|
|
69
|
+
Flywheel's indexed search found all Acme-related notes in one call. Claude read the files it needed for billing details. No grepping, no guessing paths.
|
|
73
70
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
37x savings
|
|
78
|
-
```
|
|
71
|
+
Flywheel's search found all related notes in one call. Without it, Claude would grep for "Acme" and scan every matching file.
|
|
72
|
+
|
|
73
|
+
The bigger difference isn't just tokens — it's that Flywheel answers structural questions (backlinks, hubs, shortest paths, schema analysis) that file-level access can't answer at all.
|
|
79
74
|
|
|
80
75
|
### Write: Auto-wikilinks on every mutation
|
|
81
76
|
|
|
82
77
|
```
|
|
83
|
-
❯ Log that
|
|
78
|
+
❯ Log that Stacy Thompson reviewed the API Security Checklist for Acme before the Beta Corp Dashboard kickoff
|
|
84
79
|
|
|
85
80
|
● flywheel › vault_add_to_section
|
|
86
81
|
path: "daily-notes/2026-01-04.md"
|
|
87
82
|
section: "Log"
|
|
88
|
-
content: "
|
|
89
|
-
|
|
83
|
+
content: "[[Stacy Thompson]] reviewed the [[API Security Checklist]] for [[Acme Corp|Acme]] before the [[Beta Corp Dashboard]] kickoff → [[GlobalBank API Audit]], [[Acme Analytics Add-on]], [[Acme Data Migration]]"
|
|
84
|
+
↑ 4 entities auto-linked — "Acme" resolved to Acme Corp via alias
|
|
85
|
+
→ 3 contextual suggestions appended (scored ≥12 via co-occurrence with linked entities)
|
|
90
86
|
```
|
|
91
87
|
|
|
92
88
|
Try it yourself: `cd demos/carter-strategy && claude`
|
|
@@ -184,9 +180,18 @@ Every query leverages hundreds of accumulated connections. New content auto-link
|
|
|
184
180
|
|
|
185
181
|
### What This Looks Like
|
|
186
182
|
|
|
183
|
+
```mermaid
|
|
184
|
+
graph LR
|
|
185
|
+
W[Write] --> A[Auto-link]
|
|
186
|
+
A --> D[Denser Graph]
|
|
187
|
+
D --> B[Better Queries]
|
|
188
|
+
B --> M[More Use]
|
|
189
|
+
M --> W
|
|
190
|
+
```
|
|
191
|
+
|
|
187
192
|
```
|
|
188
|
-
Input: "
|
|
189
|
-
Output: "
|
|
193
|
+
Input: "Stacy Thompson finished reviewing the API Security Checklist for the Beta Corp Dashboard"
|
|
194
|
+
Output: "[[Stacy Thompson]] finished reviewing the [[API Security Checklist]] for the [[Beta Corp Dashboard]]"
|
|
190
195
|
```
|
|
191
196
|
|
|
192
197
|
No manual linking. No broken references. Use compounds into structure, structure compounds into intelligence.
|
|
@@ -236,7 +241,7 @@ Measured against a 96-note/61-entity ground truth vault.
|
|
|
236
241
|
|
|
237
242
|
- **50-generation stress test** — suggest → accept/reject (85% correct, 15% noise) → mutate vault → rebuild index → repeat. F1 holds steady — the feedback loop doesn't degrade under realistic noise.
|
|
238
243
|
- **7 vault archetypes** — hub-and-spoke, hierarchical, dense-mesh, sparse-orphan, bridge-network, small-world, chaos
|
|
239
|
-
- **13
|
|
244
|
+
- **13 pipeline stages** (10 scoring dimensions + filters + suppression) individually ablated, contribution measured
|
|
240
245
|
- **Regression gate** — CI fails if any mode's F1/precision/recall drops >5pp from baseline
|
|
241
246
|
|
|
242
247
|
See [docs/TESTING.md](https://github.com/velvetmonkey/flywheel-memory/blob/main/docs/TESTING.md) for full methodology. Auto-generated report: [docs/QUALITY_REPORT.md](https://github.com/velvetmonkey/flywheel-memory/blob/main/docs/QUALITY_REPORT.md).
|
package/dist/index.js
CHANGED
|
@@ -1886,24 +1886,42 @@ async function initEmbeddings() {
|
|
|
1886
1886
|
if (pipeline) return;
|
|
1887
1887
|
if (initPromise) return initPromise;
|
|
1888
1888
|
initPromise = (async () => {
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1889
|
+
const MAX_RETRIES = 3;
|
|
1890
|
+
const RETRY_DELAYS = [2e3, 5e3, 1e4];
|
|
1891
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
1892
|
+
try {
|
|
1893
|
+
const transformers = await Function("specifier", "return import(specifier)")("@huggingface/transformers");
|
|
1894
|
+
console.error(`[Semantic] Loading model ${activeModelConfig.id} (~23MB, cached after first download)...`);
|
|
1895
|
+
pipeline = await transformers.pipeline("feature-extraction", activeModelConfig.id, {
|
|
1896
|
+
dtype: "fp32"
|
|
1897
|
+
});
|
|
1898
|
+
console.error(`[Semantic] Model loaded successfully`);
|
|
1899
|
+
if (activeModelConfig.dims === 0) {
|
|
1900
|
+
const probe = await pipeline("test", { pooling: "mean", normalize: true });
|
|
1901
|
+
activeModelConfig.dims = probe.data.length;
|
|
1902
|
+
console.error(`[Semantic] Probed model ${activeModelConfig.id}: ${activeModelConfig.dims} dims`);
|
|
1903
|
+
}
|
|
1904
|
+
return;
|
|
1905
|
+
} catch (err) {
|
|
1906
|
+
if (err instanceof Error && (err.message.includes("Cannot find package") || err.message.includes("MODULE_NOT_FOUND") || err.message.includes("Cannot find module") || err.message.includes("ERR_MODULE_NOT_FOUND"))) {
|
|
1907
|
+
initPromise = null;
|
|
1908
|
+
throw new Error(
|
|
1909
|
+
"Semantic search requires @huggingface/transformers. Install it with: npm install @huggingface/transformers"
|
|
1910
|
+
);
|
|
1911
|
+
}
|
|
1912
|
+
if (attempt < MAX_RETRIES) {
|
|
1913
|
+
const delay = RETRY_DELAYS[attempt - 1];
|
|
1914
|
+
console.error(`[Semantic] Model load failed (attempt ${attempt}/${MAX_RETRIES}): ${err instanceof Error ? err.message : err}`);
|
|
1915
|
+
console.error(`[Semantic] Retrying in ${delay / 1e3}s...`);
|
|
1916
|
+
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
1917
|
+
pipeline = null;
|
|
1918
|
+
} else {
|
|
1919
|
+
console.error(`[Semantic] Model load failed after ${MAX_RETRIES} attempts: ${err instanceof Error ? err.message : err}`);
|
|
1920
|
+
console.error(`[Semantic] Semantic search disabled. Keyword search (BM25) remains available.`);
|
|
1921
|
+
initPromise = null;
|
|
1922
|
+
throw err;
|
|
1923
|
+
}
|
|
1905
1924
|
}
|
|
1906
|
-
throw err;
|
|
1907
1925
|
}
|
|
1908
1926
|
})();
|
|
1909
1927
|
return initPromise;
|
|
@@ -2552,7 +2570,7 @@ function deserializeVaultIndex(data) {
|
|
|
2552
2570
|
builtAt: new Date(data.builtAt)
|
|
2553
2571
|
};
|
|
2554
2572
|
}
|
|
2555
|
-
function loadVaultIndexFromCache(stateDb2, scannedFileCount, maxAgeMs = 24 * 60 * 60 * 1e3, tolerancePercent = 5) {
|
|
2573
|
+
function loadVaultIndexFromCache(stateDb2, scannedFileCount, maxAgeMs = 24 * 60 * 60 * 1e3, tolerancePercent = 5, newestFileMtime) {
|
|
2556
2574
|
const info = getVaultIndexCacheInfo(stateDb2);
|
|
2557
2575
|
if (!info) {
|
|
2558
2576
|
console.error("[Flywheel] No cached index found");
|
|
@@ -2569,6 +2587,10 @@ function loadVaultIndexFromCache(stateDb2, scannedFileCount, maxAgeMs = 24 * 60
|
|
|
2569
2587
|
console.error(`[Flywheel] Cache invalid: too old (${Math.round(age / 1e3 / 60)} minutes)`);
|
|
2570
2588
|
return null;
|
|
2571
2589
|
}
|
|
2590
|
+
if (newestFileMtime && newestFileMtime.getTime() > info.builtAt.getTime()) {
|
|
2591
|
+
console.error(`[Flywheel] Cache invalid: files modified since cache was built (newest file: ${newestFileMtime.toISOString()}, cache built: ${info.builtAt.toISOString()})`);
|
|
2592
|
+
return null;
|
|
2593
|
+
}
|
|
2572
2594
|
const data = loadVaultIndexCache(stateDb2);
|
|
2573
2595
|
if (!data) {
|
|
2574
2596
|
console.error("[Flywheel] Failed to load cached index data");
|
|
@@ -6126,7 +6148,6 @@ function sortEntitiesByPriority(entities, notePath) {
|
|
|
6126
6148
|
}
|
|
6127
6149
|
function processWikilinks(content, notePath, existingContent) {
|
|
6128
6150
|
if (!isEntityIndexReady() || !entityIndex) {
|
|
6129
|
-
console.error("[Flywheel:DEBUG] Entity index not ready, entities:", entityIndex?._metadata?.total_entities ?? 0);
|
|
6130
6151
|
return {
|
|
6131
6152
|
content,
|
|
6132
6153
|
linksAdded: 0,
|
|
@@ -6134,7 +6155,6 @@ function processWikilinks(content, notePath, existingContent) {
|
|
|
6134
6155
|
};
|
|
6135
6156
|
}
|
|
6136
6157
|
let entities = getAllEntities(entityIndex);
|
|
6137
|
-
console.error(`[Flywheel:DEBUG] Processing wikilinks with ${entities.length} entities`);
|
|
6138
6158
|
if (moduleStateDb5) {
|
|
6139
6159
|
const folder = notePath ? notePath.split("/")[0] : void 0;
|
|
6140
6160
|
entities = entities.filter((e) => {
|
|
@@ -6239,39 +6259,6 @@ function maybeApplyWikilinks(content, skipWikilinks, notePath, existingContent)
|
|
|
6239
6259
|
}
|
|
6240
6260
|
return { content: result.content };
|
|
6241
6261
|
}
|
|
6242
|
-
function getEntityIndexStats() {
|
|
6243
|
-
if (!indexReady || !entityIndex) {
|
|
6244
|
-
return {
|
|
6245
|
-
ready: false,
|
|
6246
|
-
totalEntities: 0,
|
|
6247
|
-
categories: {},
|
|
6248
|
-
error: indexError2?.message
|
|
6249
|
-
};
|
|
6250
|
-
}
|
|
6251
|
-
return {
|
|
6252
|
-
ready: true,
|
|
6253
|
-
totalEntities: entityIndex._metadata.total_entities,
|
|
6254
|
-
categories: {
|
|
6255
|
-
technologies: entityIndex.technologies.length,
|
|
6256
|
-
acronyms: entityIndex.acronyms.length,
|
|
6257
|
-
people: entityIndex.people.length,
|
|
6258
|
-
projects: entityIndex.projects.length,
|
|
6259
|
-
organizations: entityIndex.organizations?.length ?? 0,
|
|
6260
|
-
locations: entityIndex.locations?.length ?? 0,
|
|
6261
|
-
concepts: entityIndex.concepts?.length ?? 0,
|
|
6262
|
-
animals: entityIndex.animals?.length ?? 0,
|
|
6263
|
-
media: entityIndex.media?.length ?? 0,
|
|
6264
|
-
events: entityIndex.events?.length ?? 0,
|
|
6265
|
-
documents: entityIndex.documents?.length ?? 0,
|
|
6266
|
-
vehicles: entityIndex.vehicles?.length ?? 0,
|
|
6267
|
-
health: entityIndex.health?.length ?? 0,
|
|
6268
|
-
finance: entityIndex.finance?.length ?? 0,
|
|
6269
|
-
food: entityIndex.food?.length ?? 0,
|
|
6270
|
-
hobbies: entityIndex.hobbies?.length ?? 0,
|
|
6271
|
-
other: entityIndex.other.length
|
|
6272
|
-
}
|
|
6273
|
-
};
|
|
6274
|
-
}
|
|
6275
6262
|
var SUGGESTION_PATTERN = /→\s*\[\[.+$/;
|
|
6276
6263
|
var GENERIC_WORDS = /* @__PURE__ */ new Set([
|
|
6277
6264
|
// Common nouns that appear everywhere and rarely mean anything specific
|
|
@@ -13540,12 +13527,6 @@ Example: vault_add_to_section({ path: "daily/2026-02-15.md", section: "Log", con
|
|
|
13540
13527
|
trackWikilinkApplications(stateDb2, notePath, linkedEntities);
|
|
13541
13528
|
}
|
|
13542
13529
|
}
|
|
13543
|
-
const _debug = {
|
|
13544
|
-
entityCount: getEntityIndexStats().totalEntities,
|
|
13545
|
-
indexReady: getEntityIndexStats().ready,
|
|
13546
|
-
skipWikilinks,
|
|
13547
|
-
wikilinkInfo: wikilinkInfo || "none"
|
|
13548
|
-
};
|
|
13549
13530
|
let suggestInfo;
|
|
13550
13531
|
if (suggestOutgoingLinks && !skipWikilinks && processedContent.length >= 100) {
|
|
13551
13532
|
const result = await suggestRelatedLinks(processedContent, { maxSuggestions, notePath });
|
|
@@ -13570,8 +13551,6 @@ Example: vault_add_to_section({ path: "daily/2026-02-15.md", section: "Log", con
|
|
|
13570
13551
|
updatedContent,
|
|
13571
13552
|
message: `Added content to section "${ctx.sectionBoundary.name}" in ${notePath}${createdInfo}`,
|
|
13572
13553
|
preview,
|
|
13573
|
-
_debug,
|
|
13574
|
-
// Temporary debug field for production troubleshooting
|
|
13575
13554
|
warnings: validationResult.inputWarnings.length > 0 ? validationResult.inputWarnings : void 0,
|
|
13576
13555
|
outputIssues: validationResult.outputIssues.length > 0 ? validationResult.outputIssues : void 0,
|
|
13577
13556
|
normalizationChanges: validationResult.normalizationChanges.length > 0 ? validationResult.normalizationChanges : void 0
|
|
@@ -19712,7 +19691,8 @@ async function main() {
|
|
|
19712
19691
|
const files = await scanVault(vaultPath);
|
|
19713
19692
|
const noteCount = files.length;
|
|
19714
19693
|
serverLog("index", `Found ${noteCount} markdown files`);
|
|
19715
|
-
|
|
19694
|
+
const newestMtime = files.reduce((max, f) => f.modified > max ? f.modified : max, /* @__PURE__ */ new Date(0));
|
|
19695
|
+
cachedIndex = loadVaultIndexFromCache(stateDb, noteCount, void 0, void 0, newestMtime);
|
|
19716
19696
|
} catch (err) {
|
|
19717
19697
|
serverLog("index", `Cache check failed: ${err instanceof Error ? err.message : err}`, "warn");
|
|
19718
19698
|
}
|
|
@@ -19913,30 +19893,45 @@ async function runPostIndexWork(index) {
|
|
|
19913
19893
|
if (hasEmbeddingsIndex() && !modelChanged) {
|
|
19914
19894
|
serverLog("semantic", "Embeddings already built, skipping full scan");
|
|
19915
19895
|
} else {
|
|
19916
|
-
|
|
19917
|
-
|
|
19918
|
-
|
|
19919
|
-
|
|
19920
|
-
|
|
19921
|
-
|
|
19922
|
-
|
|
19923
|
-
|
|
19924
|
-
|
|
19925
|
-
|
|
19926
|
-
|
|
19927
|
-
|
|
19928
|
-
|
|
19929
|
-
|
|
19930
|
-
|
|
19931
|
-
|
|
19896
|
+
const MAX_BUILD_RETRIES = 2;
|
|
19897
|
+
const attemptBuild = async (attempt) => {
|
|
19898
|
+
setEmbeddingsBuilding(true);
|
|
19899
|
+
try {
|
|
19900
|
+
await buildEmbeddingsIndex(vaultPath, (p) => {
|
|
19901
|
+
if (p.current % 100 === 0 || p.current === p.total) {
|
|
19902
|
+
serverLog("semantic", `Embedding ${p.current}/${p.total} notes...`);
|
|
19903
|
+
}
|
|
19904
|
+
});
|
|
19905
|
+
if (stateDb) {
|
|
19906
|
+
const entities = getAllEntitiesFromDb3(stateDb);
|
|
19907
|
+
if (entities.length > 0) {
|
|
19908
|
+
const entityMap = new Map(entities.map((e) => [e.name, {
|
|
19909
|
+
name: e.name,
|
|
19910
|
+
path: e.path,
|
|
19911
|
+
category: e.category,
|
|
19912
|
+
aliases: e.aliases
|
|
19913
|
+
}]));
|
|
19914
|
+
await buildEntityEmbeddingsIndex(vaultPath, entityMap);
|
|
19915
|
+
}
|
|
19932
19916
|
}
|
|
19917
|
+
loadEntityEmbeddingsToMemory();
|
|
19918
|
+
setEmbeddingsBuildState("complete");
|
|
19919
|
+
serverLog("semantic", "Embeddings ready \u2014 searches now use hybrid ranking");
|
|
19920
|
+
} catch (err) {
|
|
19921
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
19922
|
+
if (attempt < MAX_BUILD_RETRIES) {
|
|
19923
|
+
const delay = 1e4;
|
|
19924
|
+
serverLog("semantic", `Build failed (attempt ${attempt}/${MAX_BUILD_RETRIES}): ${msg}. Retrying in ${delay / 1e3}s...`, "error");
|
|
19925
|
+
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
19926
|
+
return attemptBuild(attempt + 1);
|
|
19927
|
+
}
|
|
19928
|
+
serverLog("semantic", `Embeddings build failed after ${MAX_BUILD_RETRIES} attempts: ${msg}`, "error");
|
|
19929
|
+
serverLog("semantic", "Keyword search (BM25) remains fully available", "error");
|
|
19930
|
+
} finally {
|
|
19931
|
+
setEmbeddingsBuilding(false);
|
|
19933
19932
|
}
|
|
19934
|
-
|
|
19935
|
-
|
|
19936
|
-
serverLog("semantic", "Embeddings ready");
|
|
19937
|
-
}).catch((err) => {
|
|
19938
|
-
serverLog("semantic", `Embeddings build failed: ${err instanceof Error ? err.message : err}`, "error");
|
|
19939
|
-
});
|
|
19933
|
+
};
|
|
19934
|
+
attemptBuild(1);
|
|
19940
19935
|
}
|
|
19941
19936
|
} else {
|
|
19942
19937
|
serverLog("semantic", "Skipping \u2014 FLYWHEEL_SKIP_EMBEDDINGS");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.77",
|
|
4
4
|
"description": "MCP server that gives Claude full read/write access to your Obsidian vault. Select from 51 tools for search, backlinks, graph queries, mutations, agent memory, and hybrid semantic search.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
55
|
-
"@velvetmonkey/vault-core": "^2.0.
|
|
55
|
+
"@velvetmonkey/vault-core": "^2.0.77",
|
|
56
56
|
"better-sqlite3": "^11.0.0",
|
|
57
57
|
"chokidar": "^4.0.0",
|
|
58
58
|
"gray-matter": "^4.0.3",
|