@velvetmonkey/flywheel-memory 2.0.120 → 2.0.122
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 +272 -14
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -7571,19 +7571,68 @@ function createVaultWatcher(options) {
|
|
|
7571
7571
|
}
|
|
7572
7572
|
|
|
7573
7573
|
// src/core/shared/hubExport.ts
|
|
7574
|
+
var EIGEN_ITERATIONS = 50;
|
|
7574
7575
|
function computeHubScores(index) {
|
|
7575
|
-
const
|
|
7576
|
+
const nodes = [];
|
|
7577
|
+
const nodeIdx = /* @__PURE__ */ new Map();
|
|
7578
|
+
const adj = [];
|
|
7576
7579
|
for (const note of index.notes.values()) {
|
|
7577
|
-
const
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
|
|
7580
|
+
const key = normalizeTarget(note.path);
|
|
7581
|
+
if (!nodeIdx.has(key)) {
|
|
7582
|
+
nodeIdx.set(key, nodes.length);
|
|
7583
|
+
nodes.push(key);
|
|
7584
|
+
adj.push([]);
|
|
7585
|
+
}
|
|
7586
|
+
const titleKey = note.title.toLowerCase();
|
|
7587
|
+
if (!nodeIdx.has(titleKey)) {
|
|
7588
|
+
nodeIdx.set(titleKey, nodeIdx.get(key));
|
|
7584
7589
|
}
|
|
7585
7590
|
}
|
|
7586
|
-
|
|
7591
|
+
const N = nodes.length;
|
|
7592
|
+
if (N === 0) return /* @__PURE__ */ new Map();
|
|
7593
|
+
for (const note of index.notes.values()) {
|
|
7594
|
+
const fromIdx = nodeIdx.get(normalizeTarget(note.path));
|
|
7595
|
+
if (fromIdx === void 0) continue;
|
|
7596
|
+
for (const link of note.outlinks) {
|
|
7597
|
+
const target = normalizeTarget(link.target);
|
|
7598
|
+
let toIdx = nodeIdx.get(target);
|
|
7599
|
+
if (toIdx === void 0) {
|
|
7600
|
+
toIdx = nodeIdx.get(link.target.toLowerCase());
|
|
7601
|
+
}
|
|
7602
|
+
if (toIdx !== void 0 && toIdx !== fromIdx) {
|
|
7603
|
+
adj[fromIdx].push(toIdx);
|
|
7604
|
+
adj[toIdx].push(fromIdx);
|
|
7605
|
+
}
|
|
7606
|
+
}
|
|
7607
|
+
}
|
|
7608
|
+
let scores = new Float64Array(N).fill(1 / N);
|
|
7609
|
+
for (let iter = 0; iter < EIGEN_ITERATIONS; iter++) {
|
|
7610
|
+
const next = new Float64Array(N);
|
|
7611
|
+
for (let i = 0; i < N; i++) {
|
|
7612
|
+
for (const j of adj[i]) {
|
|
7613
|
+
next[i] += scores[j];
|
|
7614
|
+
}
|
|
7615
|
+
}
|
|
7616
|
+
let norm = 0;
|
|
7617
|
+
for (let i = 0; i < N; i++) norm += next[i] * next[i];
|
|
7618
|
+
norm = Math.sqrt(norm);
|
|
7619
|
+
if (norm > 0) {
|
|
7620
|
+
for (let i = 0; i < N; i++) next[i] /= norm;
|
|
7621
|
+
}
|
|
7622
|
+
scores = next;
|
|
7623
|
+
}
|
|
7624
|
+
let maxScore = 0;
|
|
7625
|
+
for (let i = 0; i < N; i++) {
|
|
7626
|
+
if (scores[i] > maxScore) maxScore = scores[i];
|
|
7627
|
+
}
|
|
7628
|
+
const result = /* @__PURE__ */ new Map();
|
|
7629
|
+
for (let i = 0; i < N; i++) {
|
|
7630
|
+
const scaled = maxScore > 0 ? Math.round(scores[i] / maxScore * 100) : 0;
|
|
7631
|
+
if (scaled > 0) {
|
|
7632
|
+
result.set(nodes[i], scaled);
|
|
7633
|
+
}
|
|
7634
|
+
}
|
|
7635
|
+
return result;
|
|
7587
7636
|
}
|
|
7588
7637
|
function updateHubScoresInDb(stateDb2, hubScores) {
|
|
7589
7638
|
const updateStmt = stateDb2.db.prepare(`
|
|
@@ -8510,6 +8559,162 @@ function getConnectionStrength(index, noteAPath, noteBPath) {
|
|
|
8510
8559
|
}
|
|
8511
8560
|
return { score, factors };
|
|
8512
8561
|
}
|
|
8562
|
+
function computeCentralityMetrics(index, limit = 20) {
|
|
8563
|
+
const paths = Array.from(index.notes.keys());
|
|
8564
|
+
const N = paths.length;
|
|
8565
|
+
if (N === 0) return [];
|
|
8566
|
+
const pathIdx = /* @__PURE__ */ new Map();
|
|
8567
|
+
paths.forEach((p, i) => pathIdx.set(p, i));
|
|
8568
|
+
for (const note of index.notes.values()) {
|
|
8569
|
+
const normalized = note.title.toLowerCase();
|
|
8570
|
+
if (!pathIdx.has(normalized)) {
|
|
8571
|
+
pathIdx.set(normalized, pathIdx.get(note.path));
|
|
8572
|
+
}
|
|
8573
|
+
}
|
|
8574
|
+
const outAdj = Array.from({ length: N }, () => []);
|
|
8575
|
+
const inAdj = Array.from({ length: N }, () => []);
|
|
8576
|
+
for (const note of index.notes.values()) {
|
|
8577
|
+
const fromIdx = pathIdx.get(note.path);
|
|
8578
|
+
if (fromIdx === void 0) continue;
|
|
8579
|
+
for (const link of note.outlinks) {
|
|
8580
|
+
let toIdx = pathIdx.get(link.target);
|
|
8581
|
+
if (toIdx === void 0) toIdx = pathIdx.get(link.target.toLowerCase());
|
|
8582
|
+
if (toIdx !== void 0 && toIdx !== fromIdx) {
|
|
8583
|
+
outAdj[fromIdx].push(toIdx);
|
|
8584
|
+
inAdj[toIdx].push(fromIdx);
|
|
8585
|
+
}
|
|
8586
|
+
}
|
|
8587
|
+
}
|
|
8588
|
+
const degree = new Float64Array(N);
|
|
8589
|
+
for (let i = 0; i < N; i++) {
|
|
8590
|
+
degree[i] = outAdj[i].length + inAdj[i].length;
|
|
8591
|
+
}
|
|
8592
|
+
const betweenness = new Float64Array(N);
|
|
8593
|
+
const closenessSum = new Float64Array(N);
|
|
8594
|
+
const reachable = new Int32Array(N);
|
|
8595
|
+
const undirAdj = Array.from({ length: N }, () => []);
|
|
8596
|
+
for (let i = 0; i < N; i++) {
|
|
8597
|
+
for (const j of outAdj[i]) {
|
|
8598
|
+
undirAdj[i].push(j);
|
|
8599
|
+
undirAdj[j].push(i);
|
|
8600
|
+
}
|
|
8601
|
+
}
|
|
8602
|
+
for (let s = 0; s < N; s++) {
|
|
8603
|
+
const stack = [];
|
|
8604
|
+
const pred = Array.from({ length: N }, () => []);
|
|
8605
|
+
const sigma = new Float64Array(N);
|
|
8606
|
+
const dist = new Int32Array(N).fill(-1);
|
|
8607
|
+
sigma[s] = 1;
|
|
8608
|
+
dist[s] = 0;
|
|
8609
|
+
const queue = [s];
|
|
8610
|
+
let head = 0;
|
|
8611
|
+
while (head < queue.length) {
|
|
8612
|
+
const v = queue[head++];
|
|
8613
|
+
stack.push(v);
|
|
8614
|
+
for (const w of undirAdj[v]) {
|
|
8615
|
+
if (dist[w] < 0) {
|
|
8616
|
+
dist[w] = dist[v] + 1;
|
|
8617
|
+
queue.push(w);
|
|
8618
|
+
}
|
|
8619
|
+
if (dist[w] === dist[v] + 1) {
|
|
8620
|
+
sigma[w] += sigma[v];
|
|
8621
|
+
pred[w].push(v);
|
|
8622
|
+
}
|
|
8623
|
+
}
|
|
8624
|
+
}
|
|
8625
|
+
for (let t = 0; t < N; t++) {
|
|
8626
|
+
if (dist[t] > 0) {
|
|
8627
|
+
closenessSum[s] += dist[t];
|
|
8628
|
+
reachable[s]++;
|
|
8629
|
+
}
|
|
8630
|
+
}
|
|
8631
|
+
const delta = new Float64Array(N);
|
|
8632
|
+
while (stack.length > 0) {
|
|
8633
|
+
const w = stack.pop();
|
|
8634
|
+
for (const v of pred[w]) {
|
|
8635
|
+
delta[v] += sigma[v] / sigma[w] * (1 + delta[w]);
|
|
8636
|
+
}
|
|
8637
|
+
if (w !== s) {
|
|
8638
|
+
betweenness[w] += delta[w];
|
|
8639
|
+
}
|
|
8640
|
+
}
|
|
8641
|
+
}
|
|
8642
|
+
const normFactor = N > 2 ? (N - 1) * (N - 2) : 1;
|
|
8643
|
+
const results = [];
|
|
8644
|
+
for (let i = 0; i < N; i++) {
|
|
8645
|
+
const note = index.notes.get(paths[i]);
|
|
8646
|
+
results.push({
|
|
8647
|
+
path: paths[i],
|
|
8648
|
+
title: note?.title || paths[i],
|
|
8649
|
+
degree: degree[i],
|
|
8650
|
+
betweenness: Math.round(betweenness[i] / normFactor * 1e4) / 1e4,
|
|
8651
|
+
closeness: reachable[i] > 0 ? Math.round(reachable[i] / closenessSum[i] * 1e4) / 1e4 : 0
|
|
8652
|
+
});
|
|
8653
|
+
}
|
|
8654
|
+
results.sort((a, b) => b.betweenness - a.betweenness);
|
|
8655
|
+
return results.slice(0, limit);
|
|
8656
|
+
}
|
|
8657
|
+
function detectCycles(index, maxLength = 10, limit = 20) {
|
|
8658
|
+
const paths = Array.from(index.notes.keys());
|
|
8659
|
+
const N = paths.length;
|
|
8660
|
+
if (N === 0) return [];
|
|
8661
|
+
const pathIdx = /* @__PURE__ */ new Map();
|
|
8662
|
+
paths.forEach((p, i) => pathIdx.set(p, i));
|
|
8663
|
+
for (const note of index.notes.values()) {
|
|
8664
|
+
const normalized = note.title.toLowerCase();
|
|
8665
|
+
if (!pathIdx.has(normalized)) {
|
|
8666
|
+
pathIdx.set(normalized, pathIdx.get(note.path));
|
|
8667
|
+
}
|
|
8668
|
+
}
|
|
8669
|
+
const adj = Array.from({ length: N }, () => []);
|
|
8670
|
+
for (const note of index.notes.values()) {
|
|
8671
|
+
const fromIdx = pathIdx.get(note.path);
|
|
8672
|
+
if (fromIdx === void 0) continue;
|
|
8673
|
+
for (const link of note.outlinks) {
|
|
8674
|
+
let toIdx = pathIdx.get(link.target);
|
|
8675
|
+
if (toIdx === void 0) toIdx = pathIdx.get(link.target.toLowerCase());
|
|
8676
|
+
if (toIdx !== void 0 && toIdx !== fromIdx) {
|
|
8677
|
+
adj[fromIdx].push(toIdx);
|
|
8678
|
+
}
|
|
8679
|
+
}
|
|
8680
|
+
}
|
|
8681
|
+
const WHITE = 0, GRAY = 1, BLACK = 2;
|
|
8682
|
+
const color = new Uint8Array(N);
|
|
8683
|
+
const cycles = [];
|
|
8684
|
+
const stack = [];
|
|
8685
|
+
function dfs(u) {
|
|
8686
|
+
if (cycles.length >= limit) return;
|
|
8687
|
+
color[u] = GRAY;
|
|
8688
|
+
stack.push(u);
|
|
8689
|
+
for (const v of adj[u]) {
|
|
8690
|
+
if (cycles.length >= limit) return;
|
|
8691
|
+
if (color[v] === GRAY) {
|
|
8692
|
+
const cycleStart = stack.indexOf(v);
|
|
8693
|
+
if (cycleStart >= 0) {
|
|
8694
|
+
const cyclePath = stack.slice(cycleStart);
|
|
8695
|
+
if (cyclePath.length <= maxLength) {
|
|
8696
|
+
const note = (idx) => index.notes.get(paths[idx])?.title || paths[idx];
|
|
8697
|
+
cycles.push({
|
|
8698
|
+
cycle: cyclePath.map(note),
|
|
8699
|
+
length: cyclePath.length
|
|
8700
|
+
});
|
|
8701
|
+
}
|
|
8702
|
+
}
|
|
8703
|
+
} else if (color[v] === WHITE) {
|
|
8704
|
+
dfs(v);
|
|
8705
|
+
}
|
|
8706
|
+
}
|
|
8707
|
+
stack.pop();
|
|
8708
|
+
color[u] = BLACK;
|
|
8709
|
+
}
|
|
8710
|
+
for (let i = 0; i < N; i++) {
|
|
8711
|
+
if (color[i] === WHITE && cycles.length < limit) {
|
|
8712
|
+
dfs(i);
|
|
8713
|
+
}
|
|
8714
|
+
}
|
|
8715
|
+
cycles.sort((a, b) => a.length - b.length);
|
|
8716
|
+
return cycles;
|
|
8717
|
+
}
|
|
8513
8718
|
|
|
8514
8719
|
// src/core/read/indexGuard.ts
|
|
8515
8720
|
function requireIndex() {
|
|
@@ -9802,6 +10007,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
9802
10007
|
});
|
|
9803
10008
|
const HealthCheckOutputSchema = {
|
|
9804
10009
|
status: z3.enum(["healthy", "degraded", "unhealthy"]).describe("Overall health status"),
|
|
10010
|
+
vault_health_score: z3.coerce.number().describe("Composite vault health score (0-100)"),
|
|
9805
10011
|
schema_version: z3.coerce.number().describe("StateDb schema version"),
|
|
9806
10012
|
vault_accessible: z3.boolean().describe("Whether the vault path is accessible"),
|
|
9807
10013
|
vault_path: z3.string().describe("The vault path being used"),
|
|
@@ -9908,17 +10114,18 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
9908
10114
|
vaultAccessible = false;
|
|
9909
10115
|
recommendations.push("Vault path is not accessible. Check PROJECT_PATH environment variable.");
|
|
9910
10116
|
}
|
|
10117
|
+
let dbIntegrityFailed = false;
|
|
9911
10118
|
const stateDb2 = getStateDb2();
|
|
9912
10119
|
if (stateDb2) {
|
|
9913
10120
|
try {
|
|
9914
10121
|
const result = stateDb2.db.pragma("quick_check");
|
|
9915
10122
|
const ok = result.length === 1 && Object.values(result[0])[0] === "ok";
|
|
9916
10123
|
if (!ok) {
|
|
9917
|
-
|
|
10124
|
+
dbIntegrityFailed = true;
|
|
9918
10125
|
recommendations.push(`Database integrity check failed: ${Object.values(result[0])[0] ?? "unknown error"}`);
|
|
9919
10126
|
}
|
|
9920
10127
|
} catch (err) {
|
|
9921
|
-
|
|
10128
|
+
dbIntegrityFailed = true;
|
|
9922
10129
|
recommendations.push(`Database integrity check error: ${err instanceof Error ? err.message : err}`);
|
|
9923
10130
|
}
|
|
9924
10131
|
}
|
|
@@ -9945,7 +10152,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
9945
10152
|
recommendations.push("No notes found in vault. Is PROJECT_PATH pointing to a markdown vault?");
|
|
9946
10153
|
}
|
|
9947
10154
|
let status;
|
|
9948
|
-
if (!vaultAccessible || indexState2 === "error") {
|
|
10155
|
+
if (!vaultAccessible || indexState2 === "error" || dbIntegrityFailed) {
|
|
9949
10156
|
status = "unhealthy";
|
|
9950
10157
|
} else if (indexState2 === "building" || indexStale || recommendations.length > 0) {
|
|
9951
10158
|
status = "degraded";
|
|
@@ -10032,8 +10239,37 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
10032
10239
|
}
|
|
10033
10240
|
}
|
|
10034
10241
|
const topDeadLinkTargets = Array.from(deadTargetCounts.entries()).map(([target, mention_count]) => ({ target, mention_count })).sort((a, b) => b.mention_count - a.mention_count).slice(0, 5);
|
|
10242
|
+
let vault_health_score = 0;
|
|
10243
|
+
if (indexBuilt && noteCount > 0) {
|
|
10244
|
+
const avgOutlinks = linkCount / noteCount;
|
|
10245
|
+
const linkDensity = Math.min(1, avgOutlinks / 3);
|
|
10246
|
+
let orphanCount = 0;
|
|
10247
|
+
for (const note of index.notes.values()) {
|
|
10248
|
+
const bl = index.backlinks.get(note.title.toLowerCase());
|
|
10249
|
+
if (!bl || bl.length === 0) orphanCount++;
|
|
10250
|
+
}
|
|
10251
|
+
const orphanRatio = 1 - orphanCount / noteCount;
|
|
10252
|
+
const totalLinks = linkCount > 0 ? linkCount : 1;
|
|
10253
|
+
const deadLinkRatio = 1 - deadLinkCount / totalLinks;
|
|
10254
|
+
let notesWithFm = 0;
|
|
10255
|
+
for (const note of index.notes.values()) {
|
|
10256
|
+
if (Object.keys(note.frontmatter).length > 0) notesWithFm++;
|
|
10257
|
+
}
|
|
10258
|
+
const fmCoverage = notesWithFm / noteCount;
|
|
10259
|
+
const freshCutoff = Date.now() - 90 * 24 * 60 * 60 * 1e3;
|
|
10260
|
+
let freshCount = 0;
|
|
10261
|
+
for (const note of index.notes.values()) {
|
|
10262
|
+
if (note.modified && note.modified.getTime() > freshCutoff) freshCount++;
|
|
10263
|
+
}
|
|
10264
|
+
const freshness = freshCount / noteCount;
|
|
10265
|
+
const entityCoverage = Math.min(1, entityCount / (noteCount * 0.5));
|
|
10266
|
+
vault_health_score = Math.round(
|
|
10267
|
+
linkDensity * 25 + orphanRatio * 20 + deadLinkRatio * 15 + fmCoverage * 15 + freshness * 15 + entityCoverage * 10
|
|
10268
|
+
);
|
|
10269
|
+
}
|
|
10035
10270
|
const output = {
|
|
10036
10271
|
status,
|
|
10272
|
+
vault_health_score,
|
|
10037
10273
|
schema_version: SCHEMA_VERSION,
|
|
10038
10274
|
vault_accessible: vaultAccessible,
|
|
10039
10275
|
vault_path: vaultPath2,
|
|
@@ -12898,9 +13134,9 @@ function registerGraphAnalysisTools(server2, getIndex, getVaultPath, getStateDb2
|
|
|
12898
13134
|
"graph_analysis",
|
|
12899
13135
|
{
|
|
12900
13136
|
title: "Graph Analysis",
|
|
12901
|
-
description: 'Analyze vault link graph structure. Use analysis to pick the mode:\n- "orphans": Notes with no backlinks (params: folder, limit, offset)\n- "dead_ends": Notes with backlinks but no outgoing links (params: folder, min_backlinks, limit, offset)\n- "sources": Notes with outgoing links but no backlinks (params: folder, min_outlinks, limit, offset)\n- "hubs": Highly connected notes (params: min_links, limit, offset)\n- "stale": Important notes not recently modified (params: days [required], min_backlinks, limit, offset)\n- "immature": Notes scored by maturity (params: folder, limit, offset)\n- "emerging_hubs": Entities growing fastest in connection count (params: days, limit, offset)',
|
|
13137
|
+
description: 'Analyze vault link graph structure. Use analysis to pick the mode:\n- "orphans": Notes with no backlinks (params: folder, limit, offset)\n- "dead_ends": Notes with backlinks but no outgoing links (params: folder, min_backlinks, limit, offset)\n- "sources": Notes with outgoing links but no backlinks (params: folder, min_outlinks, limit, offset)\n- "hubs": Highly connected notes (params: min_links, limit, offset)\n- "stale": Important notes not recently modified (params: days [required], min_backlinks, limit, offset)\n- "immature": Notes scored by maturity (params: folder, limit, offset)\n- "emerging_hubs": Entities growing fastest in connection count (params: days, limit, offset)\n- "centrality": Degree, betweenness, and closeness centrality metrics (params: limit, offset)\n- "cycles": Detect circular link chains in the vault (params: limit)',
|
|
12902
13138
|
inputSchema: {
|
|
12903
|
-
analysis: z8.enum(["orphans", "dead_ends", "sources", "hubs", "stale", "immature", "emerging_hubs"]).describe("Type of graph analysis to perform"),
|
|
13139
|
+
analysis: z8.enum(["orphans", "dead_ends", "sources", "hubs", "stale", "immature", "emerging_hubs", "centrality", "cycles"]).describe("Type of graph analysis to perform"),
|
|
12904
13140
|
folder: z8.string().optional().describe("Limit to notes in this folder (orphans, dead_ends, sources, immature)"),
|
|
12905
13141
|
min_links: z8.coerce.number().default(5).describe("Minimum total connections for hubs"),
|
|
12906
13142
|
min_backlinks: z8.coerce.number().default(1).describe("Minimum backlinks (dead_ends, stale)"),
|
|
@@ -13099,6 +13335,28 @@ function registerGraphAnalysisTools(server2, getIndex, getVaultPath, getStateDb2
|
|
|
13099
13335
|
}, null, 2) }]
|
|
13100
13336
|
};
|
|
13101
13337
|
}
|
|
13338
|
+
case "centrality": {
|
|
13339
|
+
const results = computeCentralityMetrics(index, limit);
|
|
13340
|
+
const paginated = results.slice(offset, offset + limit);
|
|
13341
|
+
return {
|
|
13342
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
13343
|
+
analysis: "centrality",
|
|
13344
|
+
total_count: results.length,
|
|
13345
|
+
returned_count: paginated.length,
|
|
13346
|
+
notes: paginated
|
|
13347
|
+
}, null, 2) }]
|
|
13348
|
+
};
|
|
13349
|
+
}
|
|
13350
|
+
case "cycles": {
|
|
13351
|
+
const cycles = detectCycles(index, 10, limit);
|
|
13352
|
+
return {
|
|
13353
|
+
content: [{ type: "text", text: JSON.stringify({
|
|
13354
|
+
analysis: "cycles",
|
|
13355
|
+
total_count: cycles.length,
|
|
13356
|
+
cycles
|
|
13357
|
+
}, null, 2) }]
|
|
13358
|
+
};
|
|
13359
|
+
}
|
|
13102
13360
|
}
|
|
13103
13361
|
}
|
|
13104
13362
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.122",
|
|
4
4
|
"description": "MCP server that gives Claude full read/write access to your Obsidian vault. Select from 69 tools for search, backlinks, graph queries, mutations, agent memory, and hybrid semantic search.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
"lint": "tsc --noEmit",
|
|
50
50
|
"clean": "rm -rf dist",
|
|
51
51
|
"postinstall": "node -e \"['bin/flywheel-memory.js','dist/index.js'].forEach(f=>{try{require('fs').chmodSync(f,0o755)}catch{}})\"",
|
|
52
|
-
"prepublishOnly": "npm run build"
|
|
52
|
+
"prepublishOnly": "npm run lint && npm run build"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
55
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
56
|
-
"@velvetmonkey/vault-core": "^2.0.
|
|
56
|
+
"@velvetmonkey/vault-core": "^2.0.122",
|
|
57
57
|
"better-sqlite3": "^11.0.0",
|
|
58
58
|
"chokidar": "^4.0.0",
|
|
59
59
|
"gray-matter": "^4.0.3",
|