@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.
- package/dist/index.js +175 -69
- 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(
|
|
264
|
-
return
|
|
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,
|
|
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:
|
|
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,
|
|
892
|
+
for (const [name, path13] of entities) {
|
|
893
893
|
if (name.startsWith(targetLower) || targetLower.startsWith(name)) {
|
|
894
|
-
return
|
|
894
|
+
return path13;
|
|
895
895
|
}
|
|
896
896
|
}
|
|
897
|
-
for (const [name,
|
|
897
|
+
for (const [name, path13] of entities) {
|
|
898
898
|
if (name.includes(targetLower) || targetLower.includes(name)) {
|
|
899
|
-
return
|
|
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(
|
|
1197
|
-
const filename =
|
|
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 =
|
|
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,
|
|
2135
|
-
const targetNote = index.notes.get(
|
|
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 ===
|
|
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:
|
|
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,
|
|
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:
|
|
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:
|
|
2980
|
+
async ({ path: path13 }) => {
|
|
2981
2981
|
const index = getIndex();
|
|
2982
2982
|
const vaultPath2 = getVaultPath();
|
|
2983
|
-
const result = await getNoteStructure(index,
|
|
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:
|
|
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:
|
|
3003
|
+
async ({ path: path13 }) => {
|
|
3004
3004
|
const index = getIndex();
|
|
3005
3005
|
const vaultPath2 = getVaultPath();
|
|
3006
|
-
const result = await getHeadings(index,
|
|
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:
|
|
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:
|
|
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:
|
|
3032
|
+
async ({ path: path13, heading, include_subheadings }) => {
|
|
3033
3033
|
const index = getIndex();
|
|
3034
3034
|
const vaultPath2 = getVaultPath();
|
|
3035
|
-
const result = await getSectionContent(index,
|
|
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:
|
|
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:
|
|
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,
|
|
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:
|
|
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:
|
|
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:
|
|
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,
|
|
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:
|
|
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
|
|
5009
|
+
const path13 = normalizePath(rawPath);
|
|
5010
5010
|
const now = Date.now();
|
|
5011
5011
|
const event = {
|
|
5012
5012
|
type,
|
|
5013
|
-
path:
|
|
5013
|
+
path: path13,
|
|
5014
5014
|
timestamp: now
|
|
5015
5015
|
};
|
|
5016
|
-
let pending = this.pending.get(
|
|
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(
|
|
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 ${
|
|
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(
|
|
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(
|
|
5054
|
-
const pending = this.pending.get(
|
|
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 ${
|
|
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:
|
|
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(
|
|
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 [
|
|
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:
|
|
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", (
|
|
5243
|
-
console.error(`[flywheel] RAW EVENT: add ${
|
|
5244
|
-
if (shouldWatch(
|
|
5245
|
-
console.error(`[flywheel] ACCEPTED: add ${
|
|
5246
|
-
eventQueue.push("add",
|
|
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 ${
|
|
5248
|
+
console.error(`[flywheel] FILTERED: add ${path13}`);
|
|
5249
5249
|
}
|
|
5250
5250
|
});
|
|
5251
|
-
watcher.on("change", (
|
|
5252
|
-
console.error(`[flywheel] RAW EVENT: change ${
|
|
5253
|
-
if (shouldWatch(
|
|
5254
|
-
console.error(`[flywheel] ACCEPTED: change ${
|
|
5255
|
-
eventQueue.push("change",
|
|
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 ${
|
|
5257
|
+
console.error(`[flywheel] FILTERED: change ${path13}`);
|
|
5258
5258
|
}
|
|
5259
5259
|
});
|
|
5260
|
-
watcher.on("unlink", (
|
|
5261
|
-
console.error(`[flywheel] RAW EVENT: unlink ${
|
|
5262
|
-
if (shouldWatch(
|
|
5263
|
-
console.error(`[flywheel] ACCEPTED: unlink ${
|
|
5264
|
-
eventQueue.push("unlink",
|
|
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 ${
|
|
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,
|
|
5466
|
-
if (!
|
|
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)));
|