@velvetmonkey/flywheel-mcp 1.27.14 → 1.27.16

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.
Files changed (2) hide show
  1. package/dist/index.js +175 -69
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -260,8 +260,8 @@ function updateIndexProgress(parsed, total) {
260
260
  function normalizeTarget(target) {
261
261
  return target.toLowerCase().replace(/\.md$/, "");
262
262
  }
263
- function normalizeNotePath(path12) {
264
- return path12.toLowerCase().replace(/\.md$/, "");
263
+ function normalizeNotePath(path13) {
264
+ return path13.toLowerCase().replace(/\.md$/, "");
265
265
  }
266
266
  async function buildVaultIndex(vaultPath2, options = {}) {
267
267
  const { timeoutMs = DEFAULT_TIMEOUT_MS, onProgress } = options;
@@ -455,7 +455,7 @@ function findSimilarEntity(index, target) {
455
455
  }
456
456
  const maxDist = normalizedLen <= 10 ? 1 : 2;
457
457
  let bestMatch;
458
- for (const [entity, path12] of index.entities) {
458
+ for (const [entity, path13] of index.entities) {
459
459
  const lenDiff = Math.abs(entity.length - normalizedLen);
460
460
  if (lenDiff > maxDist) {
461
461
  continue;
@@ -463,7 +463,7 @@ function findSimilarEntity(index, target) {
463
463
  const dist = levenshteinDistance(normalized, entity);
464
464
  if (dist > 0 && dist <= maxDist) {
465
465
  if (!bestMatch || dist < bestMatch.distance) {
466
- bestMatch = { path: path12, entity, distance: dist };
466
+ bestMatch = { path: path13, entity, distance: dist };
467
467
  if (dist === 1) {
468
468
  return bestMatch;
469
469
  }
@@ -889,14 +889,14 @@ function registerWikilinkTools(server2, getIndex, getVaultPath) {
889
889
  };
890
890
  function findSimilarEntity2(target, entities) {
891
891
  const targetLower = target.toLowerCase();
892
- for (const [name, path12] of entities) {
892
+ for (const [name, path13] of entities) {
893
893
  if (name.startsWith(targetLower) || targetLower.startsWith(name)) {
894
- return path12;
894
+ return path13;
895
895
  }
896
896
  }
897
- for (const [name, path12] of entities) {
897
+ for (const [name, path13] of entities) {
898
898
  if (name.includes(targetLower) || targetLower.includes(name)) {
899
- return path12;
899
+ return path13;
900
900
  }
901
901
  }
902
902
  return void 0;
@@ -1193,8 +1193,8 @@ function registerHealthTools(server2, getIndex, getVaultPath) {
1193
1193
  top_tags: z3.array(TagStatSchema).describe("Top 20 most used tags"),
1194
1194
  folders: z3.array(FolderStatSchema).describe("Note counts by top-level folder")
1195
1195
  };
1196
- function isPeriodicNote(path12) {
1197
- const filename = path12.split("/").pop() || "";
1196
+ function isPeriodicNote(path13) {
1197
+ const filename = path13.split("/").pop() || "";
1198
1198
  const nameWithoutExt = filename.replace(/\.md$/, "");
1199
1199
  const patterns = [
1200
1200
  /^\d{4}-\d{2}-\d{2}$/,
@@ -1209,7 +1209,7 @@ function registerHealthTools(server2, getIndex, getVaultPath) {
1209
1209
  // YYYY (yearly)
1210
1210
  ];
1211
1211
  const periodicFolders = ["daily", "weekly", "monthly", "quarterly", "yearly", "journal", "journals"];
1212
- const folder = path12.split("/")[0]?.toLowerCase() || "";
1212
+ const folder = path13.split("/")[0]?.toLowerCase() || "";
1213
1213
  return patterns.some((p) => p.test(nameWithoutExt)) || periodicFolders.includes(folder);
1214
1214
  }
1215
1215
  server2.registerTool(
@@ -2131,8 +2131,8 @@ function getStaleNotes(index, days, minBacklinks = 0) {
2131
2131
  return b.days_since_modified - a.days_since_modified;
2132
2132
  });
2133
2133
  }
2134
- function getContemporaneousNotes(index, path12, hours = 24) {
2135
- const targetNote = index.notes.get(path12);
2134
+ function getContemporaneousNotes(index, path13, hours = 24) {
2135
+ const targetNote = index.notes.get(path13);
2136
2136
  if (!targetNote) {
2137
2137
  return [];
2138
2138
  }
@@ -2140,7 +2140,7 @@ function getContemporaneousNotes(index, path12, hours = 24) {
2140
2140
  const windowMs = hours * 60 * 60 * 1e3;
2141
2141
  const results = [];
2142
2142
  for (const note of index.notes.values()) {
2143
- if (note.path === path12) continue;
2143
+ if (note.path === path13) continue;
2144
2144
  const timeDiff = Math.abs(note.modified.getTime() - targetTime);
2145
2145
  if (timeDiff <= windowMs) {
2146
2146
  results.push({
@@ -2932,14 +2932,14 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig = ()
2932
2932
  offset: z6.number().default(0).describe("Number of results to skip (for pagination)")
2933
2933
  }
2934
2934
  },
2935
- async ({ path: path12, hours, limit: requestedLimit, offset }) => {
2935
+ async ({ path: path13, hours, limit: requestedLimit, offset }) => {
2936
2936
  const limit = Math.min(requestedLimit ?? 50, MAX_LIMIT);
2937
2937
  const index = getIndex();
2938
- const allResults = getContemporaneousNotes(index, path12, hours);
2938
+ const allResults = getContemporaneousNotes(index, path13, hours);
2939
2939
  const result = allResults.slice(offset, offset + limit);
2940
2940
  return {
2941
2941
  content: [{ type: "text", text: JSON.stringify({
2942
- reference_note: path12,
2942
+ reference_note: path13,
2943
2943
  window_hours: hours,
2944
2944
  total_count: allResults.length,
2945
2945
  returned_count: result.length,
@@ -2977,13 +2977,13 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig = ()
2977
2977
  path: z6.string().describe("Path to the note")
2978
2978
  }
2979
2979
  },
2980
- async ({ path: path12 }) => {
2980
+ async ({ path: path13 }) => {
2981
2981
  const index = getIndex();
2982
2982
  const vaultPath2 = getVaultPath();
2983
- const result = await getNoteStructure(index, path12, vaultPath2);
2983
+ const result = await getNoteStructure(index, path13, vaultPath2);
2984
2984
  if (!result) {
2985
2985
  return {
2986
- content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path12 }, null, 2) }]
2986
+ content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path13 }, null, 2) }]
2987
2987
  };
2988
2988
  }
2989
2989
  return {
@@ -3000,18 +3000,18 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig = ()
3000
3000
  path: z6.string().describe("Path to the note")
3001
3001
  }
3002
3002
  },
3003
- async ({ path: path12 }) => {
3003
+ async ({ path: path13 }) => {
3004
3004
  const index = getIndex();
3005
3005
  const vaultPath2 = getVaultPath();
3006
- const result = await getHeadings(index, path12, vaultPath2);
3006
+ const result = await getHeadings(index, path13, vaultPath2);
3007
3007
  if (!result) {
3008
3008
  return {
3009
- content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path12 }, null, 2) }]
3009
+ content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path13 }, null, 2) }]
3010
3010
  };
3011
3011
  }
3012
3012
  return {
3013
3013
  content: [{ type: "text", text: JSON.stringify({
3014
- path: path12,
3014
+ path: path13,
3015
3015
  heading_count: result.length,
3016
3016
  headings: result
3017
3017
  }, null, 2) }]
@@ -3029,15 +3029,15 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig = ()
3029
3029
  include_subheadings: z6.boolean().default(true).describe("Include content under subheadings")
3030
3030
  }
3031
3031
  },
3032
- async ({ path: path12, heading, include_subheadings }) => {
3032
+ async ({ path: path13, heading, include_subheadings }) => {
3033
3033
  const index = getIndex();
3034
3034
  const vaultPath2 = getVaultPath();
3035
- const result = await getSectionContent(index, path12, heading, vaultPath2, include_subheadings);
3035
+ const result = await getSectionContent(index, path13, heading, vaultPath2, include_subheadings);
3036
3036
  if (!result) {
3037
3037
  return {
3038
3038
  content: [{ type: "text", text: JSON.stringify({
3039
3039
  error: "Section not found",
3040
- path: path12,
3040
+ path: path13,
3041
3041
  heading
3042
3042
  }, null, 2) }]
3043
3043
  };
@@ -3114,19 +3114,19 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig = ()
3114
3114
  path: z6.string().describe("Path to the note")
3115
3115
  }
3116
3116
  },
3117
- async ({ path: path12 }) => {
3117
+ async ({ path: path13 }) => {
3118
3118
  const index = getIndex();
3119
3119
  const vaultPath2 = getVaultPath();
3120
3120
  const config = getConfig();
3121
- const result = await getTasksFromNote(index, path12, vaultPath2, config.exclude_task_tags || []);
3121
+ const result = await getTasksFromNote(index, path13, vaultPath2, config.exclude_task_tags || []);
3122
3122
  if (!result) {
3123
3123
  return {
3124
- content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path12 }, null, 2) }]
3124
+ content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path13 }, null, 2) }]
3125
3125
  };
3126
3126
  }
3127
3127
  return {
3128
3128
  content: [{ type: "text", text: JSON.stringify({
3129
- path: path12,
3129
+ path: path13,
3130
3130
  task_count: result.length,
3131
3131
  open: result.filter((t) => t.status === "open").length,
3132
3132
  completed: result.filter((t) => t.status === "completed").length,
@@ -3258,14 +3258,14 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig = ()
3258
3258
  offset: z6.number().default(0).describe("Number of results to skip (for pagination)")
3259
3259
  }
3260
3260
  },
3261
- async ({ path: path12, limit: requestedLimit, offset }) => {
3261
+ async ({ path: path13, limit: requestedLimit, offset }) => {
3262
3262
  const limit = Math.min(requestedLimit ?? 50, MAX_LIMIT);
3263
3263
  const index = getIndex();
3264
- const allResults = findBidirectionalLinks(index, path12);
3264
+ const allResults = findBidirectionalLinks(index, path13);
3265
3265
  const result = allResults.slice(offset, offset + limit);
3266
3266
  return {
3267
3267
  content: [{ type: "text", text: JSON.stringify({
3268
- scope: path12 || "all",
3268
+ scope: path13 || "all",
3269
3269
  total_count: allResults.length,
3270
3270
  returned_count: result.length,
3271
3271
  pairs: result
@@ -5006,30 +5006,30 @@ var EventQueue = class {
5006
5006
  * Add a new event to the queue
5007
5007
  */
5008
5008
  push(type, rawPath) {
5009
- const path12 = normalizePath(rawPath);
5009
+ const path13 = normalizePath(rawPath);
5010
5010
  const now = Date.now();
5011
5011
  const event = {
5012
5012
  type,
5013
- path: path12,
5013
+ path: path13,
5014
5014
  timestamp: now
5015
5015
  };
5016
- let pending = this.pending.get(path12);
5016
+ let pending = this.pending.get(path13);
5017
5017
  if (!pending) {
5018
5018
  pending = {
5019
5019
  events: [],
5020
5020
  timer: null,
5021
5021
  lastEvent: now
5022
5022
  };
5023
- this.pending.set(path12, pending);
5023
+ this.pending.set(path13, pending);
5024
5024
  }
5025
5025
  pending.events.push(event);
5026
5026
  pending.lastEvent = now;
5027
- console.error(`[flywheel] QUEUE: pushed ${type} for ${path12}, pending=${this.pending.size}`);
5027
+ console.error(`[flywheel] QUEUE: pushed ${type} for ${path13}, pending=${this.pending.size}`);
5028
5028
  if (pending.timer) {
5029
5029
  clearTimeout(pending.timer);
5030
5030
  }
5031
5031
  pending.timer = setTimeout(() => {
5032
- this.flushPath(path12);
5032
+ this.flushPath(path13);
5033
5033
  }, this.config.debounceMs);
5034
5034
  if (this.pending.size >= this.config.batchSize) {
5035
5035
  this.flush();
@@ -5050,10 +5050,10 @@ var EventQueue = class {
5050
5050
  /**
5051
5051
  * Flush a single path's events
5052
5052
  */
5053
- flushPath(path12) {
5054
- const pending = this.pending.get(path12);
5053
+ flushPath(path13) {
5054
+ const pending = this.pending.get(path13);
5055
5055
  if (!pending || pending.events.length === 0) return;
5056
- console.error(`[flywheel] QUEUE: flushing ${path12}, events=${pending.events.length}`);
5056
+ console.error(`[flywheel] QUEUE: flushing ${path13}, events=${pending.events.length}`);
5057
5057
  if (pending.timer) {
5058
5058
  clearTimeout(pending.timer);
5059
5059
  pending.timer = null;
@@ -5062,7 +5062,7 @@ var EventQueue = class {
5062
5062
  if (coalescedType) {
5063
5063
  const coalesced = {
5064
5064
  type: coalescedType,
5065
- path: path12,
5065
+ path: path13,
5066
5066
  originalEvents: [...pending.events]
5067
5067
  };
5068
5068
  this.onBatch({
@@ -5070,7 +5070,7 @@ var EventQueue = class {
5070
5070
  timestamp: Date.now()
5071
5071
  });
5072
5072
  }
5073
- this.pending.delete(path12);
5073
+ this.pending.delete(path13);
5074
5074
  }
5075
5075
  /**
5076
5076
  * Flush all pending events
@@ -5082,7 +5082,7 @@ var EventQueue = class {
5082
5082
  }
5083
5083
  if (this.pending.size === 0) return;
5084
5084
  const events = [];
5085
- for (const [path12, pending] of this.pending) {
5085
+ for (const [path13, pending] of this.pending) {
5086
5086
  if (pending.timer) {
5087
5087
  clearTimeout(pending.timer);
5088
5088
  }
@@ -5090,7 +5090,7 @@ var EventQueue = class {
5090
5090
  if (coalescedType) {
5091
5091
  events.push({
5092
5092
  type: coalescedType,
5093
- path: path12,
5093
+ path: path13,
5094
5094
  originalEvents: [...pending.events]
5095
5095
  });
5096
5096
  }
@@ -5239,31 +5239,31 @@ function createVaultWatcher(options) {
5239
5239
  usePolling: config.usePolling,
5240
5240
  interval: config.usePolling ? config.pollInterval : void 0
5241
5241
  });
5242
- watcher.on("add", (path12) => {
5243
- console.error(`[flywheel] RAW EVENT: add ${path12}`);
5244
- if (shouldWatch(path12, vaultPath2)) {
5245
- console.error(`[flywheel] ACCEPTED: add ${path12}`);
5246
- eventQueue.push("add", path12);
5242
+ watcher.on("add", (path13) => {
5243
+ console.error(`[flywheel] RAW EVENT: add ${path13}`);
5244
+ if (shouldWatch(path13, vaultPath2)) {
5245
+ console.error(`[flywheel] ACCEPTED: add ${path13}`);
5246
+ eventQueue.push("add", path13);
5247
5247
  } else {
5248
- console.error(`[flywheel] FILTERED: add ${path12}`);
5248
+ console.error(`[flywheel] FILTERED: add ${path13}`);
5249
5249
  }
5250
5250
  });
5251
- watcher.on("change", (path12) => {
5252
- console.error(`[flywheel] RAW EVENT: change ${path12}`);
5253
- if (shouldWatch(path12, vaultPath2)) {
5254
- console.error(`[flywheel] ACCEPTED: change ${path12}`);
5255
- eventQueue.push("change", path12);
5251
+ watcher.on("change", (path13) => {
5252
+ console.error(`[flywheel] RAW EVENT: change ${path13}`);
5253
+ if (shouldWatch(path13, vaultPath2)) {
5254
+ console.error(`[flywheel] ACCEPTED: change ${path13}`);
5255
+ eventQueue.push("change", path13);
5256
5256
  } else {
5257
- console.error(`[flywheel] FILTERED: change ${path12}`);
5257
+ console.error(`[flywheel] FILTERED: change ${path13}`);
5258
5258
  }
5259
5259
  });
5260
- watcher.on("unlink", (path12) => {
5261
- console.error(`[flywheel] RAW EVENT: unlink ${path12}`);
5262
- if (shouldWatch(path12, vaultPath2)) {
5263
- console.error(`[flywheel] ACCEPTED: unlink ${path12}`);
5264
- eventQueue.push("unlink", path12);
5260
+ watcher.on("unlink", (path13) => {
5261
+ console.error(`[flywheel] RAW EVENT: unlink ${path13}`);
5262
+ if (shouldWatch(path13, vaultPath2)) {
5263
+ console.error(`[flywheel] ACCEPTED: unlink ${path13}`);
5264
+ eventQueue.push("unlink", path13);
5265
5265
  } else {
5266
- console.error(`[flywheel] FILTERED: unlink ${path12}`);
5266
+ console.error(`[flywheel] FILTERED: unlink ${path13}`);
5267
5267
  }
5268
5268
  });
5269
5269
  watcher.on("ready", () => {
@@ -5294,6 +5294,109 @@ function createVaultWatcher(options) {
5294
5294
  return instance;
5295
5295
  }
5296
5296
 
5297
+ // src/core/hubExport.ts
5298
+ import fs13 from "fs/promises";
5299
+ import path12 from "path";
5300
+ function computeHubScores(index) {
5301
+ const hubScores = /* @__PURE__ */ new Map();
5302
+ for (const note of index.notes.values()) {
5303
+ const backlinks = getBacklinksForNote(index, note.path);
5304
+ const backlinkCount = backlinks.length;
5305
+ const normalizedPath = normalizeTarget(note.path);
5306
+ hubScores.set(normalizedPath, backlinkCount);
5307
+ const title = note.title.toLowerCase();
5308
+ if (!hubScores.has(title) || backlinkCount > hubScores.get(title)) {
5309
+ hubScores.set(title, backlinkCount);
5310
+ }
5311
+ }
5312
+ return hubScores;
5313
+ }
5314
+ function enrichEntity(entity, hubScores) {
5315
+ const entityObj = typeof entity === "string" ? { name: entity, path: "", aliases: [] } : { ...entity };
5316
+ let hubScore = 0;
5317
+ if (entityObj.path) {
5318
+ const normalizedPath = normalizeTarget(entityObj.path);
5319
+ hubScore = hubScores.get(normalizedPath) ?? 0;
5320
+ }
5321
+ if (hubScore === 0) {
5322
+ const normalizedName = entityObj.name.toLowerCase();
5323
+ hubScore = hubScores.get(normalizedName) ?? 0;
5324
+ }
5325
+ entityObj.hubScore = hubScore;
5326
+ return entityObj;
5327
+ }
5328
+ function enrichEntityIndex(index, hubScores) {
5329
+ const categories = [
5330
+ "technologies",
5331
+ "acronyms",
5332
+ "people",
5333
+ "projects",
5334
+ "organizations",
5335
+ "locations",
5336
+ "concepts",
5337
+ "other"
5338
+ ];
5339
+ const enriched = {
5340
+ ...index,
5341
+ _metadata: {
5342
+ ...index._metadata,
5343
+ generated_at: (/* @__PURE__ */ new Date()).toISOString()
5344
+ }
5345
+ };
5346
+ for (const category of categories) {
5347
+ if (enriched[category]) {
5348
+ enriched[category] = enriched[category].map((e) => enrichEntity(e, hubScores));
5349
+ }
5350
+ }
5351
+ return enriched;
5352
+ }
5353
+ async function exportHubScores(vaultPath2, vaultIndex2) {
5354
+ const cachePath = path12.join(vaultPath2, ".claude", "wikilink-entities.json");
5355
+ try {
5356
+ await fs13.access(cachePath);
5357
+ } catch {
5358
+ console.error("[Flywheel] Entity cache not found, skipping hub score export");
5359
+ return -1;
5360
+ }
5361
+ let entityIndex;
5362
+ try {
5363
+ const content = await fs13.readFile(cachePath, "utf-8");
5364
+ entityIndex = JSON.parse(content);
5365
+ } catch (e) {
5366
+ console.error("[Flywheel] Failed to load entity cache:", e);
5367
+ return -1;
5368
+ }
5369
+ const hubScores = computeHubScores(vaultIndex2);
5370
+ console.error(`[Flywheel] Computed hub scores for ${hubScores.size} notes`);
5371
+ const enriched = enrichEntityIndex(entityIndex, hubScores);
5372
+ let hubCount = 0;
5373
+ const categories = [
5374
+ "technologies",
5375
+ "acronyms",
5376
+ "people",
5377
+ "projects",
5378
+ "organizations",
5379
+ "locations",
5380
+ "concepts",
5381
+ "other"
5382
+ ];
5383
+ for (const category of categories) {
5384
+ for (const entity of enriched[category] ?? []) {
5385
+ if (typeof entity !== "string" && entity.hubScore && entity.hubScore > 0) {
5386
+ hubCount++;
5387
+ }
5388
+ }
5389
+ }
5390
+ try {
5391
+ await fs13.writeFile(cachePath, JSON.stringify(enriched, null, 2), "utf-8");
5392
+ console.error(`[Flywheel] Exported hub scores: ${hubCount} entities with backlinks`);
5393
+ return hubCount;
5394
+ } catch (e) {
5395
+ console.error("[Flywheel] Failed to save enriched entity cache:", e);
5396
+ return -1;
5397
+ }
5398
+ }
5399
+
5297
5400
  // src/index.ts
5298
5401
  var vaultPath = process.env.PROJECT_PATH || findVaultRoot();
5299
5402
  var flywheelConfig = {};
@@ -5396,11 +5499,12 @@ async function main() {
5396
5499
  console.error("Flywheel MCP server running on stdio");
5397
5500
  console.error("Building vault index in background...");
5398
5501
  const startTime = Date.now();
5399
- buildVaultIndex(vaultPath).then((index) => {
5502
+ buildVaultIndex(vaultPath).then(async (index) => {
5400
5503
  vaultIndex = index;
5401
5504
  setIndexState("ready");
5402
5505
  const duration = Date.now() - startTime;
5403
5506
  console.error(`Vault index ready in ${duration}ms`);
5507
+ await exportHubScores(vaultPath, index);
5404
5508
  const existing = loadConfig(vaultPath);
5405
5509
  const inferred = inferConfig(vaultIndex, vaultPath);
5406
5510
  saveConfig(vaultPath, inferred, existing);
@@ -5432,6 +5536,7 @@ async function main() {
5432
5536
  vaultIndex = await buildVaultIndex(vaultPath);
5433
5537
  setIndexState("ready");
5434
5538
  console.error(`[flywheel] Index rebuilt in ${Date.now() - startTime2}ms`);
5539
+ await exportHubScores(vaultPath, vaultIndex);
5435
5540
  } catch (err) {
5436
5541
  setIndexState("error");
5437
5542
  setIndexError(err instanceof Error ? err : new Error(String(err)));
@@ -5462,15 +5567,16 @@ async function main() {
5462
5567
  }
5463
5568
  });
5464
5569
  let rebuildTimer;
5465
- legacyWatcher.on("all", (event, path12) => {
5466
- if (!path12.endsWith(".md")) return;
5570
+ legacyWatcher.on("all", (event, path13) => {
5571
+ if (!path13.endsWith(".md")) return;
5467
5572
  clearTimeout(rebuildTimer);
5468
5573
  rebuildTimer = setTimeout(() => {
5469
5574
  console.error("[flywheel] Rebuilding index (file changed)");
5470
- buildVaultIndex(vaultPath).then((index2) => {
5575
+ buildVaultIndex(vaultPath).then(async (index2) => {
5471
5576
  vaultIndex = index2;
5472
5577
  setIndexState("ready");
5473
5578
  console.error("[flywheel] Index rebuilt successfully");
5579
+ await exportHubScores(vaultPath, index2);
5474
5580
  }).catch((err) => {
5475
5581
  setIndexState("error");
5476
5582
  setIndexError(err instanceof Error ? err : new Error(String(err)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velvetmonkey/flywheel-mcp",
3
- "version": "1.27.14",
3
+ "version": "1.27.16",
4
4
  "description": "Graph intelligence for markdown vaults. MCP server for Obsidian.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",