@velvetmonkey/flywheel-memory 2.5.9 → 2.5.11
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/embedding-worker.js +76 -0
- package/dist/index.js +1488 -1138
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
5
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
6
|
-
}) : x)(function(x) {
|
|
7
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
8
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
9
|
-
});
|
|
10
4
|
var __esm = (fn, res) => function __init() {
|
|
11
5
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
12
6
|
};
|
|
@@ -283,6 +277,8 @@ var init_vault_scope = __esm({
|
|
|
283
277
|
import * as crypto from "crypto";
|
|
284
278
|
import * as fs3 from "fs";
|
|
285
279
|
import * as path2 from "path";
|
|
280
|
+
import { Worker } from "node:worker_threads";
|
|
281
|
+
import { fileURLToPath } from "node:url";
|
|
286
282
|
function getModelConfig() {
|
|
287
283
|
const envModel = process.env.EMBEDDING_MODEL?.trim();
|
|
288
284
|
if (!envModel) return MODEL_REGISTRY[DEFAULT_MODEL];
|
|
@@ -334,92 +330,115 @@ function clearEmbeddingsForRebuild() {
|
|
|
334
330
|
function setEmbeddingsDatabase(database) {
|
|
335
331
|
db = database;
|
|
336
332
|
}
|
|
337
|
-
function
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
const npxDir = path2.join(home, ".npm", "_npx");
|
|
348
|
-
if (fs3.existsSync(npxDir)) {
|
|
349
|
-
for (const hash of fs3.readdirSync(npxDir)) {
|
|
350
|
-
const candidate = path2.join(npxDir, hash, "node_modules", "@huggingface", "transformers", ".cache", ...modelId.split("/"));
|
|
351
|
-
if (fs3.existsSync(candidate)) candidates.push(candidate);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
for (const cacheDir of candidates) {
|
|
356
|
-
if (fs3.existsSync(cacheDir)) {
|
|
357
|
-
fs3.rmSync(cacheDir, { recursive: true, force: true });
|
|
358
|
-
console.error(`[Semantic] Deleted corrupted model cache: ${cacheDir}`);
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
} catch (e) {
|
|
362
|
-
console.error(`[Semantic] Could not clear model cache: ${e instanceof Error ? e.message : e}`);
|
|
363
|
-
}
|
|
333
|
+
function resolveWorkerPath() {
|
|
334
|
+
const thisFile = typeof __filename !== "undefined" ? __filename : fileURLToPath(import.meta.url);
|
|
335
|
+
const thisDir = path2.dirname(thisFile);
|
|
336
|
+
const workerPath = path2.join(thisDir, "embedding-worker.js");
|
|
337
|
+
if (fs3.existsSync(workerPath)) return workerPath;
|
|
338
|
+
const devPath = path2.resolve(thisDir, "..", "..", "..", "dist", "embedding-worker.js");
|
|
339
|
+
if (fs3.existsSync(devPath)) return devPath;
|
|
340
|
+
throw new Error(
|
|
341
|
+
`Embedding worker not found at ${workerPath}. Run 'npm run build' to generate it.`
|
|
342
|
+
);
|
|
364
343
|
}
|
|
365
344
|
async function initEmbeddings() {
|
|
366
|
-
if (
|
|
367
|
-
if (
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
345
|
+
if (workerReady && worker) return;
|
|
346
|
+
if (workerInitPromise) return workerInitPromise;
|
|
347
|
+
workerInitPromise = new Promise((resolve3, reject) => {
|
|
348
|
+
try {
|
|
349
|
+
const workerPath = resolveWorkerPath();
|
|
350
|
+
console.error(`[Semantic] Spawning embedding worker: ${workerPath}`);
|
|
351
|
+
worker = new Worker(workerPath);
|
|
352
|
+
worker.on("message", (msg) => {
|
|
353
|
+
switch (msg.type) {
|
|
354
|
+
case "ready":
|
|
355
|
+
workerReady = true;
|
|
356
|
+
workerDims = msg.dims;
|
|
357
|
+
if (activeModelConfig.dims === 0) {
|
|
358
|
+
activeModelConfig.dims = msg.dims;
|
|
359
|
+
console.error(`[Semantic] Probed model ${activeModelConfig.id}: ${msg.dims} dims`);
|
|
360
|
+
}
|
|
361
|
+
console.error(`[Semantic] Worker ready (model: ${activeModelConfig.id}, dims: ${msg.dims})`);
|
|
362
|
+
resolve3();
|
|
363
|
+
break;
|
|
364
|
+
case "result": {
|
|
365
|
+
const pending = pendingEmbeds.get(msg.id);
|
|
366
|
+
if (pending) {
|
|
367
|
+
pendingEmbeds.delete(msg.id);
|
|
368
|
+
pending.resolve(new Float32Array(msg.embedding));
|
|
369
|
+
}
|
|
370
|
+
break;
|
|
371
|
+
}
|
|
372
|
+
case "error": {
|
|
373
|
+
if (msg.fatal) {
|
|
374
|
+
console.error(`[Semantic] Worker fatal error: ${msg.message}`);
|
|
375
|
+
console.error(`[Semantic] Semantic search disabled. Keyword search (BM25) remains available.`);
|
|
376
|
+
terminateWorker();
|
|
377
|
+
workerInitPromise = null;
|
|
378
|
+
reject(new Error(msg.message));
|
|
379
|
+
} else if (msg.id != null) {
|
|
380
|
+
const pending = pendingEmbeds.get(msg.id);
|
|
381
|
+
if (pending) {
|
|
382
|
+
pendingEmbeds.delete(msg.id);
|
|
383
|
+
pending.reject(new Error(msg.message));
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
392
388
|
}
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
389
|
+
});
|
|
390
|
+
worker.on("error", (err) => {
|
|
391
|
+
console.error(`[Semantic] Worker error: ${err.message}`);
|
|
392
|
+
handleWorkerCrash();
|
|
393
|
+
if (!workerReady) {
|
|
394
|
+
workerInitPromise = null;
|
|
395
|
+
reject(err);
|
|
400
396
|
}
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
console.error(`[Semantic]
|
|
405
|
-
|
|
406
|
-
pipeline = null;
|
|
407
|
-
} else {
|
|
408
|
-
console.error(`[Semantic] Model load failed after ${MAX_RETRIES} attempts: ${err instanceof Error ? err.message : err}`);
|
|
409
|
-
console.error(`[Semantic] Semantic search disabled. Keyword search (BM25) remains available.`);
|
|
410
|
-
initPromise = null;
|
|
411
|
-
throw err;
|
|
397
|
+
});
|
|
398
|
+
worker.on("exit", (code) => {
|
|
399
|
+
if (code !== 0 && workerReady) {
|
|
400
|
+
console.error(`[Semantic] Worker exited with code ${code}`);
|
|
401
|
+
handleWorkerCrash();
|
|
412
402
|
}
|
|
413
|
-
}
|
|
403
|
+
});
|
|
404
|
+
worker.postMessage({ type: "init", modelId: activeModelConfig.id });
|
|
405
|
+
} catch (err) {
|
|
406
|
+
workerInitPromise = null;
|
|
407
|
+
reject(err);
|
|
414
408
|
}
|
|
415
|
-
})
|
|
416
|
-
return
|
|
409
|
+
});
|
|
410
|
+
return workerInitPromise;
|
|
411
|
+
}
|
|
412
|
+
function handleWorkerCrash() {
|
|
413
|
+
for (const [id, pending] of pendingEmbeds) {
|
|
414
|
+
pending.reject(new Error("Embedding worker crashed"));
|
|
415
|
+
pendingEmbeds.delete(id);
|
|
416
|
+
}
|
|
417
|
+
worker = null;
|
|
418
|
+
workerReady = false;
|
|
419
|
+
workerInitPromise = null;
|
|
420
|
+
}
|
|
421
|
+
function terminateWorker() {
|
|
422
|
+
if (worker) {
|
|
423
|
+
try {
|
|
424
|
+
worker.postMessage({ type: "shutdown" });
|
|
425
|
+
} catch {
|
|
426
|
+
}
|
|
427
|
+
worker = null;
|
|
428
|
+
}
|
|
429
|
+
workerReady = false;
|
|
430
|
+
workerInitPromise = null;
|
|
417
431
|
}
|
|
418
432
|
async function embedText(text) {
|
|
419
433
|
await initEmbeddings();
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
434
|
+
if (!worker) {
|
|
435
|
+
throw new Error("Embedding worker not available");
|
|
436
|
+
}
|
|
437
|
+
const id = ++embedRequestId;
|
|
438
|
+
return new Promise((resolve3, reject) => {
|
|
439
|
+
pendingEmbeds.set(id, { resolve: resolve3, reject });
|
|
440
|
+
worker.postMessage({ type: "embed", id, text });
|
|
441
|
+
});
|
|
423
442
|
}
|
|
424
443
|
async function embedTextCached(text) {
|
|
425
444
|
const existing = embeddingCache.get(text);
|
|
@@ -1116,7 +1135,7 @@ function getEntityEmbeddingsCount() {
|
|
|
1116
1135
|
return 0;
|
|
1117
1136
|
}
|
|
1118
1137
|
}
|
|
1119
|
-
var MODEL_REGISTRY, DEFAULT_MODEL, activeModelConfig, MAX_FILE_SIZE2, db,
|
|
1138
|
+
var MODEL_REGISTRY, DEFAULT_MODEL, activeModelConfig, MAX_FILE_SIZE2, db, embeddingsBuilding, worker, workerReady, workerDims, workerInitPromise, embedRequestId, pendingEmbeds, embeddingCache, EMBEDDING_CACHE_MAX, entityEmbeddingsMap, inferredCategoriesMap, EMBEDDING_TEXT_VERSION;
|
|
1120
1139
|
var init_embeddings = __esm({
|
|
1121
1140
|
"src/core/read/embeddings.ts"() {
|
|
1122
1141
|
"use strict";
|
|
@@ -1133,9 +1152,13 @@ var init_embeddings = __esm({
|
|
|
1133
1152
|
activeModelConfig = getModelConfig();
|
|
1134
1153
|
MAX_FILE_SIZE2 = 5 * 1024 * 1024;
|
|
1135
1154
|
db = null;
|
|
1136
|
-
pipeline = null;
|
|
1137
|
-
initPromise = null;
|
|
1138
1155
|
embeddingsBuilding = false;
|
|
1156
|
+
worker = null;
|
|
1157
|
+
workerReady = false;
|
|
1158
|
+
workerDims = 0;
|
|
1159
|
+
workerInitPromise = null;
|
|
1160
|
+
embedRequestId = 0;
|
|
1161
|
+
pendingEmbeds = /* @__PURE__ */ new Map();
|
|
1139
1162
|
embeddingCache = /* @__PURE__ */ new Map();
|
|
1140
1163
|
EMBEDDING_CACHE_MAX = 500;
|
|
1141
1164
|
entityEmbeddingsMap = /* @__PURE__ */ new Map();
|
|
@@ -1255,9 +1278,12 @@ function saveRecencyToStateDb(index, explicitStateDb) {
|
|
|
1255
1278
|
}
|
|
1256
1279
|
console.error(`[Flywheel] saveRecencyToStateDb: Saving ${index.lastMentioned.size} entries...`);
|
|
1257
1280
|
try {
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1281
|
+
const runInTransaction = stateDb2.db.transaction(() => {
|
|
1282
|
+
for (const [entityNameLower, timestamp] of index.lastMentioned) {
|
|
1283
|
+
recordEntityMention(stateDb2, entityNameLower, new Date(timestamp));
|
|
1284
|
+
}
|
|
1285
|
+
});
|
|
1286
|
+
runInTransaction();
|
|
1261
1287
|
const count = stateDb2.db.prepare("SELECT COUNT(*) as cnt FROM recency").get();
|
|
1262
1288
|
console.error(`[Flywheel] Saved recency: ${index.lastMentioned.size} entries \u2192 ${count.cnt} rows in table`);
|
|
1263
1289
|
} catch (e) {
|
|
@@ -1930,18 +1956,18 @@ function updateSuppressionList(stateDb2, now) {
|
|
|
1930
1956
|
`DELETE FROM wikilink_suppressions
|
|
1931
1957
|
WHERE datetime(updated_at, '+' || ? || ' days') <= datetime('now')`
|
|
1932
1958
|
).run(SUPPRESSION_TTL_DAYS);
|
|
1933
|
-
for (const
|
|
1934
|
-
const effectiveAlpha = getEffectiveAlpha(
|
|
1935
|
-
const posteriorMean = computePosteriorMean(
|
|
1936
|
-
const totalObs = effectiveAlpha +
|
|
1959
|
+
for (const stat4 of weightedStats) {
|
|
1960
|
+
const effectiveAlpha = getEffectiveAlpha(stat4.entity);
|
|
1961
|
+
const posteriorMean = computePosteriorMean(stat4.weightedCorrect, stat4.weightedFp, effectiveAlpha);
|
|
1962
|
+
const totalObs = effectiveAlpha + stat4.weightedCorrect + PRIOR_BETA + stat4.weightedFp;
|
|
1937
1963
|
if (totalObs < SUPPRESSION_MIN_OBSERVATIONS) {
|
|
1938
1964
|
continue;
|
|
1939
1965
|
}
|
|
1940
1966
|
if (posteriorMean < SUPPRESSION_POSTERIOR_THRESHOLD) {
|
|
1941
|
-
upsert.run(
|
|
1967
|
+
upsert.run(stat4.entity, 1 - posteriorMean);
|
|
1942
1968
|
updated++;
|
|
1943
1969
|
} else {
|
|
1944
|
-
remove.run(
|
|
1970
|
+
remove.run(stat4.entity);
|
|
1945
1971
|
}
|
|
1946
1972
|
}
|
|
1947
1973
|
});
|
|
@@ -2011,31 +2037,31 @@ function getAllFeedbackBoosts(stateDb2, folder, now) {
|
|
|
2011
2037
|
if (folder !== void 0) {
|
|
2012
2038
|
folderStatsMap = /* @__PURE__ */ new Map();
|
|
2013
2039
|
for (const gs of globalStats) {
|
|
2014
|
-
const
|
|
2015
|
-
if (
|
|
2040
|
+
const fs36 = getWeightedFolderStats(stateDb2, gs.entity, folder, now);
|
|
2041
|
+
if (fs36.rawTotal >= FEEDBACK_BOOST_MIN_SAMPLES) {
|
|
2016
2042
|
folderStatsMap.set(gs.entity, {
|
|
2017
|
-
weightedAccuracy:
|
|
2018
|
-
rawCount:
|
|
2043
|
+
weightedAccuracy: fs36.weightedAccuracy,
|
|
2044
|
+
rawCount: fs36.rawTotal
|
|
2019
2045
|
});
|
|
2020
2046
|
}
|
|
2021
2047
|
}
|
|
2022
2048
|
}
|
|
2023
2049
|
const boosts = /* @__PURE__ */ new Map();
|
|
2024
|
-
for (const
|
|
2025
|
-
if (
|
|
2050
|
+
for (const stat4 of globalStats) {
|
|
2051
|
+
if (stat4.rawTotal < FEEDBACK_BOOST_MIN_SAMPLES) continue;
|
|
2026
2052
|
let accuracy;
|
|
2027
2053
|
let sampleCount;
|
|
2028
|
-
const
|
|
2029
|
-
if (
|
|
2030
|
-
accuracy =
|
|
2031
|
-
sampleCount =
|
|
2054
|
+
const fs36 = folderStatsMap?.get(stat4.entity);
|
|
2055
|
+
if (fs36 && fs36.rawCount >= FEEDBACK_BOOST_MIN_SAMPLES) {
|
|
2056
|
+
accuracy = fs36.weightedAccuracy;
|
|
2057
|
+
sampleCount = fs36.rawCount;
|
|
2032
2058
|
} else {
|
|
2033
|
-
accuracy =
|
|
2034
|
-
sampleCount =
|
|
2059
|
+
accuracy = stat4.weightedAccuracy;
|
|
2060
|
+
sampleCount = stat4.rawTotal;
|
|
2035
2061
|
}
|
|
2036
2062
|
const boost = computeBoostFromAccuracy(accuracy, sampleCount);
|
|
2037
2063
|
if (boost !== 0) {
|
|
2038
|
-
boosts.set(
|
|
2064
|
+
boosts.set(stat4.entity, boost);
|
|
2039
2065
|
}
|
|
2040
2066
|
}
|
|
2041
2067
|
return boosts;
|
|
@@ -2043,18 +2069,18 @@ function getAllFeedbackBoosts(stateDb2, folder, now) {
|
|
|
2043
2069
|
function getAllSuppressionPenalties(stateDb2, now) {
|
|
2044
2070
|
const penalties = /* @__PURE__ */ new Map();
|
|
2045
2071
|
const weightedStats = getWeightedEntityStats(stateDb2, now);
|
|
2046
|
-
for (const
|
|
2047
|
-
const effectiveAlpha = getEffectiveAlpha(
|
|
2048
|
-
const posteriorMean = computePosteriorMean(
|
|
2049
|
-
const totalObs = effectiveAlpha +
|
|
2072
|
+
for (const stat4 of weightedStats) {
|
|
2073
|
+
const effectiveAlpha = getEffectiveAlpha(stat4.entity);
|
|
2074
|
+
const posteriorMean = computePosteriorMean(stat4.weightedCorrect, stat4.weightedFp, effectiveAlpha);
|
|
2075
|
+
const totalObs = effectiveAlpha + stat4.weightedCorrect + PRIOR_BETA + stat4.weightedFp;
|
|
2050
2076
|
if (totalObs >= SUPPRESSION_MIN_OBSERVATIONS) {
|
|
2051
2077
|
if (posteriorMean < SUPPRESSION_POSTERIOR_THRESHOLD) {
|
|
2052
2078
|
const penalty = Math.round(MAX_SUPPRESSION_PENALTY * (1 - posteriorMean / SUPPRESSION_POSTERIOR_THRESHOLD));
|
|
2053
2079
|
if (penalty < 0) {
|
|
2054
|
-
penalties.set(
|
|
2080
|
+
penalties.set(stat4.entity, penalty);
|
|
2055
2081
|
}
|
|
2056
2082
|
} else if (posteriorMean < SOFT_PENALTY_THRESHOLD) {
|
|
2057
|
-
penalties.set(
|
|
2083
|
+
penalties.set(stat4.entity, SOFT_PENALTY);
|
|
2058
2084
|
}
|
|
2059
2085
|
}
|
|
2060
2086
|
}
|
|
@@ -2632,8 +2658,8 @@ function clearLastMutationCommit() {
|
|
|
2632
2658
|
async function checkGitLock(vaultPath2) {
|
|
2633
2659
|
const lockPath = path10.join(vaultPath2, ".git/index.lock");
|
|
2634
2660
|
try {
|
|
2635
|
-
const
|
|
2636
|
-
const ageMs = Date.now() -
|
|
2661
|
+
const stat4 = await fs6.stat(lockPath);
|
|
2662
|
+
const ageMs = Date.now() - stat4.mtimeMs;
|
|
2637
2663
|
return {
|
|
2638
2664
|
locked: true,
|
|
2639
2665
|
stale: ageMs > STALE_LOCK_THRESHOLD_MS,
|
|
@@ -2655,8 +2681,8 @@ async function isGitRepo(vaultPath2) {
|
|
|
2655
2681
|
async function checkLockFile(vaultPath2) {
|
|
2656
2682
|
const lockPath = path10.join(vaultPath2, ".git/index.lock");
|
|
2657
2683
|
try {
|
|
2658
|
-
const
|
|
2659
|
-
const ageMs = Date.now() -
|
|
2684
|
+
const stat4 = await fs6.stat(lockPath);
|
|
2685
|
+
const ageMs = Date.now() - stat4.mtimeMs;
|
|
2660
2686
|
return { stale: ageMs > STALE_LOCK_THRESHOLD_MS, ageMs };
|
|
2661
2687
|
} catch {
|
|
2662
2688
|
return null;
|
|
@@ -2670,12 +2696,12 @@ function isLockContentionError(error) {
|
|
|
2670
2696
|
return false;
|
|
2671
2697
|
}
|
|
2672
2698
|
function sleep(ms) {
|
|
2673
|
-
return new Promise((
|
|
2699
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
2674
2700
|
}
|
|
2675
|
-
function calculateDelay(attempt,
|
|
2676
|
-
let delay =
|
|
2677
|
-
delay = Math.min(delay,
|
|
2678
|
-
if (
|
|
2701
|
+
function calculateDelay(attempt, config2) {
|
|
2702
|
+
let delay = config2.baseDelayMs * Math.pow(2, attempt);
|
|
2703
|
+
delay = Math.min(delay, config2.maxDelayMs);
|
|
2704
|
+
if (config2.jitter) {
|
|
2679
2705
|
delay = delay + Math.random() * delay * 0.5;
|
|
2680
2706
|
}
|
|
2681
2707
|
return Math.round(delay);
|
|
@@ -3569,8 +3595,8 @@ function setWriteStateDb(stateDb2) {
|
|
|
3569
3595
|
function getWriteStateDb() {
|
|
3570
3596
|
return getActiveScopeOrNull()?.stateDb ?? moduleStateDb5;
|
|
3571
3597
|
}
|
|
3572
|
-
function setWikilinkConfig(
|
|
3573
|
-
moduleConfig =
|
|
3598
|
+
function setWikilinkConfig(config2) {
|
|
3599
|
+
moduleConfig = config2;
|
|
3574
3600
|
}
|
|
3575
3601
|
function getConfig() {
|
|
3576
3602
|
const scope = getActiveScopeOrNull();
|
|
@@ -3938,9 +3964,9 @@ function isCommonWordFalsePositive(entityName, rawContent, category) {
|
|
|
3938
3964
|
if (!IMPLICIT_EXCLUDE_WORDS.has(lowerName) && !COMMON_ENGLISH_WORDS.has(lowerName)) return false;
|
|
3939
3965
|
return !rawContent.includes(entityName);
|
|
3940
3966
|
}
|
|
3941
|
-
function capScoreWithoutContentRelevance(score, contentRelevance,
|
|
3942
|
-
if (contentRelevance <
|
|
3943
|
-
return Math.min(score,
|
|
3967
|
+
function capScoreWithoutContentRelevance(score, contentRelevance, config2) {
|
|
3968
|
+
if (contentRelevance < config2.contentRelevanceFloor) {
|
|
3969
|
+
return Math.min(score, config2.noRelevanceCap);
|
|
3944
3970
|
}
|
|
3945
3971
|
return score;
|
|
3946
3972
|
}
|
|
@@ -3988,7 +4014,7 @@ function getAdaptiveMinScore(contentLength, baseScore) {
|
|
|
3988
4014
|
}
|
|
3989
4015
|
return baseScore;
|
|
3990
4016
|
}
|
|
3991
|
-
function scoreNameAgainstContent(name, contentTokens, contentStems,
|
|
4017
|
+
function scoreNameAgainstContent(name, contentTokens, contentStems, config2, coocIndex, disableExact, disableStem) {
|
|
3992
4018
|
const nameTokens = tokenize(name);
|
|
3993
4019
|
if (nameTokens.length === 0) {
|
|
3994
4020
|
return { exactScore: 0, stemScore: 0, lexicalScore: 0, matchedWords: 0, exactMatches: 0, totalTokens: 0, nameTokens: [], unmatchedTokenIndices: [] };
|
|
@@ -4004,11 +4030,11 @@ function scoreNameAgainstContent(name, contentTokens, contentStems, config, cooc
|
|
|
4004
4030
|
const nameStem = nameStems[i];
|
|
4005
4031
|
const idfWeight = coocIndex ? tokenIdf(token, coocIndex) : 1;
|
|
4006
4032
|
if (!disableExact && contentTokens.has(token)) {
|
|
4007
|
-
exactScore +=
|
|
4033
|
+
exactScore += config2.exactMatchBonus * idfWeight;
|
|
4008
4034
|
matchedWords++;
|
|
4009
4035
|
exactMatches++;
|
|
4010
4036
|
} else if (!disableStem && contentStems.has(nameStem)) {
|
|
4011
|
-
stemScore +=
|
|
4037
|
+
stemScore += config2.stemMatchBonus * idfWeight;
|
|
4012
4038
|
matchedWords++;
|
|
4013
4039
|
} else {
|
|
4014
4040
|
unmatchedTokenIndices.push(i);
|
|
@@ -4017,7 +4043,7 @@ function scoreNameAgainstContent(name, contentTokens, contentStems, config, cooc
|
|
|
4017
4043
|
const lexicalScore = Math.round((exactScore + stemScore) * 10) / 10;
|
|
4018
4044
|
return { exactScore, stemScore, lexicalScore, matchedWords, exactMatches, totalTokens: nameTokens.length, nameTokens, unmatchedTokenIndices };
|
|
4019
4045
|
}
|
|
4020
|
-
function scoreEntity(entity, contentTokens, contentStems, collapsedContentTerms,
|
|
4046
|
+
function scoreEntity(entity, contentTokens, contentStems, collapsedContentTerms, config2, disabled, coocIndex, tokenFuzzyCache) {
|
|
4021
4047
|
const zero = { contentMatch: 0, fuzzyMatch: 0, totalLexical: 0, matchedWords: 0, exactMatches: 0, totalTokens: 0 };
|
|
4022
4048
|
const entityName = getEntityName2(entity);
|
|
4023
4049
|
const aliases = getEntityAliases(entity);
|
|
@@ -4026,10 +4052,10 @@ function scoreEntity(entity, contentTokens, contentStems, collapsedContentTerms,
|
|
|
4026
4052
|
const disableFuzzy = disabled.has("fuzzy_match");
|
|
4027
4053
|
const cache = tokenFuzzyCache ?? /* @__PURE__ */ new Map();
|
|
4028
4054
|
const idfFn = (token) => coocIndex ? tokenIdf(token, coocIndex) : 1;
|
|
4029
|
-
const nameResult = scoreNameAgainstContent(entityName, contentTokens, contentStems,
|
|
4055
|
+
const nameResult = scoreNameAgainstContent(entityName, contentTokens, contentStems, config2, coocIndex, disableExact, disableStem);
|
|
4030
4056
|
let bestAliasResult = { exactScore: 0, stemScore: 0, lexicalScore: 0, matchedWords: 0, exactMatches: 0, totalTokens: 0, nameTokens: [], unmatchedTokenIndices: [] };
|
|
4031
4057
|
for (const alias of aliases) {
|
|
4032
|
-
const aliasResult = scoreNameAgainstContent(alias, contentTokens, contentStems,
|
|
4058
|
+
const aliasResult = scoreNameAgainstContent(alias, contentTokens, contentStems, config2, coocIndex, disableExact, disableStem);
|
|
4033
4059
|
if (aliasResult.lexicalScore > bestAliasResult.lexicalScore) {
|
|
4034
4060
|
bestAliasResult = aliasResult;
|
|
4035
4061
|
}
|
|
@@ -4056,7 +4082,7 @@ function scoreEntity(entity, contentTokens, contentStems, collapsedContentTerms,
|
|
|
4056
4082
|
contentTokens,
|
|
4057
4083
|
collapsedContentTerms,
|
|
4058
4084
|
fuzzyTargetName,
|
|
4059
|
-
|
|
4085
|
+
config2.fuzzyMatchBonus,
|
|
4060
4086
|
idfFn,
|
|
4061
4087
|
cache
|
|
4062
4088
|
);
|
|
@@ -4070,11 +4096,11 @@ function scoreEntity(entity, contentTokens, contentStems, collapsedContentTerms,
|
|
|
4070
4096
|
}
|
|
4071
4097
|
if (totalTokens > 1) {
|
|
4072
4098
|
const matchRatio = matchedWords / totalTokens;
|
|
4073
|
-
if (matchRatio <
|
|
4099
|
+
if (matchRatio < config2.minMatchRatio) {
|
|
4074
4100
|
return zero;
|
|
4075
4101
|
}
|
|
4076
4102
|
}
|
|
4077
|
-
if (
|
|
4103
|
+
if (config2.requireMultipleMatches && totalTokens === 1) {
|
|
4078
4104
|
if (exactMatches === 0 && fuzzyMatchedWords === 0) {
|
|
4079
4105
|
return zero;
|
|
4080
4106
|
}
|
|
@@ -4105,8 +4131,8 @@ async function suggestRelatedLinks(content, options = {}) {
|
|
|
4105
4131
|
disabledLayers = []
|
|
4106
4132
|
} = options;
|
|
4107
4133
|
const disabled = new Set(disabledLayers);
|
|
4108
|
-
const
|
|
4109
|
-
const adaptiveMinScore = getAdaptiveMinScore(content.length,
|
|
4134
|
+
const config2 = STRICTNESS_CONFIGS[strictness];
|
|
4135
|
+
const adaptiveMinScore = getAdaptiveMinScore(content.length, config2.minSuggestionScore);
|
|
4110
4136
|
const noteContext = notePath ? getNoteContext(notePath) : "general";
|
|
4111
4137
|
const contextBoosts = CONTEXT_BOOST[noteContext];
|
|
4112
4138
|
const emptyResult = { suggestions: [], suffix: "" };
|
|
@@ -4128,7 +4154,7 @@ async function suggestRelatedLinks(content, options = {}) {
|
|
|
4128
4154
|
const contentTokens = /* @__PURE__ */ new Set();
|
|
4129
4155
|
const contentStems = /* @__PURE__ */ new Set();
|
|
4130
4156
|
for (const token of rawTokens) {
|
|
4131
|
-
if (token.length >=
|
|
4157
|
+
if (token.length >= config2.minWordLength && !STOPWORDS_EN2.has(token)) {
|
|
4132
4158
|
contentTokens.add(token);
|
|
4133
4159
|
contentStems.add(stem(token));
|
|
4134
4160
|
}
|
|
@@ -4136,7 +4162,7 @@ async function suggestRelatedLinks(content, options = {}) {
|
|
|
4136
4162
|
if (contentTokens.size === 0) {
|
|
4137
4163
|
return emptyResult;
|
|
4138
4164
|
}
|
|
4139
|
-
const orderedContentTokens = [...rawTokens].filter((token) => token.length >=
|
|
4165
|
+
const orderedContentTokens = [...rawTokens].filter((token) => token.length >= config2.minWordLength && !STOPWORDS_EN2.has(token)).map(normalizeFuzzyTerm).filter((token) => token.length > 0);
|
|
4140
4166
|
const collapsedContentTerms = disabled.has("fuzzy_match") ? /* @__PURE__ */ new Set() : buildCollapsedContentTerms(orderedContentTokens);
|
|
4141
4167
|
const tokenFuzzyCache = /* @__PURE__ */ new Map();
|
|
4142
4168
|
const linkedEntities = excludeLinked ? extractLinkedEntities(content) : /* @__PURE__ */ new Set();
|
|
@@ -4167,7 +4193,7 @@ async function suggestRelatedLinks(content, options = {}) {
|
|
|
4167
4193
|
const paths = correctedPairs.get(entityName.toLowerCase());
|
|
4168
4194
|
if (paths.has(notePath)) continue;
|
|
4169
4195
|
}
|
|
4170
|
-
const entityScore = disabled.has("exact_match") && disabled.has("stem_match") && disabled.has("fuzzy_match") ? { contentMatch: 0, fuzzyMatch: 0, totalLexical: 0, matchedWords: 0, exactMatches: 0, totalTokens: 0 } : scoreEntity(entity, contentTokens, contentStems, collapsedContentTerms,
|
|
4196
|
+
const entityScore = disabled.has("exact_match") && disabled.has("stem_match") && disabled.has("fuzzy_match") ? { contentMatch: 0, fuzzyMatch: 0, totalLexical: 0, matchedWords: 0, exactMatches: 0, totalTokens: 0 } : scoreEntity(entity, contentTokens, contentStems, collapsedContentTerms, config2, disabled, cooccurrenceIndex, tokenFuzzyCache);
|
|
4171
4197
|
const contentScore = entityScore.contentMatch;
|
|
4172
4198
|
const fuzzyMatchScore = entityScore.fuzzyMatch;
|
|
4173
4199
|
const hasLexicalEvidence = entityScore.totalLexical > 0;
|
|
@@ -4204,7 +4230,7 @@ async function suggestRelatedLinks(content, options = {}) {
|
|
|
4204
4230
|
}
|
|
4205
4231
|
const layerSuppressionPenalty = disabled.has("feedback") ? 0 : suppressionPenalties.get(entityName) ?? 0;
|
|
4206
4232
|
score += layerSuppressionPenalty;
|
|
4207
|
-
score = capScoreWithoutContentRelevance(score, contentScore + fuzzyMatchScore,
|
|
4233
|
+
score = capScoreWithoutContentRelevance(score, contentScore + fuzzyMatchScore, config2);
|
|
4208
4234
|
if (hasLexicalEvidence && score >= adaptiveMinScore) {
|
|
4209
4235
|
scoredEntities.push({
|
|
4210
4236
|
name: entityName,
|
|
@@ -4271,13 +4297,13 @@ async function suggestRelatedLinks(content, options = {}) {
|
|
|
4271
4297
|
existing.score += boost;
|
|
4272
4298
|
existing.breakdown.cooccurrenceBoost += boost;
|
|
4273
4299
|
const existingContentRelevance = existing.breakdown.contentMatch + existing.breakdown.fuzzyMatch + (existing.breakdown.semanticBoost ?? 0);
|
|
4274
|
-
existing.score = capScoreWithoutContentRelevance(existing.score, existingContentRelevance,
|
|
4300
|
+
existing.score = capScoreWithoutContentRelevance(existing.score, existingContentRelevance, config2);
|
|
4275
4301
|
} else {
|
|
4276
4302
|
const entityTokens = tokenize(entityName);
|
|
4277
4303
|
const hasContentOverlap = entityTokens.some(
|
|
4278
4304
|
(token) => contentTokens.has(token) || contentStems.has(stem(token))
|
|
4279
4305
|
);
|
|
4280
|
-
const strongCooccurrence = boost >=
|
|
4306
|
+
const strongCooccurrence = boost >= config2.minCooccurrenceGate;
|
|
4281
4307
|
if (!hasContentOverlap && !strongCooccurrence) {
|
|
4282
4308
|
continue;
|
|
4283
4309
|
}
|
|
@@ -4297,7 +4323,7 @@ async function suggestRelatedLinks(content, options = {}) {
|
|
|
4297
4323
|
const suppPenalty = disabled.has("feedback") ? 0 : suppressionPenalties.get(entityName) ?? 0;
|
|
4298
4324
|
let totalBoost = boost + typeBoost + contextBoost + recencyBoostVal + crossFolderBoost + hubBoost + feedbackAdj + edgeWeightBoost + prospectBoost + suppPenalty;
|
|
4299
4325
|
const coocContentRelevance = hasContentOverlap ? 5 : 0;
|
|
4300
|
-
totalBoost = capScoreWithoutContentRelevance(totalBoost, coocContentRelevance,
|
|
4326
|
+
totalBoost = capScoreWithoutContentRelevance(totalBoost, coocContentRelevance, config2);
|
|
4301
4327
|
const effectiveMinScore = !hasContentOverlap ? Math.max(adaptiveMinScore, 7) : adaptiveMinScore;
|
|
4302
4328
|
if (totalBoost >= effectiveMinScore) {
|
|
4303
4329
|
scoredEntities.push({
|
|
@@ -4393,11 +4419,11 @@ async function suggestRelatedLinks(content, options = {}) {
|
|
|
4393
4419
|
}
|
|
4394
4420
|
for (const entry of scoredEntities) {
|
|
4395
4421
|
const contentRelevance = entry.breakdown.contentMatch + entry.breakdown.fuzzyMatch + (entry.breakdown.semanticBoost ?? 0);
|
|
4396
|
-
entry.score = capScoreWithoutContentRelevance(entry.score, contentRelevance,
|
|
4422
|
+
entry.score = capScoreWithoutContentRelevance(entry.score, contentRelevance, config2);
|
|
4397
4423
|
}
|
|
4398
4424
|
const relevantEntities = scoredEntities.filter((e) => {
|
|
4399
4425
|
if (!entitiesWithAnyScoringPath.has(e.name)) return false;
|
|
4400
|
-
if (
|
|
4426
|
+
if (config2.minContentMatch > 0 && e.breakdown.contentMatch < config2.minContentMatch) return false;
|
|
4401
4427
|
return true;
|
|
4402
4428
|
});
|
|
4403
4429
|
if (relevantEntities.length === 0) {
|
|
@@ -4664,16 +4690,16 @@ async function checkPreflightSimilarity(noteName) {
|
|
|
4664
4690
|
}
|
|
4665
4691
|
return result;
|
|
4666
4692
|
}
|
|
4667
|
-
async function applyProactiveSuggestions(filePath, vaultPath2, suggestions,
|
|
4693
|
+
async function applyProactiveSuggestions(filePath, vaultPath2, suggestions, config2) {
|
|
4668
4694
|
const stateDb2 = getWriteStateDb();
|
|
4669
|
-
const candidates = suggestions.filter((s) => s.score >=
|
|
4695
|
+
const candidates = suggestions.filter((s) => s.score >= config2.minScore && s.confidence === "high").slice(0, config2.maxPerFile);
|
|
4670
4696
|
if (candidates.length === 0) {
|
|
4671
4697
|
return { applied: [], skipped: [] };
|
|
4672
4698
|
}
|
|
4673
4699
|
const fullPath = path11.join(vaultPath2, filePath);
|
|
4674
4700
|
try {
|
|
4675
|
-
const
|
|
4676
|
-
if (Date.now() -
|
|
4701
|
+
const stat4 = await fs7.stat(fullPath);
|
|
4702
|
+
if (Date.now() - stat4.mtimeMs < 3e4) {
|
|
4677
4703
|
return { applied: [], skipped: candidates.map((c) => c.entity) };
|
|
4678
4704
|
}
|
|
4679
4705
|
} catch {
|
|
@@ -4973,7 +4999,7 @@ function enqueueProactiveSuggestions(stateDb2, entries) {
|
|
|
4973
4999
|
}
|
|
4974
5000
|
return enqueued;
|
|
4975
5001
|
}
|
|
4976
|
-
async function drainProactiveQueue(stateDb2, vaultPath2,
|
|
5002
|
+
async function drainProactiveQueue(stateDb2, vaultPath2, config2, applyFn) {
|
|
4977
5003
|
const result = {
|
|
4978
5004
|
applied: [],
|
|
4979
5005
|
expired: 0,
|
|
@@ -5017,7 +5043,7 @@ async function drainProactiveQueue(stateDb2, vaultPath2, config, applyFn) {
|
|
|
5017
5043
|
continue;
|
|
5018
5044
|
}
|
|
5019
5045
|
const todayCount = countTodayApplied.get(filePath, todayStr).cnt;
|
|
5020
|
-
if (todayCount >=
|
|
5046
|
+
if (todayCount >= config2.maxPerDay) {
|
|
5021
5047
|
result.skippedDailyCap += suggestions.length;
|
|
5022
5048
|
for (const s of suggestions) {
|
|
5023
5049
|
try {
|
|
@@ -5029,10 +5055,10 @@ async function drainProactiveQueue(stateDb2, vaultPath2, config, applyFn) {
|
|
|
5029
5055
|
}
|
|
5030
5056
|
continue;
|
|
5031
5057
|
}
|
|
5032
|
-
const remaining =
|
|
5058
|
+
const remaining = config2.maxPerDay - todayCount;
|
|
5033
5059
|
const capped = suggestions.slice(0, remaining);
|
|
5034
5060
|
try {
|
|
5035
|
-
const applyResult = await applyFn(filePath, vaultPath2, capped,
|
|
5061
|
+
const applyResult = await applyFn(filePath, vaultPath2, capped, config2);
|
|
5036
5062
|
if (applyResult.applied.length > 0) {
|
|
5037
5063
|
result.applied.push({ file: filePath, entities: applyResult.applied });
|
|
5038
5064
|
const appliedAt = Date.now();
|
|
@@ -5081,8 +5107,8 @@ var init_tool_embeddings_generated = __esm({
|
|
|
5081
5107
|
model: "Xenova/all-MiniLM-L6-v2",
|
|
5082
5108
|
dims: 384,
|
|
5083
5109
|
version: 1,
|
|
5084
|
-
generatedAt: "2026-04-
|
|
5085
|
-
sourceHash: "
|
|
5110
|
+
generatedAt: "2026-04-06T22:11:22.074Z",
|
|
5111
|
+
sourceHash: "55904c4f8b00ff85",
|
|
5086
5112
|
tools: [
|
|
5087
5113
|
{ name: "absorb_as_alias", category: "corrections", tier: 2, descriptionHash: "27554e8b6b3bb1a4", embedding: [-0.087313, -0.015109, 0.022541, 8661e-6, -0.024621, -0.027376, -9959e-6, -4143e-6, 0.020362, -0.025815, 0.03722, -0.017272, 0.010364, -0.082973, 0.063234, 0.105669, 0.030314, 0.109193, -0.024833, -0.022116, 0.020565, 0.067968, -0.013485, -4453e-6, 0.023703, -0.012531, -0.110704, 0.046425, 0.037009, -0.100492, 0.057064, 0.106952, -0.064645, 0.032072, 0.011054, 0.113279, 6113e-6, 2155e-6, 0.052647, -0.037832, 0.032187, -6735e-6, -0.027158, -0.033756, -0.071285, -0.025865, -0.040482, 0.02512, -0.037086, 4166e-6, -0.012929, -0.07895, -0.101903, 0.085107, -0.014636, 0.091582, -0.052795, 0.05756, -0.057044, -2231e-6, -0.013445, 2289e-6, -77e-5, -0.049304, -0.013433, -0.01156, 0.039263, 0.055036, -228e-5, -0.021411, 0.025402, -0.015392, -0.016927, -0.01263, 0.016506, 5128e-6, -0.045499, 0.041929, -0.098371, -0.102299, -0.05505, -0.021133, -0.011038, -0.045412, 0.051449, 0.043985, -0.026773, -0.061248, -0.013004, 0.020089, 0.020349, -0.049656, 0.14248, -0.023849, 1326e-6, 0.038924, 0.015142, -0.027207, 0.137747, 0.086333, -0.051942, 0.125201, -0.015227, 0.035816, -0.016593, -3143e-6, 0.016584, 0.057709, 3425e-6, -0.086785, 0.077055, 0.012249, 0.054973, -0.041238, 0.036758, -0.018482, 0.017911, -0.025632, 0.070721, -0.025979, 0.090299, 0.013505, -0.015338, 0.035118, -0.062938, -55e-6, -0.031485, 0, 0.048271, 0.118939, -7432e-6, -0.027482, -0.017351, -2898e-6, -0.036961, -3044e-6, -0.032485, -0.039268, 8085e-6, -0.01109, 0.025195, 273e-6, -0.081679, -0.043759, -1642e-6, 0.133163, 0.054599, 0.052329, -0.014879, 0.066345, -0.063461, 0.046176, -6885e-6, 0.050879, -0.032148, -0.03098, -0.01647, 2323e-6, -0.012937, 0.020916, 0.05669, 0.050599, -0.012922, 0.029857, -0.023119, -0.056459, -0.052397, -0.063056, 0.032488, 0.063419, 5355e-6, -0.081225, -2636e-6, -0.051448, -0.01502, 0.046703, 0.158323, -0.016006, 0.054453, -5225e-6, 0.055346, 3194e-6, -0.055126, -0.047608, -0.027439, -6931e-6, 0.015054, 0.070964, 0.016329, -7436e-6, -0.087056, 0.0326, -8886e-6, 0.069308, 1744e-6, -0.031456, 0.05816, -0.024318, -0.084045, 0.034875, -0.052924, 0.060689, -0.020979, -0.137039, -0.069438, 4228e-6, 0.103073, 0.026546, -0.043743, 0.015869, -1255e-6, -0.020976, -0.036293, 0.019493, 0.019183, -0.167767, -0.047504, -0.043037, -0.05305, -0.011494, 0.020628, -0.029303, 0.066295, -0, 0.06668, 6677e-6, -0.027402, 0.074721, -0.071961, 0.022582, 0.074355, 0.042852, -0.025373, 0.034536, 0.017667, -0.010542, -4169e-6, -0.083182, -2746e-6, -0.053237, 0.041962, -0.041424, -0.037738, 0.075779, 7597e-6, -0.035105, 0.089325, 0.059282, 0.087981, 0.014721, -6421e-6, -0.050171, 0.022545, -0.066806, 0.053691, -0.046373, -0.066624, -0.05594, -2923e-6, -0.072017, -0.035938, -0.031965, -0.078192, -0.01764, 0.047027, 0.05296, -0.039368, 0.060137, -0.014895, 2928e-6, -0.053409, 0.059899, -0.071938, -0.084993, 0.014731, -9255e-6, -0.012538, -0.057773, -0.01937, 0.011484, 0.118908, -4683e-6, -0.101681, -0.030016, 0.04375, 0.078497, -0.024205, -4214e-6, 0.041916, -0.032365, -0.03231, -8687e-6, -0.054573, 9854e-6, 3192e-6, -0.041575, 0.030278, -0.063586, 0.050707, -0.033235, 0.028844, -0.078475, -0.046236, 0.019996, -0.103771, -0.031058, 0.036113, 0.053975, -0.013333, -0.037514, -0.05237, 0.069865, -0.022002, 0.043238, -0.058168, -0.01832, 0.065564, 0.052553, -0.016286, -0, -0.060219, 0.015225, -0.044145, 0.023352, -0.052523, -0.013584, -0.073037, -0.033518, -0.01456, -7575e-6, 2766e-6, -0.030461, -0.045765, -0.042223, -0.032423, -0.03025, 0.020423, 0.066116, -0.085401, -0.038306, -0.12109, 0.040213, 131e-6, -0.04345, 0.070947, 0.014882, 7721e-6, 0.088877, 0.047515, -0.074231, 0.048281, -648e-6, 0.016151, 0.047549, -1148e-6, 0.028044, 0.088863, 0.014517, 0.022091, 0.123896, 0.05443, 0.032042, -0.05105, 0.069732, 0.057286, -0.060648, 108e-5, -0.050034, 0.03852, -0.017144, 0.024196, -0.032345, -0.031836, -0.030237, -0.01748, -1953e-6, 0.045463, 0.059662, 0.020283, -603e-5, 0.145025, -0.031073, 0.069219, 0.012386] },
|
|
5088
5114
|
{ name: "brief", category: "memory", tier: 1, descriptionHash: "cd54c8b5f8962c0c", embedding: [-0.03413, 0.038967, -0.058868, 5065e-6, 0.013581, -9201e-6, 0.12418, 0.084974, 0.0466, -0.027719, -0.03003, 2981e-6, -0.019524, 6126e-6, 0.121445, 0.030564, 0.113974, -0.019305, 0.010584, 0.022772, 0.040912, 0.049654, 0.012009, 0.014554, -317e-6, 0.010126, -0.084735, -1667e-6, 0.083471, -7537e-6, 0.013307, 0.106008, 0.040402, 0.065893, -0.017905, 0.099291, 0.017319, 0.015036, -6372e-6, -0.066617, -0.060547, -0.011005, -0.057781, 0.076661, 9708e-6, -0.045502, -0.044302, -9454e-6, -0.045664, 9896e-6, -0.053175, 0.012171, -0.021067, 0.071767, -8367e-6, 0.128232, -5307e-6, 0.069086, -0.038624, 0.028583, -0.068574, -0.100884, -0.052176, 0.044373, -0.012476, 0.041925, 0.032421, 9466e-6, 0.055237, -0.135804, -0.045271, -0.032277, -0.034088, 0.044972, -0.037657, 0.01615, 0.023087, -0.047546, 5856e-6, -0.118672, -0.013503, 0.027683, 0.021988, 623e-5, -9077e-6, -0.021581, 0.013314, -0.037775, -9507e-6, 0.037748, 85e-5, -0.052845, -7436e-6, 0.03521, 1699e-6, 0.028288, -0.035951, -0.049538, 3357e-6, 0.065678, 0.059998, 0.099599, 0.012784, 0.041595, -0.076292, -0.037029, -0.037229, 0.013993, -0.035633, 4893e-6, -0.057584, 0.047254, -0.010266, -0.037457, 0.077024, -0.074891, -0.038335, -0.034745, 0.026768, 0.120412, 0.06315, -0.034399, -0.048602, -0.025597, -0.086307, -0.035078, 0.03381, 0, 0.074016, -5576e-6, -0.035261, 0.135218, 8885e-6, 0.058899, -0.020769, -0.018972, -0.036681, -0.017513, 0.022473, 0.099641, -0.01237, 0.02122, 0.020966, -0.011241, -0.106938, 0.129492, 0.066344, -0.036365, -0.033113, -6609e-6, -6963e-6, -335e-5, 0.113098, 0.012043, 0.013074, -0.05108, -0.02582, 0.038526, -0.04915, -0.044315, -0.060091, -8148e-6, 0.053718, 0.02143, -5476e-6, -0.021335, -0.010914, -0.106699, -0.040708, 0.013624, 0.031717, -0.058864, -0.091039, -0.10624, -0.040266, 0.024783, 0.036104, 0.013351, 5674e-6, 0.013377, -0.014192, 0.012503, 6394e-6, -0.02841, -4971e-6, -0.022927, -2823e-6, 0.087578, 0.041583, 7853e-6, -0.083155, 0.025704, 0.033491, 0.011361, -0.04942, -0.044508, 0.0311, -0.046498, -0.027239, 3602e-6, 0.040659, 0.050427, -0.025982, 0.014929, 0.015421, 0.031525, -0.062248, 0.03429, 0.052807, -0.044957, -0.023206, 0.055626, -0.038784, 0.022958, 0.043242, -0.128127, -0.054922, 3364e-6, -0.076564, -0.037719, 0.03462, 0.055063, -0.070653, -0, 0.06233, -0.030029, -0.032604, 0.014582, 0.045522, 0.028487, 0.060517, -2969e-6, -0.114903, 0.015776, -0.035656, 5943e-6, 0.029417, 1415e-6, 0.010575, 6686e-6, 0.033255, -0.076697, -0.018056, 0.07942, 0.059409, -0.03671, -0.127963, 0.026159, 0.014287, -0.024613, -0.05576, -0.032465, -0.023479, -0.030419, 9631e-6, -9214e-6, -0.016757, 0.013094, -0.050212, -0.035049, 0.049743, -0.065495, -0.075293, -9875e-6, 0.129848, 0.020889, -0.079259, -0.026347, -0.02803, 2228e-6, -0.065164, 0.043506, -0.033239, -295e-6, -6674e-6, -0.011676, 0.014787, -0.070129, -0.065786, -0.041317, -3707e-6, -0.025125, -0.027828, -0.086907, 0.07926, 7317e-6, -0.053674, -0.034213, 0.054837, -0.062371, 0.036652, 0.070297, -0.090998, 0.015399, 0.052273, -0.051188, 0.039306, -0.066122, 0.040192, 0.02005, -0.022352, -0.17157, -0.039433, -7233e-6, -0.039524, 15e-4, -0.027335, 0.059864, 0.062908, 0.051305, -0.020745, 0.089525, -0.011786, -0.020093, -0.070926, -0.055612, -0.052107, 0.110824, -385e-5, -0, -0.070805, -0.035939, 0.03615, 0.036301, 0.044026, 2509e-6, -5092e-6, 0.068686, 0.037356, 0.02202, 0.025483, -0.032118, -0.107202, -0.04086, 0.048949, 0.038155, -5906e-6, 0.013435, -0.031491, -0.055225, 0.067576, 0.035719, -0.022875, 0.016182, 0.043324, 0.011605, 0.061764, 0.143727, 0.050779, -0.052731, -0.015323, 0.062416, -0.018611, -0.07632, -0.047169, 0.100932, 0.033838, -0.021418, 0.046654, 0.014842, 0.036839, -0.024439, 0.035967, 0.067279, -5978e-6, 0.097739, -0.058634, 0.020037, 1243e-6, -0.081814, -0.06543, 8175e-6, 5117e-6, 0.080321, 57e-6, 0.063212, 0.053892, -0.021493, 0.052762, 0.018396, 0.067694, 0.088151, -0.031927, -0.019349] },
|
|
@@ -5107,7 +5133,6 @@ var init_tool_embeddings_generated = __esm({
|
|
|
5107
5133
|
{ name: "get_note_structure", category: "read", tier: 1, descriptionHash: "d46b747577458fa3", embedding: [-0.034053, 0.058568, -0.013073, 0.051956, 1878e-6, -3484e-6, -0.021384, 0.029459, 0.01866, -0.048506, 4879e-6, -0.02044, 0.02816, -0.035958, 0.080657, 0.031094, 0.034978, 0.025658, 0.023314, -0.026633, 0.088011, 0.055726, 0.045466, -0.019004, -0.045637, 0.051535, -0.119519, -817e-5, 0.051132, -8649e-6, 0.044961, 0.056027, 0.074836, 0.074553, 475e-5, 0.110606, 1981e-6, -0.035258, 0.012263, -0.027338, -0.030202, 0.040594, -0.03311, 0.025563, 0.016389, -7492e-6, -0.057405, -1909e-6, -0.045447, 0.022976, -0.076963, -0.012285, -0.068218, 0.041681, 0.041577, 0.117179, 0.019101, -0.019711, -0.089762, -0.050572, 0.028838, -0.050339, -0.029523, -0.061273, -0.030107, 0.02912, 0.019308, -5468e-6, 0.049428, -0.14302, 0.095878, 9145e-6, 0.020457, 0.041474, 9214e-6, 4388e-6, -4607e-6, -0.014015, -0.047316, -0.11267, -0.098773, 0.044953, 8675e-6, 0.041169, 0.025942, -0.011891, 0.026525, -0.064685, -6678e-6, 0.061513, 0.096213, -0.123228, -0.065633, -0.019303, 8386e-6, 0.072118, 0.01913, -0.061346, 0.102101, 5084e-6, 0.055616, 0.078131, 0.079695, 0.018583, -0.035607, 0.0278, -0.045358, 0.060122, -0.048107, -0.049176, -0.011027, 0.064188, 0.053857, -0.01995, 6125e-6, -0.108854, -7305e-6, -0.03411, 0.045967, 0.111935, 0.112075, 0.01486, -0.022984, 0.015949, -0.102044, -0.012269, -0.075949, 0, 0.054186, 0.018959, 0.0149, 0.017011, -0.049196, 0.023439, 0.023984, 0.05285, -0.06726, -0.020796, -4855e-6, -7865e-6, -0.024857, 0.043349, -0.02489, -1547e-6, -0.045001, 0.06978, 0.019432, 0.03429, 0.011628, 7296e-6, 0.018451, -0.048771, 0.057362, 0.011788, -0.064353, -0.059757, -0.106931, -8745e-6, -0.036667, -0.079833, 0.024444, -0.047112, 0.016105, 0.06526, -0.040127, -4395e-6, -0.046705, -0.106663, 7097e-6, 3845e-6, 0.063465, -0.057979, -0.069587, 773e-6, -0.11854, 0.03829, 0.025318, 0.012997, 0.071065, 0.019042, -653e-5, 0.015978, 0.011882, 0.021631, 0.056059, 0.037697, -0.025745, 0.029873, 0.069977, -1341e-6, -0.017607, 0.032514, -0.040447, 0.023605, -0.032664, -0.0646, 0.069692, 8649e-6, -7163e-6, 0.047986, -7216e-6, 0.023747, -0.016205, -0.082133, -0.048612, -0.094448, 0.069621, -7387e-6, -0.038751, 0.01059, 0.040886, 0.077897, -0.032101, 0.034213, 0.053679, -0.182541, -0.026943, -0.061374, -0.044323, -0.04285, -0.047162, -0.073464, -8375e-6, -0, 0.045809, -0.051449, 0.014081, -0.057152, -0.024508, 0.044098, -0.031783, -2289e-6, -9969e-6, 0.082517, 0.02157, 0.014477, -0.029174, -0.04392, 0.011175, 0.048167, 9909e-6, -0.051525, 0.047285, 0.080488, -0.050922, -0.01143, -0.040418, 0.062125, 0.028396, -8963e-6, -0.051493, -0.048817, 0.027338, -0.066401, 0.017883, -0.053035, -0.053539, -0.036246, -0.057684, -0.034075, -414e-5, -0.073031, -0.040457, 0.086656, 0.068735, 0.107286, 4776e-6, -4959e-6, -0.047611, -0.028712, -0.024964, 0.089701, -0.045587, -0.043428, 0.053476, -0.030139, 3572e-6, -1346e-6, -0.018707, 0.049658, 5663e-6, 1265e-6, -0.078038, -0.049664, -0.022299, 0.065011, -0.11443, 0.034629, 3051e-6, -0.084661, 0.014807, -0.076934, -0.094602, -0.014275, -0.038493, 7669e-6, 0.05943, -0.032143, 0.065115, 0.079638, -0.061122, -0.014708, -0.043751, -0.029774, -0.015585, -0.015531, 0.043522, 0.07072, 0.040783, -0.031524, 9546e-6, 0.02525, -0.023371, -0.010383, -2826e-6, -0.059428, 9702e-6, 0.06052, 0.040492, -0, -0.11971, -0.057381, -0.063901, -0.019077, -5356e-6, -1707e-6, 0.040946, 0.026112, -0.042693, 5638e-6, 0.034895, -0.043335, -0.135079, -0.044002, -0.016958, 2444e-6, 0.091011, 0.011747, -0.040649, -0.039531, -0.015739, 0.053451, -0.049184, -1028e-6, 0.066912, 0.023815, 1227e-6, 0.099626, 0.038918, 0.016415, 0.082349, 0.038295, -0.012661, 5537e-6, 0.054001, 0.03846, 0.067522, -0.012906, -4587e-6, 0.111114, 0.082928, 0.054718, -0.064304, 0.059634, 0.073812, -0.026092, -0.040453, 0.016418, 0.038886, -0.059243, -0.028257, -0.024486, -0.051302, 7722e-6, -0.095123, 0.021788, 0.070279, 0.054412, 0.042015, -0.062366, 0.102527, 0.025488, 0.072279, 0.017399] },
|
|
5108
5134
|
{ name: "get_section_content", category: "read", tier: 1, descriptionHash: "3143db09c864e123", embedding: [-0.028641, 0.085473, -76e-6, 0.05119, 0.031131, 1587e-6, -0.018978, 0.057946, -0.01217, -0.034409, -567e-6, 8665e-6, 0.021315, -0.022961, 0.091059, -0.030405, 0.046316, 0.042966, 0.021119, -1504e-6, 0.053647, 0.101898, 8477e-6, 0.03071, -0.054229, 0.035659, -0.15261, 0.053486, -4781e-6, -0.043234, 0.043718, -0.012788, 0.018779, 0.011845, 8181e-6, 0.036182, 0.0978, -0.036718, 0.072551, 0.017413, -0.027154, 0.029073, -0.060769, 0.023862, 0.027807, -0.022091, -0.063303, -5347e-6, 7137e-6, -0.021798, -0.034714, -0.023498, -0.02925, 0.092221, 0.010238, 0.109993, 534e-6, -0.025775, -0.050855, 6795e-6, -0.064772, -0.069964, -4102e-6, -0.073128, -0.035828, 0.016207, -0.03324, 0.016634, -0.045777, -0.027336, 0.027186, -0.025611, 0.038306, 0.052737, 9481e-6, -0.025895, -0.050778, -0.043424, -0.049807, -0.119121, -0.084864, 0.066539, -0.016666, 0.049291, -0.018263, 5135e-6, -2983e-6, -0.012152, -0.070296, 0.027834, 0.053321, -0.082358, -0.014167, 0.030441, -0.064228, 0.044649, -144e-5, -0.109388, 0.060139, 0.015573, -8612e-6, 0.030612, 0.060786, -0.01802, -0.067729, -0.013319, -8888e-6, 0.028932, -0.045929, -0.042227, 0.010539, 0.033105, -0.015885, -4513e-6, 116e-6, -0.066676, 0.048411, -0.029626, 0.032187, 0.082548, 0.114786, -0.01838, -8558e-6, 0.065853, -0.074634, -0.039559, 0.031106, 0, 6209e-6, -8677e-6, 3492e-6, 0.05194, -0.032895, 0.03922, -0.031535, -898e-5, -0.025381, -0.014504, 0.013425, -0.047406, -7584e-6, 0.06063, -0.029333, -0.011628, 5507e-6, 0.057618, -0.025885, 0.023597, -0.031007, 2971e-6, 0.012595, -0.060759, 0.050706, 0.052725, -0.065455, 734e-6, -0.113944, -0.011068, -908e-5, -0.03675, 0.037886, -0.118776, 254e-5, 0.051742, 3988e-6, -2943e-6, -0.073215, -0.046089, -0.014969, -6248e-6, 0.06288, -0.03653, -0.076457, 0.040562, -0.053199, 0.046905, -665e-6, -0.03219, 0.052905, 0.018131, 0.047858, -0.034036, 0.052139, -0.031547, 3647e-6, 0.053643, -4297e-6, 7731e-6, 0.124256, 0.012433, -0.04248, 0.029914, -0.059878, 0.025681, -1956e-6, -0.046563, 167e-6, -2874e-6, -0.067715, 0.032702, -0.010757, 0.048113, 729e-6, -0.046407, -0.069157, -0.070698, 0.116455, -0.038527, -454e-5, 0.038641, 0.064124, 8841e-6, 8073e-6, 0.029897, 0.065531, -0.099762, -0.027115, -0.081608, -0.128888, -0.097702, 0.03308, -0.087019, 0.013349, -0, 0.072201, -0.014661, -0.048643, -0.060926, -0.077314, 0.059431, 0.011764, 0.035201, 0.016747, 0.085562, 0.018052, 9969e-6, -0.039091, -0.012121, 0.065632, -906e-5, -0.072452, -0.069153, 0.059193, 0.019054, -0.085421, -0.077256, -0.018214, 0.012881, 0.020715, 7903e-6, -0.055617, -0.029759, 0.015393, -0.088237, -8017e-6, -0.045987, -1644e-6, -8979e-6, -0.075813, -0.031571, -0.061005, -0.027525, -339e-5, 0.060508, 0.110903, 0.101027, 0.077668, -0.034899, -0.072861, 0.028758, 0.028136, 0.095881, -0.049, 0.016251, -0.045377, -0.04172, -4138e-6, 0.031363, -0.016481, 0.051365, -0.02863, 0.038089, -0.095247, -0.068709, -0.017717, 0.067932, -0.094599, 0.061901, 0.042007, -0.020073, 0.031228, -0.09808, -0.041746, -821e-6, -0.024428, -0.04163, 0.071431, -0.059914, 0.06119, 0.112077, -0.08753, 0.020743, -0.103866, 0.039082, -4396e-6, -0.02896, -0.056667, 0.066637, 0.090426, -0.014902, -0.050542, -0.016232, 0.015545, -0.017757, 7608e-6, -0.03554, 0.078521, 0.078539, -0.030191, -0, -0.017856, -0.069979, -0.012196, -0.062197, -0.054607, 0.061452, -0.028817, -0.059814, -0.033044, 0.023097, 5961e-6, -0.069378, -0.095493, -0.084676, -0.036096, 0.047724, 0.036811, 0.060726, -0.012988, -0.051958, -0.059858, 0.063799, -0.010038, -838e-5, 0.046756, 0.077427, -2525e-6, 0.093047, 0.057901, -0.037286, 0.114778, 0.032906, 0.026368, 0.016743, 0.085209, 0.042384, 0.038353, 0.051377, 0.079111, 0.114371, 0.081498, -557e-5, -0.036095, 0.027621, 0.078159, 2603e-6, -0.046535, 0.032286, 0.069105, -0.031222, -0.071619, -0.015424, -0.045878, 0.032742, -0.087811, 0.022644, 0.071666, 0.020854, -0.028094, -0.044219, 0.070801, 0.031554, 0.06052, 0.063509] },
|
|
5109
5135
|
{ name: "get_strong_connections", category: "graph", tier: 2, descriptionHash: "6e2af7b6029af542", embedding: [-0.037771, -0.027994, -0.068021, 0.023556, -0.090681, -0.051207, -0.041261, 0.017216, -0.06359, -0.055238, 806e-5, 0.02676, 8123e-6, 1787e-6, 6061e-6, 0.135593, 0.059854, 0.028837, -0.030354, -0.060799, 0.0423, -0.063322, -0.018217, -4433e-6, 0.047159, -0.055318, -0.083385, 0.060145, 0.073422, -0.064649, 356e-6, 5751e-6, -0.139108, 7324e-6, -0.042962, 0.037446, 0.053805, -0.037605, 372e-5, 0.011429, 0.026703, 0.020876, 0.03223, -0.03014, -0.040705, 0.016356, -0.101406, 0.074424, -0.047714, -0.037129, -0.088537, 0.012154, -0.057325, 0.045473, 0.084099, 0.054132, -5249e-6, -5968e-6, -0.055892, 0.068053, 0.054631, -0.04568, -0.069245, -0.020054, -0.04224, 5718e-6, -2155e-6, 0.114993, 7884e-6, 0.018219, 0.031962, -0.072873, -0.097566, 0.019057, 0.063863, 0.052558, 0.011555, -1668e-6, -0.067272, -0.120076, -0.06419, 0.01966, -0.028191, 0.045533, 0.068489, 814e-6, -0.012517, -0.081064, 0.038081, 0.052053, 0.036193, 0.040413, -0.052446, 0.026766, 0.028761, 0.060208, -0.025242, -0.087489, -0.04182, 0.056964, -0.017134, 0.016552, 5959e-6, -0.045877, 0.043168, 0.072253, -0.031332, 0.029309, 0.046305, -7642e-6, -0.022719, 0.034447, 0.022557, 3578e-6, -0.041477, -0.024104, -0.015113, 0.027006, 0.080433, 0.090614, 0.04795, 0.015571, -5917e-6, -0.030432, -0.036741, -0.040443, -4554e-6, 0, 0.059822, 0.0145, 0.051421, 0.015885, -0.022594, 0.037805, -0.078622, 0.014572, -0.061651, 4526e-6, -0.13038, 0.075147, -0.014407, 0.027354, 0.071657, -0.043857, 0.050021, 0.026305, 0.031753, -0.01056, 0.021751, -0.040921, -7686e-6, -0.02379, 0.114929, -0.034895, -9839e-6, 0.010689, -0.035789, 0.031663, -9746e-6, -0.039082, 0.017535, -0.013062, -0.015549, 0.02697, -0.067409, -0.018802, -0.025118, -0.024353, -0.022472, -0.04769, -0.022257, -0.021798, -0.036261, 0.023876, -0.072232, 0.017299, -0.036421, 9764e-6, -0.019015, -0.018019, 0.028643, 0.059689, 0.011619, -0.061575, -0.011098, 0.135227, 0.01339, 0.095882, 0.028497, -0.015913, -0.021305, 0.012422, 0.033127, 0.02329, -0.098138, -0.039889, 0.061132, 0.025942, -0.053474, 0.080174, 0.010111, -0.012041, -0.034366, -0.098504, -0.092239, -0.066017, 0.091857, 0.02822, -0.139999, 2266e-6, -2271e-6, 0.055157, -0.04945, 0.013613, 0.046267, -0.121393, -0.048062, -4372e-6, -0.082383, 3035e-6, 0.034201, -0.018272, 0.038456, -0, -2843e-6, 0.069342, 0.103103, 0.010287, 0.029563, -0.022873, 0.027775, -0.043618, 0.010446, 0.138289, 0.03564, 0.031568, 0.039643, -0.057704, 0.14409, -0.071936, -0.010229, -0.07411, 0.01354, 0.058899, -4457e-6, 0.05122, -0.064179, -1085e-6, 0.077797, -0.021331, 8365e-6, -0.110735, -0.012208, -2982e-6, 554e-6, 5155e-6, -0.081401, 7275e-6, -0.044218, 0.097863, -0.014108, 8492e-6, 0.040481, -0.026258, 0.065645, 0.06016, -0.0312, -0.026998, -0.014959, 0.047424, -0.05033, -0.019567, -0.101543, -0.033671, 0.016911, 0.041148, 0.018528, 0.058475, 0.011338, -1783e-6, -6998e-6, 0.051356, -0.057736, -0.025262, -0.010656, -0.061711, -0.043316, 0.076918, 0.016766, -0.030118, -1567e-6, -0.063203, -0.012617, 0.019781, 7747e-6, 0.083593, 0.064443, -259e-6, 0.030238, 0.019059, -0.058083, 9456e-6, -0.061151, 0.029125, -0.071687, 0.053585, 0.056561, 5823e-6, 0.023318, -0.020783, 0.025751, 0.035499, 0.068327, 0.019582, -0.015069, -0.02526, -0.041987, 3336e-6, -0.050373, -0, -0.076057, -0.060965, 8849e-6, -2043e-6, 0.015217, 0.080132, 0.037776, 0.092546, -0.037172, 0.115836, 0.026506, 6788e-6, -0.073694, 0.019286, -974e-5, -9829e-6, 0.046523, -0.018646, -0.041308, -0.059466, -0.096567, 192e-6, 0.025832, 0.129585, -0.01088, -0.06024, -0.019829, 0.028988, -0.011028, 0.017712, 0.022421, -0.043442, 0.01067, 0.013991, -0.031013, 0.152245, -0.121124, 0.078891, -0.028629, 0.117615, 0.026723, 0.03564, 0.032972, 0.05445, 0.111144, 0.037447, 0.02066, 0.018956, -8914e-6, -0.070642, -0.052423, -0.041498, -0.044652, -0.101854, -0.057583, -0.01058, -0.01979, -0.013734, -0.021211, -6494e-6, 2554e-6, 0.021907, 304e-5, -2321e-6] },
|
|
5110
|
-
{ name: "get_weighted_links", category: "graph", tier: 2, descriptionHash: "b15908c4739b5d3a", embedding: [-0.090975, -0.014501, -0.021864, 0.065818, -0.030286, -0.047781, -0.026542, 0.021761, -0.038294, -0.032738, -0.015163, -0.012753, 0.031894, 0.01047, 0.029989, 0.120474, 0.098771, 0.067441, -0.040694, -0.062644, 0.035177, -0.050144, -0.034699, -1753e-6, 0.070949, -0.10291, -0.13736, 0.074743, 0.089653, -0.05479, 0.020764, 0.020775, -0.10811, -0.010933, -0.097484, 0.018926, 0.046231, -0.062474, 0.022908, -0.021497, 3353e-6, 6996e-6, -1637e-6, -0.020593, -0.031638, 1062e-6, -0.07396, 0.049341, -0.096428, 0.032815, -0.058664, 0.01456, -0.032546, 0.070596, 0.048763, 0.05182, 0.023216, -0.039782, -0.049001, 0.037621, 0.029424, -0.047569, -0.074779, 0.011681, -0.012067, 3717e-6, -0.030751, 0.089352, 298e-6, -0.018922, 0.056772, -0.092662, -0.063705, 0.022077, 0.065903, 0.02897, -0.024127, -0.055941, -0.030059, -0.065825, -0.072135, 0.032097, -0.023324, 0.015092, 0.061272, -7666e-6, 0.012571, -0.051188, -0.010824, 0.079681, 0.013229, 0.065022, -0.012118, 0.063461, 0.017044, 0.059285, -0.04809, -0.079425, -0.016637, 0.076686, -5778e-6, 0.035252, 9646e-6, -0.021157, 0.022145, 6747e-6, 879e-6, 0.113067, 0.037145, -1402e-6, -0.036647, 0.037682, 0.04401, -6466e-6, -0.03851, -0.012131, 0.029901, 0.024441, 0.068556, 0.054587, 0.075806, 7062e-6, -0.016095, -0.076931, -0.022381, -0.035324, -0.02266, 0, 0.100173, -0.015921, 0.029145, -0.022108, -0.011857, 0.042613, -0.070996, -0.032842, -0.072239, -0.05262, -0.10894, 0.133753, 0.013103, 0.038695, 0.044727, -0.033728, 0.038247, 0.068295, 0.07883, -0.052155, 0.044765, -0.014218, 215e-6, 0.028915, 0.082827, -531e-6, -9761e-6, -0.019842, -0.06516, 0.047081, 4414e-6, -0.070453, -1103e-6, -0.052589, -0.018823, 0.012986, -0.037209, -0.010124, -0.011257, -0.071029, -0.016809, -0.037493, 331e-5, -0.014289, -0.078064, 7253e-6, -0.053571, 0.019083, -0.040748, 0.015793, 0.015338, -0.046805, 0.029152, 0.030541, -9975e-6, -0.024135, -0.011031, 0.086744, 0.013653, 0.032038, 0.037415, -0.013344, -0.040472, -3412e-6, -0.012778, 0.010936, -0.054764, -0.062977, 0.022929, 9276e-6, -2083e-6, 0.092949, 0.020496, 0.021255, -0.02846, -0.078688, -0.102784, -0.090458, 0.071563, 0.039953, -0.09925, -0.02562, 9363e-6, 0.026493, -0.04102, 0.024758, 0.024951, -0.134036, -0.039121, 8251e-6, -0.067827, -0.043155, -0.021372, -0.020859, 0.015264, -0, -0.029416, 0.080464, 0.060345, 0.010391, 0.02143, -6675e-6, 0.010375, -0.068239, -0.016398, 0.122661, 0.0456, 4852e-6, -0.02345, -0.025092, 0.133585, -0.079084, -0.025699, -0.044697, -0.05656, 0.03485, 0.040477, 0.051672, -0.082537, 0.09383, 0.095729, -0.021573, 0.015405, -0.051452, 81e-6, -0.022034, 4398e-6, -0.010902, -0.051642, 1343e-6, -0.02102, 0.073711, 0.024084, -0.039672, -0.012054, 3372e-6, 0.048955, 0.019228, -0.032866, -0.014474, -6002e-6, 0.021877, -0.070833, 6029e-6, -0.087879, -0.056169, 0.020016, 0.017023, 0.029297, 0.061129, -0.017747, -8161e-6, 0.017038, 0.056091, -0.092429, -0.026094, 0.016189, -0.020079, -0.0584, 0.066234, -1429e-6, -0.038221, 0.030045, -0.06633, -0.054358, 0.064258, 79e-6, 0.03817, 0.099264, -0.040309, 0.058427, -454e-6, -0.044228, 0.052338, -0.081565, -0.017557, -0.074933, 2475e-6, 0.127554, 7727e-6, 0.053678, -0.045102, 2107e-6, 0.037336, 0.040023, 4466e-6, -0.019027, -0.047474, -0.077896, 0.022258, -0.032852, -0, -0.07619, -0.01597, 0.030851, 0.057193, -0.021068, 0.108372, 0.055887, 0.076764, -0.017763, 0.10255, 0.034333, -0.039576, -0.061609, -0.02854, 0.014108, 0.016585, 0.030504, -4742e-6, -0.036712, -0.090062, -0.096437, -0.017134, 0.021224, 0.094701, 8389e-6, -0.012675, -0.020066, 0.035665, 0.050386, 0.028501, 0.038512, 238e-5, 295e-6, -0.039607, -0.024497, 0.142554, -0.075649, 0.028408, -0.039409, 0.119073, 0.046154, 0.060929, 0.039705, 0.05497, 0.116029, 0.047151, -0.059783, 0.068407, 0.039543, -0.086565, -0.01137, -0.08773, -0.061231, -0.037986, -0.052677, 0.015221, 0.022846, -7088e-6, 6404e-6, 0.010344, 0.112113, -0.020958, -6841e-6, 0.020835] },
|
|
5111
5136
|
{ name: "graph_analysis", category: "graph", tier: 2, descriptionHash: "21da4313470c8acf", embedding: [0.014982, 0.048667, -0.05496, 0.012859, -7511e-6, -0.080556, -0.080775, 0.010218, -6301e-6, 0.017034, 0.013734, -7477e-6, 0.060422, 0.076067, 1336e-6, 0.08975, -0.041889, 0.014917, 0.079108, -0.074126, 5897e-6, -0.056713, -0.010258, -0.042079, 0.062049, 8864e-6, -0.111993, 0.040664, 0.054887, -0.027314, -8775e-6, 0.049098, 0.016304, 0.015778, 2669e-6, 0.087052, 0.05313, 0.015003, 0.026028, -6206e-6, -0.033553, 0.01072, 0.017311, 0.035093, 0.012491, -0.034683, -0.153859, -2641e-6, -0.065432, 0.010869, -0.072863, 0.042287, -0.036151, 0.035371, 0.062681, 0.078202, 7565e-6, -0.027103, 8832e-6, -0.015145, 0.129478, -96e-5, -0.084373, 0.018726, 0.030673, 0.08512, 0.05594, 0.068198, 0.03111, 0.02847, 0.01319, -0.068583, -0.042534, 0.046815, 0.062125, 0.099523, -0.025556, 0.033946, -0.021172, -0.149886, -0.024221, -4303e-6, 2622e-6, 0.022859, -0.022048, 0.025957, -0.055911, -0.076048, 0.014287, 0.066012, 0.010988, 0.058647, -0.010238, -7619e-6, 0.046626, -6447e-6, -8203e-6, -0.041375, 3573e-6, 0.050071, 0.012537, -0.024124, 0.031998, -0.029242, -759e-5, 0.034055, -3655e-6, 0.019935, -0.020079, 3175e-6, -7819e-6, 0.024804, 0.023123, 8955e-6, 0.03206, -0.039055, -0.0511, -0.024222, 0.058775, 0.084928, 0.054938, -8055e-6, 0.067706, -0.057881, -0.017355, -0.017372, -0.071099, 0, 0.039272, -3494e-6, -0.049648, -0.017106, 0.026142, -7155e-6, -0.124206, -0.022045, -0.034048, 0.042819, -0.047638, 0.098044, -0.014169, 0.016236, 0.093998, 0.029911, 0.079728, 0.053739, -0.017549, -0.064304, 0.03097, -0.036439, 0.033522, 0.035343, 0.134186, 0.017999, 0.01137, -0.044759, -0.058738, 0.024135, -0.037312, 0.019298, 0.013909, 6484e-6, 0.038982, -0.014468, -6381e-6, -0.021227, -0.053476, -0.071868, -0.010886, -0.058535, 0.014366, -0.038675, -0.033031, 4711e-6, -0.013062, -0.031503, 0.01143, 0.02046, -0.024102, -6962e-6, -0.036209, 0.079632, -0.042118, -1449e-6, 0.013005, -1418e-6, 0.017152, 0.065533, 0.020589, -0.013602, -1018e-6, -0.029669, -0.035822, -0.026891, -0.12942, -0.02858, 0.076096, 0.044514, -0.054821, 0.082988, -6901e-6, 0.049194, -0.057347, -0.099937, -0.089271, -0.013113, -0.025257, -6699e-6, -0.083914, -0.079291, 0.014989, -0.010643, -1926e-6, -0.02845, 0.051667, -0.068924, -0.042244, -0.067379, -0.078613, -0.020605, -0.01071, 0.020394, 0.02361, -0, 5196e-6, 0.059459, 0.066098, 0.016319, 0.029665, -0.038921, 0.019445, -0.108754, 9862e-6, 0.099327, 0.011067, 0.019257, 7469e-6, -0.026524, 0.067341, -0.024194, -0.047803, -0.138884, -0.035914, 0.064449, 0.020511, -0.012535, -0.074021, 0.012927, 0.078131, 0.01318, -0.021434, -0.092091, 0.039101, 9615e-6, -0.034145, 0.013148, 0.011523, -0.010175, 0.019436, 0.023398, 0.101393, -0.080156, -0.03636, -0.066278, 0.05756, 0.068323, -0.095833, -0.029751, 0.031757, 0.075222, 66e-6, 0.073814, -0.151532, -0.067838, 0.023268, 0.060245, 0.072305, -0.038108, -0.024749, -9942e-6, 0.04971, 0.073733, -0.072968, 0.032388, 0.028846, -0.057119, -0.032219, 6511e-6, 1294e-6, -0.080719, 474e-5, -0.045133, -0.157085, 0.069242, 0.064041, 0.026467, -0.022417, -0.032153, 0.04462, -0.040503, -0.041883, -4582e-6, -5381e-6, -0.014891, -0.042522, -7419e-6, 0.060908, 0.053358, 0.013997, -697e-5, -8638e-6, 0.039554, 8026e-6, 0.027568, -0.042951, -0.046245, -0.118002, 0.030924, 0.032844, -0, -0.073586, -0.03406, -0.02731, -0.040197, 0.035654, -43e-4, 0.068621, 0.104104, -0.019369, 0.063457, 0.068685, -0.018536, -0.142823, -6219e-6, -0.040994, -0.039883, -504e-6, -0.011205, -0.037401, 0.014862, -0.0493, -1524e-6, -0.024527, 0.053348, 0.020769, -0.076086, -0.048086, 0.035365, 0.017626, -0.012496, 0.031508, 0.0174, 0.080662, -0.032491, -0.019471, 0.142096, 0.052662, 0.066275, -7575e-6, 0.089928, -0.020622, 0.022103, -0.021359, 5322e-6, 0.037768, 0.031636, -9026e-6, 0.091438, 0.049468, -0.069661, -0.018383, -0.064816, -0.094888, -0.050419, 0.015122, 0.029533, 0.01494, -0.021185, 0.085005, 0.033213, 0.107679, -0.02922, -1768e-6, -0.116227] },
|
|
5112
5137
|
{ name: "init_semantic", category: "search", tier: 1, descriptionHash: "43c65824a56583c8", embedding: [-0.036645, -0.048423, -0.02987, 0.017334, 918e-6, 0.017996, -0.055598, -0.046157, -0.060235, -0.03943, 0.0203, -0.068196, 0.06205, 4746e-6, 0.021267, 0.1163, 0.082818, 7109e-6, 7912e-6, -0.031837, 0.039406, 0.039051, 0.041681, -0.024567, -0.030271, -4424e-6, -0.073251, -0.01792, 0.06297, -0.021106, 0.068028, 0.119778, -8886e-6, 0.124277, -0.015507, 0.047549, -0.043916, -0.03781, 798e-6, -0.068685, -0.042466, 0.022345, -0.045979, 0.02311, 0.025188, 0.011397, -0.107553, -0.055501, -0.024378, 0.018865, -0.112677, -2974e-6, -0.063417, 0.01448, 0.06073, 0.052563, -0.029852, 0.026697, -0.026249, -0.095955, 0.011018, -0.064869, 0.03559, -0.013392, -0.025816, -6166e-6, 0.03396, -3985e-6, 0.074746, -0.051948, 0.04735, 5699e-6, -0.047815, 5083e-6, -6656e-6, 0.058819, 0.017751, 9618e-6, -0.034329, -0.142559, -0.092611, -0.011707, 0.054788, 0.026713, 0.046238, -0.058496, 0.053149, -0.064674, -7911e-6, 0.025146, 0.090716, -0.095411, -0.025432, -4719e-6, 0.044307, -9021e-6, 0.03782, 5209e-6, 0.090933, 0.010226, -0.031091, 0.059074, 0.050608, 0.02373, -0.066836, -6565e-6, -0.019631, -0.010734, 0.051661, -0.114177, -143e-5, 0.012429, 0.075123, -0.028144, 146e-5, -0.024961, 0.018168, -0.07083, 0.118612, 0.069403, 0.025245, 0.058002, -0.02929, -0.028592, -0.108462, 0.041775, -0.053198, 0, 0.081256, -6094e-6, -7254e-6, 0.056054, -0.015568, 0.037045, 0.028584, 0.106462, -0.131533, -0.058554, -0.077175, 0.046473, -0.028723, 0.082435, 0.02169, -0.05884, -0.066908, 7516e-6, -0.024083, -0.016896, 0.043299, 0.025071, -0.052559, -0.036259, 0.018016, 0.014868, 0.059475, -0.10813, -734e-6, 0.013911, -0.069934, -0.023654, -0.046397, 0.028024, 7712e-6, 0.013454, -0.034854, 0.039348, -0.084795, -0.118392, -0.010387, 0.027472, -0.013331, -0.125895, -0.041034, 0.016784, -0.037666, 0.010413, 0.062172, -0.028341, 0.054028, 0.056302, -0.026385, 0.017281, 0.080924, 0.022605, 0.028539, 0.040226, 0.033684, 4577e-6, 0.026979, -0.011067, 659e-6, 0.085469, -0.020598, 0.049251, 0.041391, -0.010572, 0.097357, 0.043209, 0.037071, 0.025847, 0.012736, -0.0629, -0.024554, -0.093576, -0.038353, -0.11029, -0.041252, 0.017235, -0.059938, -0.013057, 6945e-6, -0.02508, 1215e-6, -0.023817, 0.030486, -0.14928, 0.033516, -0.032182, 778e-5, -0.058548, -0.028677, -5839e-6, -0.018994, -0, 0.014996, -0.058951, 0.048915, 0.047534, -0.053714, -0.025611, 0.039118, 0.045065, -0.011793, 0.065329, 0.03063, -0.033068, 0.025043, -0.032686, -0.038037, 0.056003, -0.043733, -0.019769, 0.02776, 0.120434, -0.029215, 0.041728, -0.074871, 0.110722, 0.047525, 0.081546, 0.028591, -0.030607, -0.023528, -0.103818, 0.02561, -0.069368, -0.103536, -0.041664, -0.088307, 0.018, 31e-4, -0.02202, -0.099577, 0.04134, 8269e-6, 0.092631, -0.093147, 0.024291, 0.01071, -0.017115, -0.090242, 0.029033, 0.041009, -0.067994, 0.027573, -9146e-6, 0.032512, -0.033344, 1196e-6, -0.081688, 0.027215, 0.053545, -0.134254, 0.033339, 0.043814, -0.014692, 0.044891, 0.015723, -6774e-6, -0.021276, -0.036136, 0.043279, -0.04103, -3137e-6, -0.025336, 0.027017, 0.031852, 0.037979, 0.056888, 0.016986, 0.015888, -0.046773, -8154e-6, -0.053904, -0.066498, -0.012196, 0.056062, 0.091648, -0.044858, -73e-4, 0.01175, 0.061546, -0.017644, 0.02004, -66e-5, -0.018841, -0.018539, 0.079675, 0.022509, -0, -0.064331, -0.026576, -0.042327, 0.028332, -5853e-6, -0.024744, 0.047413, 0.051537, -0.056245, 4566e-6, 0.036773, -0.029659, -0.079398, 0.052442, 8523e-6, 0.020647, 9384e-6, -3593e-6, -0.014809, -0.013876, -328e-6, 0.079194, 0.010541, 0.048098, 0.018196, -6731e-6, 0.03661, 0.05604, 0.119197, 0.027253, -0.041915, 0.046821, -0.053771, -0.02169, 0.046453, 0.08273, 0.053455, 3144e-6, -0.037192, 0.043435, 0.073059, -0.034059, -0.036581, -0.041038, 0.088468, 0.015768, -0.094215, -0.021958, 0.035929, 0.011529, -0.025067, -0.013653, -0.04267, 0.0741, 4846e-6, 0.039491, -1457e-6, 7501e-6, 0.104915, -0.062235, 0.049405, -0.074123, 0.092452, 0.022295] },
|
|
5113
5138
|
{ name: "list_entities", category: "graph", tier: 2, descriptionHash: "151c9f9cc7ae7935", embedding: [9869e-6, 0.039221, -0.017985, 0.062127, 0.017846, 0.011479, -0.053497, 0.012181, -0.027631, 0.022112, 0.024476, -0.067664, 0.036259, -6546e-6, 0.060455, 0.083451, 0.04418, 0.055013, 0.05941, -0.150598, 0.090187, 0.018138, -0.029135, -0.021231, -5622e-6, -0.045522, -0.093901, 4966e-6, 0.019375, -0.045165, -0.046949, 0.08159, -0.05141, 0.107208, -0.062976, 0.015833, 0.072251, -0.034373, 7219e-6, -0.034998, 1076e-6, 0.013932, -0.015443, -0.025458, -0.026742, 0.020433, -0.110288, -0.017173, -0.059967, -572e-6, -0.07416, 0.038236, -0.052883, 0.05788, 0.068984, 0.076051, -0.018585, -0.029614, -0.060105, 8377e-6, 1809e-6, -0.016518, -9934e-6, 6265e-6, -0.053017, -9911e-6, 8147e-6, -404e-6, 0.043481, -0.098265, 0.080826, -0.038752, -0.055578, -0.021447, 0.046618, 5738e-6, 0.018936, 0.018056, -6225e-6, -0.09769, -0.11041, 0.089765, 0.041564, -4204e-6, -0.012703, -0.044349, -0.016736, -0.082801, -0.048237, 0.048849, -4514e-6, -0.014904, 0.091902, 0.011808, 0.06668, 0.059157, 0.067194, -0.081303, 0.044341, 0.016443, -0.039689, 0.02914, -0.032206, 0.021104, -0.073646, -0.013962, -0.014455, 0.040568, -0.022705, -8866e-6, -0.011802, 0.118062, -0.016973, -0.045874, 0.011322, -0.048643, -3256e-6, -0.0199, 0.063331, 0.037032, 0.042789, 0.03297, 6748e-6, -0.037267, -0.029454, 0.055477, -0.031838, 0, 0.132252, 0.032721, 0.040518, -0.029734, -0.088739, 0.028325, -0.027406, -8194e-6, -0.035274, 0.026574, -0.10169, 0.132857, -0.01054, 0.085067, 0.072249, -0.018404, 0.029723, 0.124009, 0.050307, -0.044323, 4088e-6, 0.034195, 8592e-6, 0.023349, 0.03524, 0.073328, -0.042406, -0.062921, -0.048544, 0.043889, -9885e-6, -0.046737, 4034e-6, 2817e-6, 0.023997, 0.066605, -0.041633, 0.023126, -0.028927, -0.076544, 0.027614, -0.025601, 0.022261, -0.039308, -0.08337, -102e-6, -0.039863, -0.058725, 0.041901, 0.062422, -261e-5, 155e-5, -0.02527, 0.047808, 0.016203, 0.020861, 0.072298, 7431e-6, -0.025393, 0.050597, 128e-6, -0.018836, -0.014498, 0.010012, -5951e-6, 0.029245, -0.029082, -0.042208, 0.06618, 0.011464, 0.046211, 0.100401, 0.096154, 101e-5, 893e-6, -0.065115, -0.074135, -0.146964, -0.034718, 0.025901, -0.050778, -0.039288, -0.060746, -0.017685, 0.029436, -0.042277, 3545e-6, -0.076755, 4314e-6, -0.032518, -0.07518, -2035e-6, 9925e-6, 121e-5, -0.031225, -0, 0.03046, -0.020084, 0.084786, -0.047479, 396e-6, -0.090925, 0.045119, 1007e-6, -0.090049, 0.087244, 1199e-6, -0.0213, -2816e-6, -0.034068, 0.046173, -0.019643, -0.052543, -0.061895, 4877e-6, 0.098381, -0.0366, -0.036437, -0.091761, 0.115674, 0.077214, -4719e-6, -0.036058, -0.074632, 0.040946, -0.080891, 0.076584, -0.071783, -0.030318, -0.018569, -6304e-6, -0.028519, -0.011312, -0.085765, -0.081931, -0.028463, -0.041507, 0.045099, -0.070759, 0.031346, 2321e-6, -0.014253, -9167e-6, 0.071664, -0.017914, -0.044684, -7361e-6, 0.033823, 0.064097, 0.034082, 0.031671, 9526e-6, -0.046141, 0.071486, -0.06172, -2236e-6, 0.030522, -3738e-6, -0.048973, 0.098027, 495e-5, -0.080678, 2526e-6, -0.049155, -0.064288, -0.01438, 0.01443, -0.017578, 9537e-6, -0.047826, 0.056887, -0.052009, -0.024801, 0.023291, -0.024461, 0.012786, -0.023644, -0.037693, 0.047088, 0.032393, 0.046701, -0.061738, 0.028013, 0.08225, 4238e-6, 0.039802, -0.028564, -0.050046, -0.115669, 0.074233, 0.050805, -0, -0.082516, 0.015736, -5736e-6, -0.033366, -0.028511, 9821e-6, 0.052369, 0.086908, -0.066551, 0.051093, 0.045218, -7851e-6, -0.121843, 4426e-6, 0.103404, -0.028686, 0.068992, 0.028774, 0.033047, 0.072594, -0.096302, -0.014749, -0.046137, -0.042746, -0.016003, -545e-5, -3868e-6, 0.047832, 0.046029, 0.038886, 0.025325, 0.070839, 0.015144, 8753e-6, 0.015014, 0.035623, 5691e-6, 0.010876, 4773e-6, 0.078394, 0.092455, 0.055167, -9887e-6, 0.067516, 0.051329, 0.031391, -0.01022, 0.017734, 0.064036, -0.063985, -0.124364, -0.058518, -0.089096, -0.042014, -0.023448, 0.04869, 0.013319, -0.032993, 0.058465, -0.016633, 0.130929, -0.055697, 7264e-6, -0.018472] },
|
|
@@ -5158,28 +5183,207 @@ var init_tool_embeddings_generated = __esm({
|
|
|
5158
5183
|
}
|
|
5159
5184
|
});
|
|
5160
5185
|
|
|
5161
|
-
// src/core/write/
|
|
5162
|
-
function estimateTokens(content) {
|
|
5163
|
-
const str = typeof content === "string" ? content : JSON.stringify(content);
|
|
5164
|
-
return Math.ceil(str.length / 4);
|
|
5165
|
-
}
|
|
5166
|
-
var HEADING_REGEX3;
|
|
5167
|
-
var init_constants2 = __esm({
|
|
5168
|
-
"src/core/write/constants.ts"() {
|
|
5169
|
-
"use strict";
|
|
5170
|
-
HEADING_REGEX3 = /^(#{1,6})\s+(.+)$/;
|
|
5171
|
-
}
|
|
5172
|
-
});
|
|
5173
|
-
|
|
5174
|
-
// src/core/write/writer.ts
|
|
5186
|
+
// src/core/write/path-security.ts
|
|
5175
5187
|
import fs20 from "fs/promises";
|
|
5176
5188
|
import path22 from "path";
|
|
5177
|
-
import matter5 from "gray-matter";
|
|
5178
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
5179
5189
|
function isSensitivePath(filePath) {
|
|
5180
5190
|
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
5181
5191
|
return SENSITIVE_PATH_PATTERNS.some((pattern) => pattern.test(normalizedPath));
|
|
5182
5192
|
}
|
|
5193
|
+
function isWithinDirectory(child, parent, allowEqual = false) {
|
|
5194
|
+
const rel = path22.relative(path22.resolve(parent), path22.resolve(child));
|
|
5195
|
+
if (rel === "") return allowEqual;
|
|
5196
|
+
const firstSeg = rel.split(path22.sep)[0];
|
|
5197
|
+
return firstSeg !== ".." && !path22.isAbsolute(rel);
|
|
5198
|
+
}
|
|
5199
|
+
function validatePath(vaultPath2, notePath) {
|
|
5200
|
+
if (notePath.startsWith("/")) {
|
|
5201
|
+
return false;
|
|
5202
|
+
}
|
|
5203
|
+
if (process.platform === "win32" && /^[a-zA-Z]:/.test(notePath)) {
|
|
5204
|
+
return false;
|
|
5205
|
+
}
|
|
5206
|
+
if (notePath.startsWith("\\")) {
|
|
5207
|
+
return false;
|
|
5208
|
+
}
|
|
5209
|
+
return isWithinDirectory(path22.resolve(vaultPath2, notePath), vaultPath2);
|
|
5210
|
+
}
|
|
5211
|
+
function sanitizeNotePath(notePath) {
|
|
5212
|
+
const dir = path22.dirname(notePath);
|
|
5213
|
+
let filename = path22.basename(notePath);
|
|
5214
|
+
const ext = filename.endsWith(".md") ? ".md" : "";
|
|
5215
|
+
let stem2 = ext ? filename.slice(0, -ext.length) : filename;
|
|
5216
|
+
stem2 = stem2.replace(/\s+/g, "-");
|
|
5217
|
+
stem2 = stem2.replace(/[?*<>|":]/g, "");
|
|
5218
|
+
stem2 = stem2.toLowerCase();
|
|
5219
|
+
stem2 = stem2.replace(/-{2,}/g, "-");
|
|
5220
|
+
stem2 = stem2.replace(/^-+|-+$/g, "");
|
|
5221
|
+
filename = stem2 + (ext || ".md");
|
|
5222
|
+
return dir === "." ? filename : path22.join(dir, filename).replace(/\\/g, "/");
|
|
5223
|
+
}
|
|
5224
|
+
async function validatePathSecure(vaultPath2, notePath) {
|
|
5225
|
+
if (notePath.startsWith("/")) {
|
|
5226
|
+
return {
|
|
5227
|
+
valid: false,
|
|
5228
|
+
reason: "Absolute paths not allowed"
|
|
5229
|
+
};
|
|
5230
|
+
}
|
|
5231
|
+
if (process.platform === "win32" && /^[a-zA-Z]:/.test(notePath)) {
|
|
5232
|
+
return {
|
|
5233
|
+
valid: false,
|
|
5234
|
+
reason: "Absolute paths not allowed"
|
|
5235
|
+
};
|
|
5236
|
+
}
|
|
5237
|
+
if (notePath.startsWith("\\")) {
|
|
5238
|
+
return {
|
|
5239
|
+
valid: false,
|
|
5240
|
+
reason: "Absolute paths not allowed"
|
|
5241
|
+
};
|
|
5242
|
+
}
|
|
5243
|
+
const firstSeg = path22.normalize(notePath).split(path22.sep).filter(Boolean)[0];
|
|
5244
|
+
if (firstSeg === "..") {
|
|
5245
|
+
return {
|
|
5246
|
+
valid: false,
|
|
5247
|
+
reason: "Path traversal not allowed"
|
|
5248
|
+
};
|
|
5249
|
+
}
|
|
5250
|
+
if (!isWithinDirectory(path22.resolve(vaultPath2, notePath), vaultPath2)) {
|
|
5251
|
+
return {
|
|
5252
|
+
valid: false,
|
|
5253
|
+
reason: "Path traversal not allowed"
|
|
5254
|
+
};
|
|
5255
|
+
}
|
|
5256
|
+
if (isSensitivePath(notePath)) {
|
|
5257
|
+
return {
|
|
5258
|
+
valid: false,
|
|
5259
|
+
reason: "Cannot write to sensitive file (credentials, keys, secrets)"
|
|
5260
|
+
};
|
|
5261
|
+
}
|
|
5262
|
+
try {
|
|
5263
|
+
const fullPath = path22.join(vaultPath2, notePath);
|
|
5264
|
+
try {
|
|
5265
|
+
await fs20.access(fullPath);
|
|
5266
|
+
const realPath = await fs20.realpath(fullPath);
|
|
5267
|
+
const realVaultPath = await fs20.realpath(vaultPath2);
|
|
5268
|
+
if (!isWithinDirectory(realPath, realVaultPath)) {
|
|
5269
|
+
return {
|
|
5270
|
+
valid: false,
|
|
5271
|
+
reason: "Symlink target is outside vault"
|
|
5272
|
+
};
|
|
5273
|
+
}
|
|
5274
|
+
const relativePath = path22.relative(realVaultPath, realPath);
|
|
5275
|
+
if (isSensitivePath(relativePath)) {
|
|
5276
|
+
return {
|
|
5277
|
+
valid: false,
|
|
5278
|
+
reason: "Symlink target is a sensitive file"
|
|
5279
|
+
};
|
|
5280
|
+
}
|
|
5281
|
+
} catch {
|
|
5282
|
+
const parentDir = path22.dirname(fullPath);
|
|
5283
|
+
try {
|
|
5284
|
+
await fs20.access(parentDir);
|
|
5285
|
+
const realParentPath = await fs20.realpath(parentDir);
|
|
5286
|
+
const realVaultPath = await fs20.realpath(vaultPath2);
|
|
5287
|
+
if (!isWithinDirectory(realParentPath, realVaultPath, true)) {
|
|
5288
|
+
return {
|
|
5289
|
+
valid: false,
|
|
5290
|
+
reason: "Parent directory symlink target is outside vault"
|
|
5291
|
+
};
|
|
5292
|
+
}
|
|
5293
|
+
} catch {
|
|
5294
|
+
}
|
|
5295
|
+
}
|
|
5296
|
+
} catch (error) {
|
|
5297
|
+
return {
|
|
5298
|
+
valid: false,
|
|
5299
|
+
reason: `Path validation error: ${error.message}`
|
|
5300
|
+
};
|
|
5301
|
+
}
|
|
5302
|
+
return { valid: true };
|
|
5303
|
+
}
|
|
5304
|
+
var SENSITIVE_PATH_PATTERNS;
|
|
5305
|
+
var init_path_security = __esm({
|
|
5306
|
+
"src/core/write/path-security.ts"() {
|
|
5307
|
+
"use strict";
|
|
5308
|
+
SENSITIVE_PATH_PATTERNS = [
|
|
5309
|
+
// Environment files (including backups, variations, and Windows ADS)
|
|
5310
|
+
/\.env($|\..*|~|\.swp|\.swo|:)/i,
|
|
5311
|
+
// .env, .env.local, .env~, .env.swp, .env:$DATA (ADS), etc.
|
|
5312
|
+
// Git credentials and config
|
|
5313
|
+
/\.git\/config$/i,
|
|
5314
|
+
// Git config (may contain tokens)
|
|
5315
|
+
/\.git\/credentials$/i,
|
|
5316
|
+
// Git credentials
|
|
5317
|
+
// SSL/TLS certificates and private keys (including backups)
|
|
5318
|
+
/\.pem($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5319
|
+
// SSL/TLS certificates + backups
|
|
5320
|
+
/\.key($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5321
|
+
// Private keys + backups
|
|
5322
|
+
/\.p12($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5323
|
+
// PKCS#12 certificates + backups
|
|
5324
|
+
/\.pfx($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5325
|
+
// Windows certificate format + backups
|
|
5326
|
+
/\.jks($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5327
|
+
// Java keystore + backups
|
|
5328
|
+
/\.crt($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5329
|
+
// Certificate files + backups
|
|
5330
|
+
// SSH keys
|
|
5331
|
+
/id_rsa/i,
|
|
5332
|
+
// SSH private key
|
|
5333
|
+
/id_ed25519/i,
|
|
5334
|
+
// SSH private key (ed25519)
|
|
5335
|
+
/id_ecdsa/i,
|
|
5336
|
+
// SSH private key (ecdsa)
|
|
5337
|
+
/id_dsa/i,
|
|
5338
|
+
// SSH private key (dsa)
|
|
5339
|
+
/\.ssh\/config$/i,
|
|
5340
|
+
// SSH config
|
|
5341
|
+
/authorized_keys$/i,
|
|
5342
|
+
// SSH authorized keys
|
|
5343
|
+
/known_hosts$/i,
|
|
5344
|
+
// SSH known hosts
|
|
5345
|
+
// Generic credentials/secrets files (including backups)
|
|
5346
|
+
/credentials\.json($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5347
|
+
// Cloud credentials + backups
|
|
5348
|
+
/secrets\.json($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5349
|
+
// Secrets files + backups
|
|
5350
|
+
/secrets\.ya?ml($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5351
|
+
// Secrets YAML + backups
|
|
5352
|
+
// Package manager auth
|
|
5353
|
+
/\.npmrc$/i,
|
|
5354
|
+
// npm config (may contain tokens)
|
|
5355
|
+
/\.netrc$/i,
|
|
5356
|
+
// Netrc (HTTP auth credentials)
|
|
5357
|
+
/\.yarnrc$/i,
|
|
5358
|
+
// Yarn config
|
|
5359
|
+
// Cloud provider credentials
|
|
5360
|
+
/\.aws\/credentials$/i,
|
|
5361
|
+
// AWS credentials
|
|
5362
|
+
/\.aws\/config$/i,
|
|
5363
|
+
// AWS config
|
|
5364
|
+
/gcloud\/credentials\.json/i,
|
|
5365
|
+
// Google Cloud credentials
|
|
5366
|
+
/\.azure\/credentials$/i,
|
|
5367
|
+
// Azure credentials
|
|
5368
|
+
/\.docker\/config\.json$/i,
|
|
5369
|
+
// Docker registry auth
|
|
5370
|
+
/\.kube\/config$/i,
|
|
5371
|
+
// Kubernetes config
|
|
5372
|
+
// System password files
|
|
5373
|
+
/\.htpasswd$/i,
|
|
5374
|
+
// Apache password file
|
|
5375
|
+
/shadow$/,
|
|
5376
|
+
// Unix shadow password file
|
|
5377
|
+
/passwd$/,
|
|
5378
|
+
// Unix password file
|
|
5379
|
+
// Hidden credential files (starting with dot)
|
|
5380
|
+
/^\.(credentials|secrets|tokens)$/i
|
|
5381
|
+
// .credentials, .secrets, .tokens
|
|
5382
|
+
];
|
|
5383
|
+
}
|
|
5384
|
+
});
|
|
5385
|
+
|
|
5386
|
+
// src/core/write/regex-safety.ts
|
|
5183
5387
|
function checkRegexSafety(pattern) {
|
|
5184
5388
|
if (pattern.length > MAX_REGEX_LENGTH) {
|
|
5185
5389
|
return `Regex pattern too long (${pattern.length} chars, max ${MAX_REGEX_LENGTH})`;
|
|
@@ -5216,21 +5420,62 @@ function safeRegexReplace(input, pattern, replacement, useRegex, global = false)
|
|
|
5216
5420
|
const regex = createSafeRegex(pattern, global ? "g" : void 0);
|
|
5217
5421
|
return input.replace(regex, replacement);
|
|
5218
5422
|
}
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5423
|
+
var REDOS_PATTERNS, MAX_REGEX_LENGTH;
|
|
5424
|
+
var init_regex_safety = __esm({
|
|
5425
|
+
"src/core/write/regex-safety.ts"() {
|
|
5426
|
+
"use strict";
|
|
5427
|
+
REDOS_PATTERNS = [
|
|
5428
|
+
// Nested quantifiers: (a+)+, (a*)+, (a+)*, (a*)*, etc.
|
|
5429
|
+
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
5430
|
+
// Quantifiers followed by optional same-type quantifiers
|
|
5431
|
+
/[+*]\??\s*[+*]/,
|
|
5432
|
+
// Overlapping character classes with quantifiers followed by similar
|
|
5433
|
+
/\[[^\]]*\][+*].*\[[^\]]*\][+*]/,
|
|
5434
|
+
// Multiple adjacent capturing groups with quantifiers
|
|
5435
|
+
/(\([^)]+[+*]\)){2,}/,
|
|
5436
|
+
// Extremely long alternation groups
|
|
5437
|
+
/\([^)]{100,}\)/
|
|
5438
|
+
];
|
|
5439
|
+
MAX_REGEX_LENGTH = 500;
|
|
5440
|
+
}
|
|
5441
|
+
});
|
|
5442
|
+
|
|
5443
|
+
// src/core/write/line-endings.ts
|
|
5444
|
+
function detectLineEnding(content) {
|
|
5445
|
+
const crlfCount = (content.match(/\r\n/g) || []).length;
|
|
5446
|
+
const lfCount = (content.match(/(?<!\r)\n/g) || []).length;
|
|
5447
|
+
return crlfCount > lfCount ? "CRLF" : "LF";
|
|
5448
|
+
}
|
|
5449
|
+
function normalizeLineEndings(content) {
|
|
5450
|
+
return content.replace(/\r\n/g, "\n");
|
|
5451
|
+
}
|
|
5452
|
+
function convertLineEndings(content, style) {
|
|
5453
|
+
const normalized = content.replace(/\r\n/g, "\n");
|
|
5229
5454
|
return style === "CRLF" ? normalized.replace(/\n/g, "\r\n") : normalized;
|
|
5230
5455
|
}
|
|
5231
5456
|
function normalizeTrailingNewline(content) {
|
|
5232
5457
|
return content.replace(/[\r\n\s]+$/, "") + "\n";
|
|
5233
5458
|
}
|
|
5459
|
+
var init_line_endings = __esm({
|
|
5460
|
+
"src/core/write/line-endings.ts"() {
|
|
5461
|
+
"use strict";
|
|
5462
|
+
}
|
|
5463
|
+
});
|
|
5464
|
+
|
|
5465
|
+
// src/core/write/constants.ts
|
|
5466
|
+
function estimateTokens(content) {
|
|
5467
|
+
const str = typeof content === "string" ? content : JSON.stringify(content);
|
|
5468
|
+
return Math.ceil(str.length / 4);
|
|
5469
|
+
}
|
|
5470
|
+
var HEADING_REGEX3;
|
|
5471
|
+
var init_constants2 = __esm({
|
|
5472
|
+
"src/core/write/constants.ts"() {
|
|
5473
|
+
"use strict";
|
|
5474
|
+
HEADING_REGEX3 = /^(#{1,6})\s+(.+)$/;
|
|
5475
|
+
}
|
|
5476
|
+
});
|
|
5477
|
+
|
|
5478
|
+
// src/core/write/markdown-structure.ts
|
|
5234
5479
|
function isEmptyPlaceholder(line) {
|
|
5235
5480
|
const trimmed = line.trim();
|
|
5236
5481
|
return EMPTY_PLACEHOLDER_PATTERNS.some((p) => p.test(trimmed));
|
|
@@ -5536,189 +5781,27 @@ function insertInSection(content, section, newContent, position, options) {
|
|
|
5536
5781
|
}
|
|
5537
5782
|
return lines.join("\n");
|
|
5538
5783
|
}
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
const ext = filename.endsWith(".md") ? ".md" : "";
|
|
5557
|
-
let stem2 = ext ? filename.slice(0, -ext.length) : filename;
|
|
5558
|
-
stem2 = stem2.replace(/\s+/g, "-");
|
|
5559
|
-
stem2 = stem2.replace(/[?*<>|":]/g, "");
|
|
5560
|
-
stem2 = stem2.toLowerCase();
|
|
5561
|
-
stem2 = stem2.replace(/-{2,}/g, "-");
|
|
5562
|
-
stem2 = stem2.replace(/^-+|-+$/g, "");
|
|
5563
|
-
filename = stem2 + (ext || ".md");
|
|
5564
|
-
return dir === "." ? filename : path22.join(dir, filename).replace(/\\/g, "/");
|
|
5565
|
-
}
|
|
5566
|
-
async function validatePathSecure(vaultPath2, notePath) {
|
|
5567
|
-
if (notePath.startsWith("/")) {
|
|
5568
|
-
return {
|
|
5569
|
-
valid: false,
|
|
5570
|
-
reason: "Absolute paths not allowed"
|
|
5571
|
-
};
|
|
5572
|
-
}
|
|
5573
|
-
if (process.platform === "win32" && /^[a-zA-Z]:/.test(notePath)) {
|
|
5574
|
-
return {
|
|
5575
|
-
valid: false,
|
|
5576
|
-
reason: "Absolute paths not allowed"
|
|
5577
|
-
};
|
|
5578
|
-
}
|
|
5579
|
-
if (notePath.startsWith("\\")) {
|
|
5580
|
-
return {
|
|
5581
|
-
valid: false,
|
|
5582
|
-
reason: "Absolute paths not allowed"
|
|
5583
|
-
};
|
|
5584
|
-
}
|
|
5585
|
-
if (notePath.startsWith("..")) {
|
|
5586
|
-
return {
|
|
5587
|
-
valid: false,
|
|
5588
|
-
reason: "Path traversal not allowed"
|
|
5589
|
-
};
|
|
5590
|
-
}
|
|
5591
|
-
const resolvedVault = path22.resolve(vaultPath2);
|
|
5592
|
-
const resolvedNote = path22.resolve(vaultPath2, notePath);
|
|
5593
|
-
if (!resolvedNote.startsWith(resolvedVault)) {
|
|
5594
|
-
return {
|
|
5595
|
-
valid: false,
|
|
5596
|
-
reason: "Path traversal not allowed"
|
|
5597
|
-
};
|
|
5598
|
-
}
|
|
5599
|
-
if (isSensitivePath(notePath)) {
|
|
5600
|
-
return {
|
|
5601
|
-
valid: false,
|
|
5602
|
-
reason: "Cannot write to sensitive file (credentials, keys, secrets)"
|
|
5603
|
-
};
|
|
5604
|
-
}
|
|
5605
|
-
try {
|
|
5606
|
-
const fullPath = path22.join(vaultPath2, notePath);
|
|
5607
|
-
try {
|
|
5608
|
-
await fs20.access(fullPath);
|
|
5609
|
-
const realPath = await fs20.realpath(fullPath);
|
|
5610
|
-
const realVaultPath = await fs20.realpath(vaultPath2);
|
|
5611
|
-
if (!realPath.startsWith(realVaultPath)) {
|
|
5612
|
-
return {
|
|
5613
|
-
valid: false,
|
|
5614
|
-
reason: "Symlink target is outside vault"
|
|
5615
|
-
};
|
|
5616
|
-
}
|
|
5617
|
-
const relativePath = path22.relative(realVaultPath, realPath);
|
|
5618
|
-
if (isSensitivePath(relativePath)) {
|
|
5619
|
-
return {
|
|
5620
|
-
valid: false,
|
|
5621
|
-
reason: "Symlink target is a sensitive file"
|
|
5622
|
-
};
|
|
5623
|
-
}
|
|
5624
|
-
} catch {
|
|
5625
|
-
const parentDir = path22.dirname(fullPath);
|
|
5626
|
-
try {
|
|
5627
|
-
await fs20.access(parentDir);
|
|
5628
|
-
const realParentPath = await fs20.realpath(parentDir);
|
|
5629
|
-
const realVaultPath = await fs20.realpath(vaultPath2);
|
|
5630
|
-
if (!realParentPath.startsWith(realVaultPath)) {
|
|
5631
|
-
return {
|
|
5632
|
-
valid: false,
|
|
5633
|
-
reason: "Parent directory symlink target is outside vault"
|
|
5634
|
-
};
|
|
5635
|
-
}
|
|
5636
|
-
} catch {
|
|
5637
|
-
}
|
|
5638
|
-
}
|
|
5639
|
-
} catch (error) {
|
|
5640
|
-
return {
|
|
5641
|
-
valid: false,
|
|
5642
|
-
reason: `Path validation error: ${error.message}`
|
|
5643
|
-
};
|
|
5644
|
-
}
|
|
5645
|
-
return { valid: true };
|
|
5646
|
-
}
|
|
5647
|
-
function computeContentHash(rawContent) {
|
|
5648
|
-
return createHash2("sha256").update(rawContent).digest("hex").slice(0, 16);
|
|
5649
|
-
}
|
|
5650
|
-
async function readVaultFile(vaultPath2, notePath) {
|
|
5651
|
-
if (!validatePath(vaultPath2, notePath)) {
|
|
5652
|
-
throw new Error("Invalid path: path traversal not allowed");
|
|
5653
|
-
}
|
|
5654
|
-
const fullPath = path22.join(vaultPath2, notePath);
|
|
5655
|
-
const [rawContent, stat5] = await Promise.all([
|
|
5656
|
-
fs20.readFile(fullPath, "utf-8"),
|
|
5657
|
-
fs20.stat(fullPath)
|
|
5658
|
-
]);
|
|
5659
|
-
const contentHash2 = computeContentHash(rawContent);
|
|
5660
|
-
const lineEnding = detectLineEnding(rawContent);
|
|
5661
|
-
const normalizedContent = normalizeLineEndings(rawContent);
|
|
5662
|
-
const parsed = matter5(normalizedContent);
|
|
5663
|
-
const frontmatter = deepCloneFrontmatter(parsed.data);
|
|
5664
|
-
return {
|
|
5665
|
-
content: parsed.content,
|
|
5666
|
-
frontmatter,
|
|
5667
|
-
rawContent,
|
|
5668
|
-
lineEnding,
|
|
5669
|
-
mtimeMs: stat5.mtimeMs,
|
|
5670
|
-
contentHash: contentHash2
|
|
5671
|
-
};
|
|
5672
|
-
}
|
|
5673
|
-
function deepCloneFrontmatter(obj) {
|
|
5674
|
-
if (obj === null || typeof obj !== "object") {
|
|
5675
|
-
return obj;
|
|
5676
|
-
}
|
|
5677
|
-
if (obj instanceof Date) {
|
|
5678
|
-
return new Date(obj.getTime());
|
|
5679
|
-
}
|
|
5680
|
-
if (Array.isArray(obj)) {
|
|
5681
|
-
return obj.map((item) => {
|
|
5682
|
-
if (item instanceof Date) {
|
|
5683
|
-
return new Date(item.getTime());
|
|
5684
|
-
}
|
|
5685
|
-
if (item !== null && typeof item === "object") {
|
|
5686
|
-
return deepCloneFrontmatter(item);
|
|
5687
|
-
}
|
|
5688
|
-
return item;
|
|
5689
|
-
});
|
|
5690
|
-
}
|
|
5691
|
-
const cloned = {};
|
|
5692
|
-
for (const key of Object.keys(obj)) {
|
|
5693
|
-
const value = obj[key];
|
|
5694
|
-
if (value instanceof Date) {
|
|
5695
|
-
cloned[key] = new Date(value.getTime());
|
|
5696
|
-
} else if (value !== null && typeof value === "object") {
|
|
5697
|
-
cloned[key] = deepCloneFrontmatter(value);
|
|
5698
|
-
} else {
|
|
5699
|
-
cloned[key] = value;
|
|
5700
|
-
}
|
|
5701
|
-
}
|
|
5702
|
-
return cloned;
|
|
5703
|
-
}
|
|
5704
|
-
async function writeVaultFile(vaultPath2, notePath, content, frontmatter, lineEnding = "LF", expectedHash) {
|
|
5705
|
-
const validation = await validatePathSecure(vaultPath2, notePath);
|
|
5706
|
-
if (!validation.valid) {
|
|
5707
|
-
throw new Error(`Invalid path: ${validation.reason}`);
|
|
5708
|
-
}
|
|
5709
|
-
const fullPath = path22.join(vaultPath2, notePath);
|
|
5710
|
-
if (expectedHash) {
|
|
5711
|
-
const currentRaw = await fs20.readFile(fullPath, "utf-8");
|
|
5712
|
-
const currentHash = computeContentHash(currentRaw);
|
|
5713
|
-
if (currentHash !== expectedHash) {
|
|
5714
|
-
throw new WriteConflictError(notePath);
|
|
5715
|
-
}
|
|
5784
|
+
var EMPTY_PLACEHOLDER_PATTERNS;
|
|
5785
|
+
var init_markdown_structure = __esm({
|
|
5786
|
+
"src/core/write/markdown-structure.ts"() {
|
|
5787
|
+
"use strict";
|
|
5788
|
+
init_constants2();
|
|
5789
|
+
EMPTY_PLACEHOLDER_PATTERNS = [
|
|
5790
|
+
/^\d+\.\s*$/,
|
|
5791
|
+
// "1. " or "2. " (numbered list placeholder)
|
|
5792
|
+
/^-\s*$/,
|
|
5793
|
+
// "- " (bullet placeholder)
|
|
5794
|
+
/^-\s*\[\s*\]\s*$/,
|
|
5795
|
+
// "- [ ] " (empty task placeholder)
|
|
5796
|
+
/^-\s*\[x\]\s*$/i,
|
|
5797
|
+
// "- [x] " (completed task placeholder)
|
|
5798
|
+
/^\*\s*$/
|
|
5799
|
+
// "* " (asterisk bullet placeholder)
|
|
5800
|
+
];
|
|
5716
5801
|
}
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
await fs20.writeFile(fullPath, output, "utf-8");
|
|
5721
|
-
}
|
|
5802
|
+
});
|
|
5803
|
+
|
|
5804
|
+
// src/core/write/content-mutation.ts
|
|
5722
5805
|
function removeFromSection(content, section, pattern, mode = "first", useRegex = false) {
|
|
5723
5806
|
const lines = content.split("\n");
|
|
5724
5807
|
const removedLines = [];
|
|
@@ -5956,119 +6039,12 @@ function injectMutationMetadata(frontmatter, scoping) {
|
|
|
5956
6039
|
}
|
|
5957
6040
|
return frontmatter;
|
|
5958
6041
|
}
|
|
5959
|
-
var
|
|
5960
|
-
var
|
|
5961
|
-
"src/core/write/
|
|
6042
|
+
var DiagnosticError;
|
|
6043
|
+
var init_content_mutation = __esm({
|
|
6044
|
+
"src/core/write/content-mutation.ts"() {
|
|
5962
6045
|
"use strict";
|
|
5963
|
-
|
|
6046
|
+
init_regex_safety();
|
|
5964
6047
|
init_levenshtein();
|
|
5965
|
-
SENSITIVE_PATH_PATTERNS = [
|
|
5966
|
-
// Environment files (including backups, variations, and Windows ADS)
|
|
5967
|
-
/\.env($|\..*|~|\.swp|\.swo|:)/i,
|
|
5968
|
-
// .env, .env.local, .env~, .env.swp, .env:$DATA (ADS), etc.
|
|
5969
|
-
// Git credentials and config
|
|
5970
|
-
/\.git\/config$/i,
|
|
5971
|
-
// Git config (may contain tokens)
|
|
5972
|
-
/\.git\/credentials$/i,
|
|
5973
|
-
// Git credentials
|
|
5974
|
-
// SSL/TLS certificates and private keys (including backups)
|
|
5975
|
-
/\.pem($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5976
|
-
// SSL/TLS certificates + backups
|
|
5977
|
-
/\.key($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5978
|
-
// Private keys + backups
|
|
5979
|
-
/\.p12($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5980
|
-
// PKCS#12 certificates + backups
|
|
5981
|
-
/\.pfx($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5982
|
-
// Windows certificate format + backups
|
|
5983
|
-
/\.jks($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5984
|
-
// Java keystore + backups
|
|
5985
|
-
/\.crt($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
5986
|
-
// Certificate files + backups
|
|
5987
|
-
// SSH keys
|
|
5988
|
-
/id_rsa/i,
|
|
5989
|
-
// SSH private key
|
|
5990
|
-
/id_ed25519/i,
|
|
5991
|
-
// SSH private key (ed25519)
|
|
5992
|
-
/id_ecdsa/i,
|
|
5993
|
-
// SSH private key (ecdsa)
|
|
5994
|
-
/id_dsa/i,
|
|
5995
|
-
// SSH private key (dsa)
|
|
5996
|
-
/\.ssh\/config$/i,
|
|
5997
|
-
// SSH config
|
|
5998
|
-
/authorized_keys$/i,
|
|
5999
|
-
// SSH authorized keys
|
|
6000
|
-
/known_hosts$/i,
|
|
6001
|
-
// SSH known hosts
|
|
6002
|
-
// Generic credentials/secrets files (including backups)
|
|
6003
|
-
/credentials\.json($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
6004
|
-
// Cloud credentials + backups
|
|
6005
|
-
/secrets\.json($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
6006
|
-
// Secrets files + backups
|
|
6007
|
-
/secrets\.ya?ml($|\.bak|\.backup|\.old|\.orig|~)$/i,
|
|
6008
|
-
// Secrets YAML + backups
|
|
6009
|
-
// Package manager auth
|
|
6010
|
-
/\.npmrc$/i,
|
|
6011
|
-
// npm config (may contain tokens)
|
|
6012
|
-
/\.netrc$/i,
|
|
6013
|
-
// Netrc (HTTP auth credentials)
|
|
6014
|
-
/\.yarnrc$/i,
|
|
6015
|
-
// Yarn config
|
|
6016
|
-
// Cloud provider credentials
|
|
6017
|
-
/\.aws\/credentials$/i,
|
|
6018
|
-
// AWS credentials
|
|
6019
|
-
/\.aws\/config$/i,
|
|
6020
|
-
// AWS config
|
|
6021
|
-
/gcloud\/credentials\.json/i,
|
|
6022
|
-
// Google Cloud credentials
|
|
6023
|
-
/\.azure\/credentials$/i,
|
|
6024
|
-
// Azure credentials
|
|
6025
|
-
/\.docker\/config\.json$/i,
|
|
6026
|
-
// Docker registry auth
|
|
6027
|
-
/\.kube\/config$/i,
|
|
6028
|
-
// Kubernetes config
|
|
6029
|
-
// System password files
|
|
6030
|
-
/\.htpasswd$/i,
|
|
6031
|
-
// Apache password file
|
|
6032
|
-
/shadow$/,
|
|
6033
|
-
// Unix shadow password file
|
|
6034
|
-
/passwd$/,
|
|
6035
|
-
// Unix password file
|
|
6036
|
-
// Hidden credential files (starting with dot)
|
|
6037
|
-
/^\.(credentials|secrets|tokens)$/i
|
|
6038
|
-
// .credentials, .secrets, .tokens
|
|
6039
|
-
];
|
|
6040
|
-
REDOS_PATTERNS = [
|
|
6041
|
-
// Nested quantifiers: (a+)+, (a*)+, (a+)*, (a*)*, etc.
|
|
6042
|
-
/(\([^)]*[+*][^)]*\))[+*]/,
|
|
6043
|
-
// Quantifiers followed by optional same-type quantifiers
|
|
6044
|
-
/[+*]\??\s*[+*]/,
|
|
6045
|
-
// Overlapping character classes with quantifiers followed by similar
|
|
6046
|
-
/\[[^\]]*\][+*].*\[[^\]]*\][+*]/,
|
|
6047
|
-
// Multiple adjacent capturing groups with quantifiers
|
|
6048
|
-
/(\([^)]+[+*]\)){2,}/,
|
|
6049
|
-
// Extremely long alternation groups
|
|
6050
|
-
/\([^)]{100,}\)/
|
|
6051
|
-
];
|
|
6052
|
-
MAX_REGEX_LENGTH = 500;
|
|
6053
|
-
EMPTY_PLACEHOLDER_PATTERNS = [
|
|
6054
|
-
/^\d+\.\s*$/,
|
|
6055
|
-
// "1. " or "2. " (numbered list placeholder)
|
|
6056
|
-
/^-\s*$/,
|
|
6057
|
-
// "- " (bullet placeholder)
|
|
6058
|
-
/^-\s*\[\s*\]\s*$/,
|
|
6059
|
-
// "- [ ] " (empty task placeholder)
|
|
6060
|
-
/^-\s*\[x\]\s*$/i,
|
|
6061
|
-
// "- [x] " (completed task placeholder)
|
|
6062
|
-
/^\*\s*$/
|
|
6063
|
-
// "* " (asterisk bullet placeholder)
|
|
6064
|
-
];
|
|
6065
|
-
WriteConflictError = class extends Error {
|
|
6066
|
-
constructor(notePath) {
|
|
6067
|
-
super(`Write conflict on ${notePath}: file was modified externally since it was read. Re-read and retry.`);
|
|
6068
|
-
this.notePath = notePath;
|
|
6069
|
-
this.name = "WriteConflictError";
|
|
6070
|
-
}
|
|
6071
|
-
};
|
|
6072
6048
|
DiagnosticError = class extends Error {
|
|
6073
6049
|
diagnostic;
|
|
6074
6050
|
constructor(message, diagnostic) {
|
|
@@ -6080,6 +6056,116 @@ var init_writer = __esm({
|
|
|
6080
6056
|
}
|
|
6081
6057
|
});
|
|
6082
6058
|
|
|
6059
|
+
// src/core/write/file-io.ts
|
|
6060
|
+
import fs21 from "fs/promises";
|
|
6061
|
+
import path23 from "path";
|
|
6062
|
+
import matter5 from "gray-matter";
|
|
6063
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
6064
|
+
function computeContentHash(rawContent) {
|
|
6065
|
+
return createHash2("sha256").update(rawContent).digest("hex").slice(0, 16);
|
|
6066
|
+
}
|
|
6067
|
+
async function readVaultFile(vaultPath2, notePath) {
|
|
6068
|
+
const validation = await validatePathSecure(vaultPath2, notePath);
|
|
6069
|
+
if (!validation.valid) {
|
|
6070
|
+
throw new Error(`Invalid path: ${validation.reason}`);
|
|
6071
|
+
}
|
|
6072
|
+
const fullPath = path23.join(vaultPath2, notePath);
|
|
6073
|
+
const [rawContent, stat4] = await Promise.all([
|
|
6074
|
+
fs21.readFile(fullPath, "utf-8"),
|
|
6075
|
+
fs21.stat(fullPath)
|
|
6076
|
+
]);
|
|
6077
|
+
const contentHash2 = computeContentHash(rawContent);
|
|
6078
|
+
const lineEnding = detectLineEnding(rawContent);
|
|
6079
|
+
const normalizedContent = normalizeLineEndings(rawContent);
|
|
6080
|
+
const parsed = matter5(normalizedContent);
|
|
6081
|
+
const frontmatter = deepCloneFrontmatter(parsed.data);
|
|
6082
|
+
return {
|
|
6083
|
+
content: parsed.content,
|
|
6084
|
+
frontmatter,
|
|
6085
|
+
rawContent,
|
|
6086
|
+
lineEnding,
|
|
6087
|
+
mtimeMs: stat4.mtimeMs,
|
|
6088
|
+
contentHash: contentHash2
|
|
6089
|
+
};
|
|
6090
|
+
}
|
|
6091
|
+
function deepCloneFrontmatter(obj) {
|
|
6092
|
+
if (obj === null || typeof obj !== "object") {
|
|
6093
|
+
return obj;
|
|
6094
|
+
}
|
|
6095
|
+
if (obj instanceof Date) {
|
|
6096
|
+
return new Date(obj.getTime());
|
|
6097
|
+
}
|
|
6098
|
+
if (Array.isArray(obj)) {
|
|
6099
|
+
return obj.map((item) => {
|
|
6100
|
+
if (item instanceof Date) {
|
|
6101
|
+
return new Date(item.getTime());
|
|
6102
|
+
}
|
|
6103
|
+
if (item !== null && typeof item === "object") {
|
|
6104
|
+
return deepCloneFrontmatter(item);
|
|
6105
|
+
}
|
|
6106
|
+
return item;
|
|
6107
|
+
});
|
|
6108
|
+
}
|
|
6109
|
+
const cloned = {};
|
|
6110
|
+
for (const key of Object.keys(obj)) {
|
|
6111
|
+
const value = obj[key];
|
|
6112
|
+
if (value instanceof Date) {
|
|
6113
|
+
cloned[key] = new Date(value.getTime());
|
|
6114
|
+
} else if (value !== null && typeof value === "object") {
|
|
6115
|
+
cloned[key] = deepCloneFrontmatter(value);
|
|
6116
|
+
} else {
|
|
6117
|
+
cloned[key] = value;
|
|
6118
|
+
}
|
|
6119
|
+
}
|
|
6120
|
+
return cloned;
|
|
6121
|
+
}
|
|
6122
|
+
async function writeVaultFile(vaultPath2, notePath, content, frontmatter, lineEnding = "LF", expectedHash) {
|
|
6123
|
+
const validation = await validatePathSecure(vaultPath2, notePath);
|
|
6124
|
+
if (!validation.valid) {
|
|
6125
|
+
throw new Error(`Invalid path: ${validation.reason}`);
|
|
6126
|
+
}
|
|
6127
|
+
const fullPath = path23.join(vaultPath2, notePath);
|
|
6128
|
+
if (expectedHash) {
|
|
6129
|
+
const currentRaw = await fs21.readFile(fullPath, "utf-8");
|
|
6130
|
+
const currentHash = computeContentHash(currentRaw);
|
|
6131
|
+
if (currentHash !== expectedHash) {
|
|
6132
|
+
throw new WriteConflictError(notePath);
|
|
6133
|
+
}
|
|
6134
|
+
}
|
|
6135
|
+
let output = matter5.stringify(content, frontmatter);
|
|
6136
|
+
output = normalizeTrailingNewline(output);
|
|
6137
|
+
output = convertLineEndings(output, lineEnding);
|
|
6138
|
+
await fs21.writeFile(fullPath, output, "utf-8");
|
|
6139
|
+
}
|
|
6140
|
+
var WriteConflictError;
|
|
6141
|
+
var init_file_io = __esm({
|
|
6142
|
+
"src/core/write/file-io.ts"() {
|
|
6143
|
+
"use strict";
|
|
6144
|
+
init_path_security();
|
|
6145
|
+
init_line_endings();
|
|
6146
|
+
WriteConflictError = class extends Error {
|
|
6147
|
+
constructor(notePath) {
|
|
6148
|
+
super(`Write conflict on ${notePath}: file was modified externally since it was read. Re-read and retry.`);
|
|
6149
|
+
this.notePath = notePath;
|
|
6150
|
+
this.name = "WriteConflictError";
|
|
6151
|
+
}
|
|
6152
|
+
};
|
|
6153
|
+
}
|
|
6154
|
+
});
|
|
6155
|
+
|
|
6156
|
+
// src/core/write/writer.ts
|
|
6157
|
+
var init_writer = __esm({
|
|
6158
|
+
"src/core/write/writer.ts"() {
|
|
6159
|
+
"use strict";
|
|
6160
|
+
init_path_security();
|
|
6161
|
+
init_regex_safety();
|
|
6162
|
+
init_line_endings();
|
|
6163
|
+
init_markdown_structure();
|
|
6164
|
+
init_content_mutation();
|
|
6165
|
+
init_file_io();
|
|
6166
|
+
}
|
|
6167
|
+
});
|
|
6168
|
+
|
|
6083
6169
|
// src/core/write/policy/template.ts
|
|
6084
6170
|
function formatDate2(date) {
|
|
6085
6171
|
const year = date.getFullYear();
|
|
@@ -6106,8 +6192,8 @@ function createContext(variables = {}) {
|
|
|
6106
6192
|
steps: {}
|
|
6107
6193
|
};
|
|
6108
6194
|
}
|
|
6109
|
-
function resolvePath(obj,
|
|
6110
|
-
const parts =
|
|
6195
|
+
function resolvePath(obj, path40) {
|
|
6196
|
+
const parts = path40.split(".");
|
|
6111
6197
|
let current = obj;
|
|
6112
6198
|
for (const part of parts) {
|
|
6113
6199
|
if (current === void 0 || current === null) {
|
|
@@ -6564,8 +6650,8 @@ __export(conditions_exports, {
|
|
|
6564
6650
|
evaluateCondition: () => evaluateCondition,
|
|
6565
6651
|
shouldStepExecute: () => shouldStepExecute
|
|
6566
6652
|
});
|
|
6567
|
-
import
|
|
6568
|
-
import
|
|
6653
|
+
import fs29 from "fs/promises";
|
|
6654
|
+
import path31 from "path";
|
|
6569
6655
|
async function evaluateCondition(condition, vaultPath2, context) {
|
|
6570
6656
|
const interpolatedPath = condition.path ? interpolate(condition.path, context) : void 0;
|
|
6571
6657
|
const interpolatedSection = condition.section ? interpolate(condition.section, context) : void 0;
|
|
@@ -6618,9 +6704,9 @@ async function evaluateCondition(condition, vaultPath2, context) {
|
|
|
6618
6704
|
}
|
|
6619
6705
|
}
|
|
6620
6706
|
async function evaluateFileExists(vaultPath2, notePath, expectExists) {
|
|
6621
|
-
const fullPath =
|
|
6707
|
+
const fullPath = path31.join(vaultPath2, notePath);
|
|
6622
6708
|
try {
|
|
6623
|
-
await
|
|
6709
|
+
await fs29.access(fullPath);
|
|
6624
6710
|
return {
|
|
6625
6711
|
met: expectExists,
|
|
6626
6712
|
reason: expectExists ? `File exists: ${notePath}` : `File exists (expected not to): ${notePath}`
|
|
@@ -6633,9 +6719,9 @@ async function evaluateFileExists(vaultPath2, notePath, expectExists) {
|
|
|
6633
6719
|
}
|
|
6634
6720
|
}
|
|
6635
6721
|
async function evaluateSectionExists(vaultPath2, notePath, sectionName, expectExists) {
|
|
6636
|
-
const fullPath =
|
|
6722
|
+
const fullPath = path31.join(vaultPath2, notePath);
|
|
6637
6723
|
try {
|
|
6638
|
-
await
|
|
6724
|
+
await fs29.access(fullPath);
|
|
6639
6725
|
} catch {
|
|
6640
6726
|
return {
|
|
6641
6727
|
met: !expectExists,
|
|
@@ -6664,9 +6750,9 @@ async function evaluateSectionExists(vaultPath2, notePath, sectionName, expectEx
|
|
|
6664
6750
|
}
|
|
6665
6751
|
}
|
|
6666
6752
|
async function evaluateFrontmatterExists(vaultPath2, notePath, fieldName, expectExists) {
|
|
6667
|
-
const fullPath =
|
|
6753
|
+
const fullPath = path31.join(vaultPath2, notePath);
|
|
6668
6754
|
try {
|
|
6669
|
-
await
|
|
6755
|
+
await fs29.access(fullPath);
|
|
6670
6756
|
} catch {
|
|
6671
6757
|
return {
|
|
6672
6758
|
met: !expectExists,
|
|
@@ -6695,9 +6781,9 @@ async function evaluateFrontmatterExists(vaultPath2, notePath, fieldName, expect
|
|
|
6695
6781
|
}
|
|
6696
6782
|
}
|
|
6697
6783
|
async function evaluateFrontmatterEquals(vaultPath2, notePath, fieldName, expectedValue) {
|
|
6698
|
-
const fullPath =
|
|
6784
|
+
const fullPath = path31.join(vaultPath2, notePath);
|
|
6699
6785
|
try {
|
|
6700
|
-
await
|
|
6786
|
+
await fs29.access(fullPath);
|
|
6701
6787
|
} catch {
|
|
6702
6788
|
return {
|
|
6703
6789
|
met: false,
|
|
@@ -6839,9 +6925,9 @@ var init_taskHelpers = __esm({
|
|
|
6839
6925
|
});
|
|
6840
6926
|
|
|
6841
6927
|
// src/index.ts
|
|
6842
|
-
import * as
|
|
6928
|
+
import * as path39 from "path";
|
|
6843
6929
|
import { readFileSync as readFileSync6, realpathSync, existsSync as existsSync3 } from "fs";
|
|
6844
|
-
import { fileURLToPath as
|
|
6930
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
6845
6931
|
import { dirname as dirname7, join as join20 } from "path";
|
|
6846
6932
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6847
6933
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -7079,8 +7165,8 @@ function updateIndexProgress(parsed, total) {
|
|
|
7079
7165
|
function normalizeTarget(target) {
|
|
7080
7166
|
return target.toLowerCase().replace(/\.md$/, "");
|
|
7081
7167
|
}
|
|
7082
|
-
function normalizeNotePath(
|
|
7083
|
-
return
|
|
7168
|
+
function normalizeNotePath(path40) {
|
|
7169
|
+
return path40.toLowerCase().replace(/\.md$/, "");
|
|
7084
7170
|
}
|
|
7085
7171
|
async function buildVaultIndex(vaultPath2, options = {}) {
|
|
7086
7172
|
const { timeoutMs = DEFAULT_TIMEOUT_MS, onProgress } = options;
|
|
@@ -7128,6 +7214,7 @@ async function buildVaultIndexInternal(vaultPath2, startTime, onProgress) {
|
|
|
7128
7214
|
console.error(`Parsed ${parsedCount}/${files.length} files (${elapsed}s)`);
|
|
7129
7215
|
onProgress?.(parsedCount, files.length);
|
|
7130
7216
|
}
|
|
7217
|
+
await new Promise((resolve3) => setImmediate(resolve3));
|
|
7131
7218
|
}
|
|
7132
7219
|
if (parseErrors.length > 0) {
|
|
7133
7220
|
const msg = `Failed to parse ${parseErrors.length} file(s):`;
|
|
@@ -7253,7 +7340,7 @@ function findSimilarEntity(index, target) {
|
|
|
7253
7340
|
}
|
|
7254
7341
|
const maxDist = normalizedLen <= 10 ? 1 : 2;
|
|
7255
7342
|
let bestMatch;
|
|
7256
|
-
for (const [entity,
|
|
7343
|
+
for (const [entity, path40] of index.entities) {
|
|
7257
7344
|
const lenDiff = Math.abs(entity.length - normalizedLen);
|
|
7258
7345
|
if (lenDiff > maxDist) {
|
|
7259
7346
|
continue;
|
|
@@ -7261,7 +7348,7 @@ function findSimilarEntity(index, target) {
|
|
|
7261
7348
|
const dist = levenshteinDistance(normalized, entity);
|
|
7262
7349
|
if (dist > 0 && dist <= maxDist) {
|
|
7263
7350
|
if (!bestMatch || dist < bestMatch.distance) {
|
|
7264
|
-
bestMatch = { path:
|
|
7351
|
+
bestMatch = { path: path40, entity, distance: dist };
|
|
7265
7352
|
if (dist === 1) {
|
|
7266
7353
|
return bestMatch;
|
|
7267
7354
|
}
|
|
@@ -7375,21 +7462,21 @@ var DEFAULT_CONFIG = {
|
|
|
7375
7462
|
implicit_detection: true,
|
|
7376
7463
|
adaptive_strictness: true
|
|
7377
7464
|
};
|
|
7378
|
-
function migrateExcludeConfig(
|
|
7465
|
+
function migrateExcludeConfig(config2) {
|
|
7379
7466
|
const oldTags = [
|
|
7380
|
-
...
|
|
7381
|
-
...
|
|
7467
|
+
...config2.exclude_task_tags ?? [],
|
|
7468
|
+
...config2.exclude_analysis_tags ?? []
|
|
7382
7469
|
];
|
|
7383
|
-
const oldEntities =
|
|
7384
|
-
if (oldTags.length === 0 && oldEntities.length === 0) return
|
|
7470
|
+
const oldEntities = config2.exclude_entities ?? [];
|
|
7471
|
+
if (oldTags.length === 0 && oldEntities.length === 0) return config2;
|
|
7385
7472
|
const normalizedTags = oldTags.map((t) => t.startsWith("#") ? t : `#${t}`);
|
|
7386
7473
|
const merged = /* @__PURE__ */ new Set([
|
|
7387
|
-
...
|
|
7474
|
+
...config2.exclude ?? [],
|
|
7388
7475
|
...normalizedTags,
|
|
7389
7476
|
...oldEntities
|
|
7390
7477
|
]);
|
|
7391
7478
|
return {
|
|
7392
|
-
...
|
|
7479
|
+
...config2,
|
|
7393
7480
|
exclude: Array.from(merged),
|
|
7394
7481
|
// Clear deprecated fields
|
|
7395
7482
|
exclude_task_tags: void 0,
|
|
@@ -7488,11 +7575,11 @@ function inferConfig(index, vaultPath2) {
|
|
|
7488
7575
|
}
|
|
7489
7576
|
return inferred;
|
|
7490
7577
|
}
|
|
7491
|
-
function getExcludeTags(
|
|
7492
|
-
return (
|
|
7578
|
+
function getExcludeTags(config2) {
|
|
7579
|
+
return (config2.exclude ?? []).filter((e) => e.startsWith("#")).map((e) => e.slice(1));
|
|
7493
7580
|
}
|
|
7494
|
-
function getExcludeEntities(
|
|
7495
|
-
return (
|
|
7581
|
+
function getExcludeEntities(config2) {
|
|
7582
|
+
return (config2.exclude ?? []).filter((e) => !e.startsWith("#"));
|
|
7496
7583
|
}
|
|
7497
7584
|
var TEMPLATE_PATTERNS = {
|
|
7498
7585
|
daily: /^daily[\s._-]*(note|template)?\.md$/i,
|
|
@@ -7685,8 +7772,8 @@ function normalizePath(filePath) {
|
|
|
7685
7772
|
function getRelativePath(vaultPath2, filePath) {
|
|
7686
7773
|
const normalizedVault = normalizePath(vaultPath2);
|
|
7687
7774
|
const normalizedFile = normalizePath(filePath);
|
|
7688
|
-
const
|
|
7689
|
-
return
|
|
7775
|
+
const relative2 = path5.posix.relative(normalizedVault, normalizedFile);
|
|
7776
|
+
return relative2;
|
|
7690
7777
|
}
|
|
7691
7778
|
function shouldWatch(filePath, vaultPath2) {
|
|
7692
7779
|
const normalized = normalizePath(filePath);
|
|
@@ -7812,38 +7899,38 @@ var EventQueue = class {
|
|
|
7812
7899
|
config;
|
|
7813
7900
|
flushTimer = null;
|
|
7814
7901
|
onBatch;
|
|
7815
|
-
constructor(
|
|
7816
|
-
this.config =
|
|
7902
|
+
constructor(config2, onBatch) {
|
|
7903
|
+
this.config = config2;
|
|
7817
7904
|
this.onBatch = onBatch;
|
|
7818
7905
|
}
|
|
7819
7906
|
/**
|
|
7820
7907
|
* Add a new event to the queue
|
|
7821
7908
|
*/
|
|
7822
7909
|
push(type, rawPath) {
|
|
7823
|
-
const
|
|
7910
|
+
const path40 = normalizePath(rawPath);
|
|
7824
7911
|
const now = Date.now();
|
|
7825
7912
|
const event = {
|
|
7826
7913
|
type,
|
|
7827
|
-
path:
|
|
7914
|
+
path: path40,
|
|
7828
7915
|
timestamp: now
|
|
7829
7916
|
};
|
|
7830
|
-
let pending = this.pending.get(
|
|
7917
|
+
let pending = this.pending.get(path40);
|
|
7831
7918
|
if (!pending) {
|
|
7832
7919
|
pending = {
|
|
7833
7920
|
events: [],
|
|
7834
7921
|
timer: null,
|
|
7835
7922
|
lastEvent: now
|
|
7836
7923
|
};
|
|
7837
|
-
this.pending.set(
|
|
7924
|
+
this.pending.set(path40, pending);
|
|
7838
7925
|
}
|
|
7839
7926
|
pending.events.push(event);
|
|
7840
7927
|
pending.lastEvent = now;
|
|
7841
|
-
console.error(`[flywheel] QUEUE: pushed ${type} for ${
|
|
7928
|
+
console.error(`[flywheel] QUEUE: pushed ${type} for ${path40}, pending=${this.pending.size}`);
|
|
7842
7929
|
if (pending.timer) {
|
|
7843
7930
|
clearTimeout(pending.timer);
|
|
7844
7931
|
}
|
|
7845
7932
|
pending.timer = setTimeout(() => {
|
|
7846
|
-
this.flushPath(
|
|
7933
|
+
this.flushPath(path40);
|
|
7847
7934
|
}, this.config.debounceMs);
|
|
7848
7935
|
if (this.pending.size >= this.config.batchSize) {
|
|
7849
7936
|
this.flush();
|
|
@@ -7864,10 +7951,10 @@ var EventQueue = class {
|
|
|
7864
7951
|
/**
|
|
7865
7952
|
* Flush a single path's events
|
|
7866
7953
|
*/
|
|
7867
|
-
flushPath(
|
|
7868
|
-
const pending = this.pending.get(
|
|
7954
|
+
flushPath(path40) {
|
|
7955
|
+
const pending = this.pending.get(path40);
|
|
7869
7956
|
if (!pending || pending.events.length === 0) return;
|
|
7870
|
-
console.error(`[flywheel] QUEUE: flushing ${
|
|
7957
|
+
console.error(`[flywheel] QUEUE: flushing ${path40}, events=${pending.events.length}`);
|
|
7871
7958
|
if (pending.timer) {
|
|
7872
7959
|
clearTimeout(pending.timer);
|
|
7873
7960
|
pending.timer = null;
|
|
@@ -7876,7 +7963,7 @@ var EventQueue = class {
|
|
|
7876
7963
|
if (coalescedType) {
|
|
7877
7964
|
const coalesced = {
|
|
7878
7965
|
type: coalescedType,
|
|
7879
|
-
path:
|
|
7966
|
+
path: path40,
|
|
7880
7967
|
originalEvents: [...pending.events]
|
|
7881
7968
|
};
|
|
7882
7969
|
this.onBatch({
|
|
@@ -7885,7 +7972,7 @@ var EventQueue = class {
|
|
|
7885
7972
|
timestamp: Date.now()
|
|
7886
7973
|
});
|
|
7887
7974
|
}
|
|
7888
|
-
this.pending.delete(
|
|
7975
|
+
this.pending.delete(path40);
|
|
7889
7976
|
}
|
|
7890
7977
|
/**
|
|
7891
7978
|
* Flush all pending events
|
|
@@ -7897,7 +7984,7 @@ var EventQueue = class {
|
|
|
7897
7984
|
}
|
|
7898
7985
|
if (this.pending.size === 0) return;
|
|
7899
7986
|
const events = [];
|
|
7900
|
-
for (const [
|
|
7987
|
+
for (const [path40, pending] of this.pending) {
|
|
7901
7988
|
if (pending.timer) {
|
|
7902
7989
|
clearTimeout(pending.timer);
|
|
7903
7990
|
}
|
|
@@ -7905,7 +7992,7 @@ var EventQueue = class {
|
|
|
7905
7992
|
if (coalescedType) {
|
|
7906
7993
|
events.push({
|
|
7907
7994
|
type: coalescedType,
|
|
7908
|
-
path:
|
|
7995
|
+
path: path40,
|
|
7909
7996
|
originalEvents: [...pending.events]
|
|
7910
7997
|
});
|
|
7911
7998
|
}
|
|
@@ -8099,8 +8186,8 @@ async function upsertNote(index, vaultPath2, notePath) {
|
|
|
8099
8186
|
releasedKeys = removeNoteFromIndex(index, notePath);
|
|
8100
8187
|
}
|
|
8101
8188
|
const fullPath = path7.join(vaultPath2, notePath);
|
|
8102
|
-
const
|
|
8103
|
-
const stats = await
|
|
8189
|
+
const fs36 = await import("fs/promises");
|
|
8190
|
+
const stats = await fs36.stat(fullPath);
|
|
8104
8191
|
const vaultFile = {
|
|
8105
8192
|
path: notePath,
|
|
8106
8193
|
absolutePath: fullPath,
|
|
@@ -8205,7 +8292,7 @@ async function processBatch(index, vaultPath2, batch, options = {}) {
|
|
|
8205
8292
|
}
|
|
8206
8293
|
onProgress?.(processed, total);
|
|
8207
8294
|
if (processed % YIELD_INTERVAL === 0 && processed < total) {
|
|
8208
|
-
await new Promise((
|
|
8295
|
+
await new Promise((resolve3) => setImmediate(resolve3));
|
|
8209
8296
|
}
|
|
8210
8297
|
}
|
|
8211
8298
|
const durationMs = Date.now() - startTime;
|
|
@@ -8226,7 +8313,7 @@ init_serverLog();
|
|
|
8226
8313
|
// src/core/read/watch/index.ts
|
|
8227
8314
|
function createVaultWatcher(options) {
|
|
8228
8315
|
const { vaultPath: vaultPath2, onBatch, onStateChange, onError } = options;
|
|
8229
|
-
const
|
|
8316
|
+
const config2 = {
|
|
8230
8317
|
...DEFAULT_WATCHER_CONFIG,
|
|
8231
8318
|
...parseWatcherConfig(),
|
|
8232
8319
|
...options.config
|
|
@@ -8271,7 +8358,7 @@ function createVaultWatcher(options) {
|
|
|
8271
8358
|
}
|
|
8272
8359
|
}
|
|
8273
8360
|
};
|
|
8274
|
-
const eventQueue = new EventQueue(
|
|
8361
|
+
const eventQueue = new EventQueue(config2, processBatch2);
|
|
8275
8362
|
const instance = {
|
|
8276
8363
|
get status() {
|
|
8277
8364
|
return getStatus();
|
|
@@ -8284,8 +8371,8 @@ function createVaultWatcher(options) {
|
|
|
8284
8371
|
console.error("[flywheel] Watcher already started");
|
|
8285
8372
|
return;
|
|
8286
8373
|
}
|
|
8287
|
-
console.error(`[flywheel] Starting file watcher (debounce: ${
|
|
8288
|
-
console.error(`[flywheel] Chokidar options: usePolling=${
|
|
8374
|
+
console.error(`[flywheel] Starting file watcher (debounce: ${config2.debounceMs}ms, flush: ${config2.flushMs}ms)`);
|
|
8375
|
+
console.error(`[flywheel] Chokidar options: usePolling=${config2.usePolling}, interval=${config2.pollInterval}, vaultPath=${vaultPath2}`);
|
|
8289
8376
|
watcher = chokidar.watch(vaultPath2, {
|
|
8290
8377
|
ignored: createIgnoreFunction(vaultPath2),
|
|
8291
8378
|
persistent: true,
|
|
@@ -8294,34 +8381,34 @@ function createVaultWatcher(options) {
|
|
|
8294
8381
|
stabilityThreshold: 300,
|
|
8295
8382
|
pollInterval: 100
|
|
8296
8383
|
},
|
|
8297
|
-
usePolling:
|
|
8298
|
-
interval:
|
|
8384
|
+
usePolling: config2.usePolling,
|
|
8385
|
+
interval: config2.usePolling ? config2.pollInterval : void 0
|
|
8299
8386
|
});
|
|
8300
|
-
watcher.on("add", (
|
|
8301
|
-
console.error(`[flywheel] RAW EVENT: add ${
|
|
8302
|
-
if (shouldWatch(
|
|
8303
|
-
console.error(`[flywheel] ACCEPTED: add ${
|
|
8304
|
-
eventQueue.push("add",
|
|
8387
|
+
watcher.on("add", (path40) => {
|
|
8388
|
+
console.error(`[flywheel] RAW EVENT: add ${path40}`);
|
|
8389
|
+
if (shouldWatch(path40, vaultPath2)) {
|
|
8390
|
+
console.error(`[flywheel] ACCEPTED: add ${path40}`);
|
|
8391
|
+
eventQueue.push("add", path40);
|
|
8305
8392
|
} else {
|
|
8306
|
-
console.error(`[flywheel] FILTERED: add ${
|
|
8393
|
+
console.error(`[flywheel] FILTERED: add ${path40}`);
|
|
8307
8394
|
}
|
|
8308
8395
|
});
|
|
8309
|
-
watcher.on("change", (
|
|
8310
|
-
console.error(`[flywheel] RAW EVENT: change ${
|
|
8311
|
-
if (shouldWatch(
|
|
8312
|
-
console.error(`[flywheel] ACCEPTED: change ${
|
|
8313
|
-
eventQueue.push("change",
|
|
8396
|
+
watcher.on("change", (path40) => {
|
|
8397
|
+
console.error(`[flywheel] RAW EVENT: change ${path40}`);
|
|
8398
|
+
if (shouldWatch(path40, vaultPath2)) {
|
|
8399
|
+
console.error(`[flywheel] ACCEPTED: change ${path40}`);
|
|
8400
|
+
eventQueue.push("change", path40);
|
|
8314
8401
|
} else {
|
|
8315
|
-
console.error(`[flywheel] FILTERED: change ${
|
|
8402
|
+
console.error(`[flywheel] FILTERED: change ${path40}`);
|
|
8316
8403
|
}
|
|
8317
8404
|
});
|
|
8318
|
-
watcher.on("unlink", (
|
|
8319
|
-
console.error(`[flywheel] RAW EVENT: unlink ${
|
|
8320
|
-
if (shouldWatch(
|
|
8321
|
-
console.error(`[flywheel] ACCEPTED: unlink ${
|
|
8322
|
-
eventQueue.push("unlink",
|
|
8405
|
+
watcher.on("unlink", (path40) => {
|
|
8406
|
+
console.error(`[flywheel] RAW EVENT: unlink ${path40}`);
|
|
8407
|
+
if (shouldWatch(path40, vaultPath2)) {
|
|
8408
|
+
console.error(`[flywheel] ACCEPTED: unlink ${path40}`);
|
|
8409
|
+
eventQueue.push("unlink", path40);
|
|
8323
8410
|
} else {
|
|
8324
|
-
console.error(`[flywheel] FILTERED: unlink ${
|
|
8411
|
+
console.error(`[flywheel] FILTERED: unlink ${path40}`);
|
|
8325
8412
|
}
|
|
8326
8413
|
});
|
|
8327
8414
|
watcher.on("ready", () => {
|
|
@@ -9479,6 +9566,104 @@ function refreshIfStale(vaultPath2, index, excludeTags) {
|
|
|
9479
9566
|
init_wikilinkFeedback();
|
|
9480
9567
|
init_corrections();
|
|
9481
9568
|
init_edgeWeights();
|
|
9569
|
+
var DeferredStepScheduler = class {
|
|
9570
|
+
timers = /* @__PURE__ */ new Map();
|
|
9571
|
+
executor = null;
|
|
9572
|
+
/** Set the executor context (called once during watcher setup) */
|
|
9573
|
+
setExecutor(exec) {
|
|
9574
|
+
this.executor = exec;
|
|
9575
|
+
}
|
|
9576
|
+
/** Schedule a deferred step to run after delayMs. Cancels any existing timer for this step. */
|
|
9577
|
+
schedule(step, delayMs) {
|
|
9578
|
+
this.cancel(step);
|
|
9579
|
+
const timer2 = setTimeout(() => {
|
|
9580
|
+
this.timers.delete(step);
|
|
9581
|
+
this.executeStep(step);
|
|
9582
|
+
}, delayMs);
|
|
9583
|
+
timer2.unref();
|
|
9584
|
+
this.timers.set(step, timer2);
|
|
9585
|
+
serverLog("deferred", `Scheduled ${step} in ${Math.round(delayMs / 1e3)}s`);
|
|
9586
|
+
}
|
|
9587
|
+
/** Cancel a pending deferred step */
|
|
9588
|
+
cancel(step) {
|
|
9589
|
+
const existing = this.timers.get(step);
|
|
9590
|
+
if (existing) {
|
|
9591
|
+
clearTimeout(existing);
|
|
9592
|
+
this.timers.delete(step);
|
|
9593
|
+
}
|
|
9594
|
+
}
|
|
9595
|
+
/** Cancel all pending deferred steps (called on shutdown) */
|
|
9596
|
+
cancelAll() {
|
|
9597
|
+
for (const timer2 of this.timers.values()) clearTimeout(timer2);
|
|
9598
|
+
this.timers.clear();
|
|
9599
|
+
}
|
|
9600
|
+
/** Check if any steps are pending */
|
|
9601
|
+
get pendingCount() {
|
|
9602
|
+
return this.timers.size;
|
|
9603
|
+
}
|
|
9604
|
+
async executeStep(step) {
|
|
9605
|
+
const exec = this.executor;
|
|
9606
|
+
if (!exec) return;
|
|
9607
|
+
if (exec.ctx.pipelineActivity.busy) {
|
|
9608
|
+
serverLog("deferred", `Skipping ${step}: pipeline busy`);
|
|
9609
|
+
return;
|
|
9610
|
+
}
|
|
9611
|
+
const start = Date.now();
|
|
9612
|
+
try {
|
|
9613
|
+
switch (step) {
|
|
9614
|
+
case "entity_scan": {
|
|
9615
|
+
await exec.updateEntitiesInStateDb(exec.vp, exec.sd);
|
|
9616
|
+
exec.ctx.lastEntityScanAt = Date.now();
|
|
9617
|
+
if (exec.sd) {
|
|
9618
|
+
await exportHubScores(exec.getVaultIndex(), exec.sd);
|
|
9619
|
+
exec.ctx.lastHubScoreRebuildAt = Date.now();
|
|
9620
|
+
}
|
|
9621
|
+
break;
|
|
9622
|
+
}
|
|
9623
|
+
case "hub_scores": {
|
|
9624
|
+
await exportHubScores(exec.getVaultIndex(), exec.sd);
|
|
9625
|
+
exec.ctx.lastHubScoreRebuildAt = Date.now();
|
|
9626
|
+
break;
|
|
9627
|
+
}
|
|
9628
|
+
case "recency": {
|
|
9629
|
+
const entities = exec.sd ? getAllEntitiesFromDb(exec.sd) : [];
|
|
9630
|
+
const entityInput = entities.map((e) => ({ name: e.name, path: e.path, aliases: e.aliases }));
|
|
9631
|
+
const recencyIndex2 = await buildRecencyIndex(exec.vp, entityInput);
|
|
9632
|
+
saveRecencyToStateDb(recencyIndex2, exec.sd ?? void 0);
|
|
9633
|
+
break;
|
|
9634
|
+
}
|
|
9635
|
+
case "cooccurrence": {
|
|
9636
|
+
const entities = exec.sd ? getAllEntitiesFromDb(exec.sd) : [];
|
|
9637
|
+
const entityNames = entities.map((e) => e.name);
|
|
9638
|
+
const cooccurrenceIdx = await mineCooccurrences(exec.vp, entityNames);
|
|
9639
|
+
setCooccurrenceIndex(cooccurrenceIdx);
|
|
9640
|
+
exec.ctx.lastCooccurrenceRebuildAt = Date.now();
|
|
9641
|
+
exec.ctx.cooccurrenceIndex = cooccurrenceIdx;
|
|
9642
|
+
if (exec.sd) saveCooccurrenceToStateDb(exec.sd, cooccurrenceIdx);
|
|
9643
|
+
break;
|
|
9644
|
+
}
|
|
9645
|
+
case "edge_weights": {
|
|
9646
|
+
if (exec.sd) {
|
|
9647
|
+
recomputeEdgeWeights(exec.sd);
|
|
9648
|
+
exec.ctx.lastEdgeWeightRebuildAt = Date.now();
|
|
9649
|
+
}
|
|
9650
|
+
break;
|
|
9651
|
+
}
|
|
9652
|
+
}
|
|
9653
|
+
const duration = Date.now() - start;
|
|
9654
|
+
serverLog("deferred", `Completed ${step} in ${duration}ms`);
|
|
9655
|
+
if (exec.sd) {
|
|
9656
|
+
recordIndexEvent(exec.sd, {
|
|
9657
|
+
trigger: "deferred",
|
|
9658
|
+
duration_ms: duration,
|
|
9659
|
+
note_count: exec.getVaultIndex().notes.size
|
|
9660
|
+
});
|
|
9661
|
+
}
|
|
9662
|
+
} catch (err) {
|
|
9663
|
+
serverLog("deferred", `Failed ${step}: ${err instanceof Error ? err.message : err}`, "error");
|
|
9664
|
+
}
|
|
9665
|
+
}
|
|
9666
|
+
};
|
|
9482
9667
|
var PIPELINE_TOTAL_STEPS = 22;
|
|
9483
9668
|
function createEmptyPipelineActivity() {
|
|
9484
9669
|
return {
|
|
@@ -9706,6 +9891,7 @@ var PipelineRunner = class {
|
|
|
9706
9891
|
tracker.skip("entity_scan", `cache valid (${Math.round(entityScanAgeMs / 1e3)}s old)`);
|
|
9707
9892
|
this.entitiesBefore = p.sd ? getAllEntitiesFromDb(p.sd) : [];
|
|
9708
9893
|
this.entitiesAfter = this.entitiesBefore;
|
|
9894
|
+
p.deferredScheduler?.schedule("entity_scan", 5 * 60 * 1e3 - entityScanAgeMs);
|
|
9709
9895
|
serverLog("watcher", `Entity scan: throttled (${Math.round(entityScanAgeMs / 1e3)}s old)`);
|
|
9710
9896
|
return;
|
|
9711
9897
|
}
|
|
@@ -9751,6 +9937,7 @@ var PipelineRunner = class {
|
|
|
9751
9937
|
const { p } = this;
|
|
9752
9938
|
const hubAgeMs = p.ctx.lastHubScoreRebuildAt > 0 ? Date.now() - p.ctx.lastHubScoreRebuildAt : Infinity;
|
|
9753
9939
|
if (hubAgeMs < 5 * 60 * 1e3) {
|
|
9940
|
+
p.deferredScheduler?.schedule("hub_scores", 5 * 60 * 1e3 - hubAgeMs);
|
|
9754
9941
|
serverLog("watcher", `Hub scores: throttled (${Math.round(hubAgeMs / 1e3)}s old)`);
|
|
9755
9942
|
return { skipped: true, age_ms: hubAgeMs };
|
|
9756
9943
|
}
|
|
@@ -9780,6 +9967,7 @@ var PipelineRunner = class {
|
|
|
9780
9967
|
serverLog("watcher", `Recency: rebuilt ${recencyIndex2.lastMentioned.size} entities`);
|
|
9781
9968
|
return { rebuilt: true, entities: recencyIndex2.lastMentioned.size };
|
|
9782
9969
|
}
|
|
9970
|
+
p.deferredScheduler?.schedule("recency", 60 * 60 * 1e3 - cacheAgeMs);
|
|
9783
9971
|
serverLog("watcher", `Recency: cache valid (${Math.round(cacheAgeMs / 1e3)}s old)`);
|
|
9784
9972
|
return { rebuilt: false, cached_age_ms: cacheAgeMs };
|
|
9785
9973
|
}
|
|
@@ -9799,6 +9987,7 @@ var PipelineRunner = class {
|
|
|
9799
9987
|
serverLog("watcher", `Co-occurrence: rebuilt ${cooccurrenceIdx._metadata.total_associations} associations`);
|
|
9800
9988
|
return { rebuilt: true, associations: cooccurrenceIdx._metadata.total_associations };
|
|
9801
9989
|
}
|
|
9990
|
+
p.deferredScheduler?.schedule("cooccurrence", 60 * 60 * 1e3 - cooccurrenceAgeMs);
|
|
9802
9991
|
serverLog("watcher", `Co-occurrence: cache valid (${Math.round(cooccurrenceAgeMs / 1e3)}s old)`);
|
|
9803
9992
|
return { rebuilt: false, age_ms: cooccurrenceAgeMs };
|
|
9804
9993
|
}
|
|
@@ -9821,6 +10010,7 @@ var PipelineRunner = class {
|
|
|
9821
10010
|
top_changes: result.top_changes
|
|
9822
10011
|
};
|
|
9823
10012
|
}
|
|
10013
|
+
p.deferredScheduler?.schedule("edge_weights", 60 * 60 * 1e3 - edgeWeightAgeMs);
|
|
9824
10014
|
serverLog("watcher", `Edge weights: cache valid (${Math.round(edgeWeightAgeMs / 1e3)}s old)`);
|
|
9825
10015
|
return { rebuilt: false, age_ms: edgeWeightAgeMs };
|
|
9826
10016
|
}
|
|
@@ -10810,7 +11000,7 @@ function getToolSelectionReport(stateDb2, daysBack = 7) {
|
|
|
10810
11000
|
}
|
|
10811
11001
|
|
|
10812
11002
|
// src/index.ts
|
|
10813
|
-
import { openStateDb, scanVaultEntities as
|
|
11003
|
+
import { openStateDb, scanVaultEntities as scanVaultEntities5, getAllEntitiesFromDb as getAllEntitiesFromDb6, loadContentHashes, saveContentHashBatch, renameContentHash, checkDbIntegrity as checkDbIntegrity2, safeBackupAsync as safeBackupAsync2, preserveCorruptedDb, deleteStateDbFiles, attemptSalvage } from "@velvetmonkey/vault-core";
|
|
10814
11004
|
|
|
10815
11005
|
// src/core/write/memory.ts
|
|
10816
11006
|
init_wikilinkFeedback();
|
|
@@ -11229,37 +11419,413 @@ function getSweepResults() {
|
|
|
11229
11419
|
return cachedResults;
|
|
11230
11420
|
}
|
|
11231
11421
|
|
|
11232
|
-
// src/core/
|
|
11233
|
-
|
|
11234
|
-
|
|
11235
|
-
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
|
|
11239
|
-
|
|
11240
|
-
|
|
11241
|
-
|
|
11242
|
-
"connected_ratio",
|
|
11243
|
-
"wikilink_accuracy",
|
|
11244
|
-
"wikilink_feedback_volume",
|
|
11245
|
-
"wikilink_suppressed_count"
|
|
11246
|
-
];
|
|
11247
|
-
function computeMetrics(index, stateDb2) {
|
|
11422
|
+
// src/core/read/watch/maintenance.ts
|
|
11423
|
+
init_serverLog();
|
|
11424
|
+
import { getAllEntitiesFromDb as getAllEntitiesFromDb2 } from "@velvetmonkey/vault-core";
|
|
11425
|
+
init_recency();
|
|
11426
|
+
init_cooccurrence();
|
|
11427
|
+
init_wikilinks();
|
|
11428
|
+
init_edgeWeights();
|
|
11429
|
+
|
|
11430
|
+
// src/core/shared/graphSnapshots.ts
|
|
11431
|
+
function computeGraphMetrics(index) {
|
|
11248
11432
|
const noteCount = index.notes.size;
|
|
11249
|
-
|
|
11250
|
-
|
|
11251
|
-
|
|
11433
|
+
if (noteCount === 0) {
|
|
11434
|
+
return {
|
|
11435
|
+
avg_degree: 0,
|
|
11436
|
+
max_degree: 0,
|
|
11437
|
+
cluster_count: 0,
|
|
11438
|
+
largest_cluster_size: 0,
|
|
11439
|
+
hub_scores_top10: []
|
|
11440
|
+
};
|
|
11252
11441
|
}
|
|
11253
|
-
const
|
|
11442
|
+
const degreeMap = /* @__PURE__ */ new Map();
|
|
11443
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
11254
11444
|
for (const [notePath, note] of index.notes) {
|
|
11255
|
-
if (
|
|
11256
|
-
|
|
11257
|
-
|
|
11258
|
-
|
|
11259
|
-
|
|
11260
|
-
|
|
11261
|
-
|
|
11262
|
-
|
|
11445
|
+
if (!adjacency.has(notePath)) adjacency.set(notePath, /* @__PURE__ */ new Set());
|
|
11446
|
+
let degree = note.outlinks.length;
|
|
11447
|
+
for (const link of note.outlinks) {
|
|
11448
|
+
const targetLower = link.target.toLowerCase();
|
|
11449
|
+
const resolvedPath = index.entities.get(targetLower);
|
|
11450
|
+
if (resolvedPath && index.notes.has(resolvedPath)) {
|
|
11451
|
+
adjacency.get(notePath).add(resolvedPath);
|
|
11452
|
+
if (!adjacency.has(resolvedPath)) adjacency.set(resolvedPath, /* @__PURE__ */ new Set());
|
|
11453
|
+
adjacency.get(resolvedPath).add(notePath);
|
|
11454
|
+
}
|
|
11455
|
+
}
|
|
11456
|
+
degreeMap.set(notePath, degree);
|
|
11457
|
+
}
|
|
11458
|
+
for (const [target, backlinks] of index.backlinks) {
|
|
11459
|
+
const targetLower = target.toLowerCase();
|
|
11460
|
+
const resolvedPath = index.entities.get(targetLower);
|
|
11461
|
+
if (resolvedPath && degreeMap.has(resolvedPath)) {
|
|
11462
|
+
degreeMap.set(resolvedPath, degreeMap.get(resolvedPath) + backlinks.length);
|
|
11463
|
+
}
|
|
11464
|
+
}
|
|
11465
|
+
let totalDegree = 0;
|
|
11466
|
+
let maxDegree = 0;
|
|
11467
|
+
let maxDegreeNote = "";
|
|
11468
|
+
for (const [notePath, degree] of degreeMap) {
|
|
11469
|
+
totalDegree += degree;
|
|
11470
|
+
if (degree > maxDegree) {
|
|
11471
|
+
maxDegree = degree;
|
|
11472
|
+
maxDegreeNote = notePath;
|
|
11473
|
+
}
|
|
11474
|
+
}
|
|
11475
|
+
const avgDegree = noteCount > 0 ? Math.round(totalDegree / noteCount * 100) / 100 : 0;
|
|
11476
|
+
const visited = /* @__PURE__ */ new Set();
|
|
11477
|
+
const clusters = [];
|
|
11478
|
+
for (const notePath of index.notes.keys()) {
|
|
11479
|
+
if (visited.has(notePath)) continue;
|
|
11480
|
+
const queue = [notePath];
|
|
11481
|
+
visited.add(notePath);
|
|
11482
|
+
let clusterSize = 0;
|
|
11483
|
+
while (queue.length > 0) {
|
|
11484
|
+
const current = queue.shift();
|
|
11485
|
+
clusterSize++;
|
|
11486
|
+
const neighbors = adjacency.get(current);
|
|
11487
|
+
if (neighbors) {
|
|
11488
|
+
for (const neighbor of neighbors) {
|
|
11489
|
+
if (!visited.has(neighbor)) {
|
|
11490
|
+
visited.add(neighbor);
|
|
11491
|
+
queue.push(neighbor);
|
|
11492
|
+
}
|
|
11493
|
+
}
|
|
11494
|
+
}
|
|
11495
|
+
}
|
|
11496
|
+
clusters.push(clusterSize);
|
|
11497
|
+
}
|
|
11498
|
+
const clusterCount = clusters.length;
|
|
11499
|
+
const largestClusterSize = clusters.length > 0 ? Math.max(...clusters) : 0;
|
|
11500
|
+
const sorted = Array.from(degreeMap.entries()).sort((a, b) => b[1] - a[1]).slice(0, 10);
|
|
11501
|
+
const hubScoresTop10 = sorted.map(([notePath, degree]) => {
|
|
11502
|
+
const note = index.notes.get(notePath);
|
|
11503
|
+
return {
|
|
11504
|
+
entity: note?.title ?? notePath,
|
|
11505
|
+
degree
|
|
11506
|
+
};
|
|
11507
|
+
});
|
|
11508
|
+
return {
|
|
11509
|
+
avg_degree: avgDegree,
|
|
11510
|
+
max_degree: maxDegree,
|
|
11511
|
+
cluster_count: clusterCount,
|
|
11512
|
+
largest_cluster_size: largestClusterSize,
|
|
11513
|
+
hub_scores_top10: hubScoresTop10
|
|
11514
|
+
};
|
|
11515
|
+
}
|
|
11516
|
+
function recordGraphSnapshot(stateDb2, metrics) {
|
|
11517
|
+
const timestamp = Date.now();
|
|
11518
|
+
const insert = stateDb2.db.prepare(
|
|
11519
|
+
"INSERT INTO graph_snapshots (timestamp, metric, value, details) VALUES (?, ?, ?, ?)"
|
|
11520
|
+
);
|
|
11521
|
+
const transaction = stateDb2.db.transaction(() => {
|
|
11522
|
+
insert.run(timestamp, "avg_degree", metrics.avg_degree, null);
|
|
11523
|
+
insert.run(timestamp, "max_degree", metrics.max_degree, null);
|
|
11524
|
+
insert.run(timestamp, "cluster_count", metrics.cluster_count, null);
|
|
11525
|
+
insert.run(timestamp, "largest_cluster_size", metrics.largest_cluster_size, null);
|
|
11526
|
+
insert.run(
|
|
11527
|
+
timestamp,
|
|
11528
|
+
"hub_scores_top10",
|
|
11529
|
+
metrics.hub_scores_top10.length,
|
|
11530
|
+
JSON.stringify(metrics.hub_scores_top10)
|
|
11531
|
+
);
|
|
11532
|
+
});
|
|
11533
|
+
transaction();
|
|
11534
|
+
}
|
|
11535
|
+
function getEmergingHubs(stateDb2, daysBack = 30) {
|
|
11536
|
+
const cutoff = Date.now() - daysBack * 24 * 60 * 60 * 1e3;
|
|
11537
|
+
const latestRow = stateDb2.db.prepare(
|
|
11538
|
+
`SELECT details FROM graph_snapshots
|
|
11539
|
+
WHERE metric = 'hub_scores_top10'
|
|
11540
|
+
ORDER BY timestamp DESC LIMIT 1`
|
|
11541
|
+
).get();
|
|
11542
|
+
const previousRow = stateDb2.db.prepare(
|
|
11543
|
+
`SELECT details FROM graph_snapshots
|
|
11544
|
+
WHERE metric = 'hub_scores_top10' AND timestamp >= ?
|
|
11545
|
+
ORDER BY timestamp ASC LIMIT 1`
|
|
11546
|
+
).get(cutoff);
|
|
11547
|
+
if (!latestRow?.details) return [];
|
|
11548
|
+
const currentHubs = JSON.parse(latestRow.details);
|
|
11549
|
+
const previousHubs = previousRow?.details ? JSON.parse(previousRow.details) : [];
|
|
11550
|
+
const previousMap = /* @__PURE__ */ new Map();
|
|
11551
|
+
for (const hub of previousHubs) {
|
|
11552
|
+
previousMap.set(hub.entity, hub.degree);
|
|
11553
|
+
}
|
|
11554
|
+
const emerging = currentHubs.map((hub) => {
|
|
11555
|
+
const prevDegree = previousMap.get(hub.entity) ?? 0;
|
|
11556
|
+
return {
|
|
11557
|
+
entity: hub.entity,
|
|
11558
|
+
current_degree: hub.degree,
|
|
11559
|
+
previous_degree: prevDegree,
|
|
11560
|
+
growth: hub.degree - prevDegree
|
|
11561
|
+
};
|
|
11562
|
+
});
|
|
11563
|
+
emerging.sort((a, b) => b.growth - a.growth);
|
|
11564
|
+
return emerging;
|
|
11565
|
+
}
|
|
11566
|
+
function compareGraphSnapshots(stateDb2, timestampBefore, timestampAfter) {
|
|
11567
|
+
const SCALAR_METRICS = ["avg_degree", "max_degree", "cluster_count", "largest_cluster_size"];
|
|
11568
|
+
function getSnapshotAt(ts) {
|
|
11569
|
+
const row = stateDb2.db.prepare(
|
|
11570
|
+
`SELECT DISTINCT timestamp FROM graph_snapshots WHERE timestamp <= ? ORDER BY timestamp DESC LIMIT 1`
|
|
11571
|
+
).get(ts);
|
|
11572
|
+
if (!row) return null;
|
|
11573
|
+
const rows = stateDb2.db.prepare(
|
|
11574
|
+
`SELECT metric, value, details FROM graph_snapshots WHERE timestamp = ?`
|
|
11575
|
+
).all(row.timestamp);
|
|
11576
|
+
return rows;
|
|
11577
|
+
}
|
|
11578
|
+
const beforeRows = getSnapshotAt(timestampBefore) ?? [];
|
|
11579
|
+
const afterRows = getSnapshotAt(timestampAfter) ?? [];
|
|
11580
|
+
const beforeMap = /* @__PURE__ */ new Map();
|
|
11581
|
+
const afterMap = /* @__PURE__ */ new Map();
|
|
11582
|
+
for (const r of beforeRows) beforeMap.set(r.metric, { value: r.value, details: r.details });
|
|
11583
|
+
for (const r of afterRows) afterMap.set(r.metric, { value: r.value, details: r.details });
|
|
11584
|
+
const metricChanges = SCALAR_METRICS.map((metric) => {
|
|
11585
|
+
const before = beforeMap.get(metric)?.value ?? 0;
|
|
11586
|
+
const after = afterMap.get(metric)?.value ?? 0;
|
|
11587
|
+
const delta = after - before;
|
|
11588
|
+
const deltaPercent = before !== 0 ? Math.round(delta / before * 1e4) / 100 : delta !== 0 ? 100 : 0;
|
|
11589
|
+
return { metric, before, after, delta, deltaPercent };
|
|
11590
|
+
});
|
|
11591
|
+
const beforeHubs = beforeMap.get("hub_scores_top10")?.details ? JSON.parse(beforeMap.get("hub_scores_top10").details) : [];
|
|
11592
|
+
const afterHubs = afterMap.get("hub_scores_top10")?.details ? JSON.parse(afterMap.get("hub_scores_top10").details) : [];
|
|
11593
|
+
const beforeHubMap = /* @__PURE__ */ new Map();
|
|
11594
|
+
for (const h of beforeHubs) beforeHubMap.set(h.entity, h.degree);
|
|
11595
|
+
const afterHubMap = /* @__PURE__ */ new Map();
|
|
11596
|
+
for (const h of afterHubs) afterHubMap.set(h.entity, h.degree);
|
|
11597
|
+
const allHubEntities = /* @__PURE__ */ new Set([...beforeHubMap.keys(), ...afterHubMap.keys()]);
|
|
11598
|
+
const hubScoreChanges = [];
|
|
11599
|
+
for (const entity of allHubEntities) {
|
|
11600
|
+
const before = beforeHubMap.get(entity) ?? 0;
|
|
11601
|
+
const after = afterHubMap.get(entity) ?? 0;
|
|
11602
|
+
if (before !== after) {
|
|
11603
|
+
hubScoreChanges.push({ entity, before, after, delta: after - before });
|
|
11604
|
+
}
|
|
11605
|
+
}
|
|
11606
|
+
hubScoreChanges.sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta));
|
|
11607
|
+
return { metricChanges, hubScoreChanges };
|
|
11608
|
+
}
|
|
11609
|
+
function purgeOldSnapshots(stateDb2, retentionDays = 90) {
|
|
11610
|
+
const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
11611
|
+
const result = stateDb2.db.prepare(
|
|
11612
|
+
"DELETE FROM graph_snapshots WHERE timestamp < ?"
|
|
11613
|
+
).run(cutoff);
|
|
11614
|
+
return result.changes;
|
|
11615
|
+
}
|
|
11616
|
+
|
|
11617
|
+
// src/core/read/watch/maintenance.ts
|
|
11618
|
+
var DEFAULT_INTERVAL_MS = 2 * 60 * 60 * 1e3;
|
|
11619
|
+
var MIN_INTERVAL_MS = 10 * 60 * 1e3;
|
|
11620
|
+
var JITTER_FACTOR = 0.15;
|
|
11621
|
+
var RECENT_REBUILD_THRESHOLD_MS = 60 * 60 * 1e3;
|
|
11622
|
+
var IDLE_THRESHOLD_MS = 30 * 1e3;
|
|
11623
|
+
var STEP_TTLS = {
|
|
11624
|
+
entity_scan: 5 * 60 * 1e3,
|
|
11625
|
+
// 5 minutes
|
|
11626
|
+
hub_scores: 5 * 60 * 1e3,
|
|
11627
|
+
// 5 minutes
|
|
11628
|
+
recency: 60 * 60 * 1e3,
|
|
11629
|
+
// 1 hour
|
|
11630
|
+
cooccurrence: 60 * 60 * 1e3,
|
|
11631
|
+
// 1 hour
|
|
11632
|
+
edge_weights: 60 * 60 * 1e3,
|
|
11633
|
+
// 1 hour
|
|
11634
|
+
config_inference: 2 * 60 * 60 * 1e3
|
|
11635
|
+
// 2 hours (only runs during maintenance)
|
|
11636
|
+
};
|
|
11637
|
+
var timer = null;
|
|
11638
|
+
var config = null;
|
|
11639
|
+
var lastConfigInferenceAt = 0;
|
|
11640
|
+
function addJitter(interval) {
|
|
11641
|
+
const jitter = interval * JITTER_FACTOR * (2 * Math.random() - 1);
|
|
11642
|
+
return Math.max(MIN_INTERVAL_MS, interval + jitter);
|
|
11643
|
+
}
|
|
11644
|
+
function startMaintenanceTimer(cfg, intervalMs) {
|
|
11645
|
+
config = cfg;
|
|
11646
|
+
const baseInterval = Math.max(intervalMs ?? DEFAULT_INTERVAL_MS, MIN_INTERVAL_MS);
|
|
11647
|
+
scheduleNext(baseInterval);
|
|
11648
|
+
serverLog("maintenance", `Timer started (interval ~${Math.round(baseInterval / 6e4)}min)`);
|
|
11649
|
+
}
|
|
11650
|
+
function stopMaintenanceTimer() {
|
|
11651
|
+
if (timer) {
|
|
11652
|
+
clearTimeout(timer);
|
|
11653
|
+
timer = null;
|
|
11654
|
+
}
|
|
11655
|
+
config = null;
|
|
11656
|
+
}
|
|
11657
|
+
function scheduleNext(baseInterval) {
|
|
11658
|
+
timer = setTimeout(() => {
|
|
11659
|
+
runMaintenance(baseInterval);
|
|
11660
|
+
}, addJitter(baseInterval));
|
|
11661
|
+
timer.unref();
|
|
11662
|
+
}
|
|
11663
|
+
async function runMaintenance(baseInterval) {
|
|
11664
|
+
const cfg = config;
|
|
11665
|
+
if (!cfg) return;
|
|
11666
|
+
const { ctx, sd } = cfg;
|
|
11667
|
+
if (ctx.pipelineActivity.busy) {
|
|
11668
|
+
serverLog("maintenance", "Skipped: pipeline busy");
|
|
11669
|
+
scheduleNext(baseInterval);
|
|
11670
|
+
return;
|
|
11671
|
+
}
|
|
11672
|
+
const lastFullRebuild = cfg.getLastFullRebuildAt();
|
|
11673
|
+
if (lastFullRebuild > 0 && Date.now() - lastFullRebuild < RECENT_REBUILD_THRESHOLD_MS) {
|
|
11674
|
+
serverLog("maintenance", `Skipped: full rebuild ${Math.round((Date.now() - lastFullRebuild) / 6e4)}min ago`);
|
|
11675
|
+
scheduleNext(baseInterval);
|
|
11676
|
+
return;
|
|
11677
|
+
}
|
|
11678
|
+
const lastRequest = cfg.getLastMcpRequestAt();
|
|
11679
|
+
if (lastRequest > 0 && Date.now() - lastRequest < IDLE_THRESHOLD_MS) {
|
|
11680
|
+
serverLog("maintenance", "Skipped: server not idle, retrying in 1min");
|
|
11681
|
+
timer = setTimeout(() => runMaintenance(baseInterval), 60 * 1e3);
|
|
11682
|
+
timer.unref();
|
|
11683
|
+
return;
|
|
11684
|
+
}
|
|
11685
|
+
const start = Date.now();
|
|
11686
|
+
const stepsRun = [];
|
|
11687
|
+
const tracker = createStepTracker();
|
|
11688
|
+
try {
|
|
11689
|
+
const now = Date.now();
|
|
11690
|
+
const entityAge = ctx.lastEntityScanAt > 0 ? now - ctx.lastEntityScanAt : Infinity;
|
|
11691
|
+
if (entityAge >= STEP_TTLS.entity_scan) {
|
|
11692
|
+
tracker.start("entity_scan", {});
|
|
11693
|
+
await cfg.updateEntitiesInStateDb(cfg.vp, sd);
|
|
11694
|
+
ctx.lastEntityScanAt = Date.now();
|
|
11695
|
+
const entities = sd ? getAllEntitiesFromDb2(sd) : [];
|
|
11696
|
+
tracker.end({ entity_count: entities.length });
|
|
11697
|
+
stepsRun.push("entity_scan");
|
|
11698
|
+
}
|
|
11699
|
+
const hubAge = ctx.lastHubScoreRebuildAt > 0 ? now - ctx.lastHubScoreRebuildAt : Infinity;
|
|
11700
|
+
if (hubAge >= STEP_TTLS.hub_scores) {
|
|
11701
|
+
tracker.start("hub_scores", {});
|
|
11702
|
+
const updated = await exportHubScores(cfg.getVaultIndex(), sd);
|
|
11703
|
+
ctx.lastHubScoreRebuildAt = Date.now();
|
|
11704
|
+
tracker.end({ updated: updated ?? 0 });
|
|
11705
|
+
stepsRun.push("hub_scores");
|
|
11706
|
+
}
|
|
11707
|
+
const cachedRecency = loadRecencyFromStateDb(sd ?? void 0);
|
|
11708
|
+
const recencyAge = cachedRecency ? now - (cachedRecency.lastUpdated ?? 0) : Infinity;
|
|
11709
|
+
if (recencyAge >= STEP_TTLS.recency) {
|
|
11710
|
+
tracker.start("recency", {});
|
|
11711
|
+
const entities = sd ? getAllEntitiesFromDb2(sd) : [];
|
|
11712
|
+
const entityInput = entities.map((e) => ({ name: e.name, path: e.path, aliases: e.aliases }));
|
|
11713
|
+
const recencyIndex2 = await buildRecencyIndex(cfg.vp, entityInput);
|
|
11714
|
+
saveRecencyToStateDb(recencyIndex2, sd ?? void 0);
|
|
11715
|
+
tracker.end({ entities: recencyIndex2.lastMentioned.size });
|
|
11716
|
+
stepsRun.push("recency");
|
|
11717
|
+
}
|
|
11718
|
+
const cooccurrenceAge = ctx.lastCooccurrenceRebuildAt > 0 ? now - ctx.lastCooccurrenceRebuildAt : Infinity;
|
|
11719
|
+
if (cooccurrenceAge >= STEP_TTLS.cooccurrence) {
|
|
11720
|
+
tracker.start("cooccurrence", {});
|
|
11721
|
+
const entities = sd ? getAllEntitiesFromDb2(sd) : [];
|
|
11722
|
+
const entityNames = entities.map((e) => e.name);
|
|
11723
|
+
const cooccurrenceIdx = await mineCooccurrences(cfg.vp, entityNames);
|
|
11724
|
+
setCooccurrenceIndex(cooccurrenceIdx);
|
|
11725
|
+
ctx.lastCooccurrenceRebuildAt = Date.now();
|
|
11726
|
+
ctx.cooccurrenceIndex = cooccurrenceIdx;
|
|
11727
|
+
if (sd) saveCooccurrenceToStateDb(sd, cooccurrenceIdx);
|
|
11728
|
+
tracker.end({ associations: cooccurrenceIdx._metadata.total_associations });
|
|
11729
|
+
stepsRun.push("cooccurrence");
|
|
11730
|
+
}
|
|
11731
|
+
const edgeWeightAge = ctx.lastEdgeWeightRebuildAt > 0 ? now - ctx.lastEdgeWeightRebuildAt : Infinity;
|
|
11732
|
+
if (sd && edgeWeightAge >= STEP_TTLS.edge_weights) {
|
|
11733
|
+
tracker.start("edge_weights", {});
|
|
11734
|
+
const result = recomputeEdgeWeights(sd);
|
|
11735
|
+
ctx.lastEdgeWeightRebuildAt = Date.now();
|
|
11736
|
+
tracker.end({ edges: result.edges_updated });
|
|
11737
|
+
stepsRun.push("edge_weights");
|
|
11738
|
+
}
|
|
11739
|
+
const configAge = lastConfigInferenceAt > 0 ? now - lastConfigInferenceAt : Infinity;
|
|
11740
|
+
if (sd && configAge >= STEP_TTLS.config_inference) {
|
|
11741
|
+
tracker.start("config_inference", {});
|
|
11742
|
+
const existing = loadConfig(sd);
|
|
11743
|
+
const inferred = inferConfig(cfg.getVaultIndex(), cfg.vp);
|
|
11744
|
+
saveConfig(sd, inferred, existing);
|
|
11745
|
+
cfg.updateFlywheelConfig(loadConfig(sd));
|
|
11746
|
+
lastConfigInferenceAt = Date.now();
|
|
11747
|
+
tracker.end({ inferred: true });
|
|
11748
|
+
stepsRun.push("config_inference");
|
|
11749
|
+
}
|
|
11750
|
+
if (sd && stepsRun.length > 0) {
|
|
11751
|
+
try {
|
|
11752
|
+
tracker.start("graph_snapshot", {});
|
|
11753
|
+
const graphMetrics = computeGraphMetrics(cfg.getVaultIndex());
|
|
11754
|
+
recordGraphSnapshot(sd, graphMetrics);
|
|
11755
|
+
tracker.end({ recorded: true });
|
|
11756
|
+
stepsRun.push("graph_snapshot");
|
|
11757
|
+
} catch (err) {
|
|
11758
|
+
tracker.end({ error: String(err) });
|
|
11759
|
+
}
|
|
11760
|
+
}
|
|
11761
|
+
if (sd && stepsRun.length > 0) {
|
|
11762
|
+
try {
|
|
11763
|
+
saveVaultIndexToCache(sd, cfg.getVaultIndex());
|
|
11764
|
+
ctx.lastIndexCacheSaveAt = Date.now();
|
|
11765
|
+
} catch {
|
|
11766
|
+
}
|
|
11767
|
+
}
|
|
11768
|
+
const duration = Date.now() - start;
|
|
11769
|
+
if (stepsRun.length > 0) {
|
|
11770
|
+
serverLog("maintenance", `Completed ${stepsRun.length} steps in ${duration}ms: ${stepsRun.join(", ")}`);
|
|
11771
|
+
if (sd) {
|
|
11772
|
+
recordIndexEvent(sd, {
|
|
11773
|
+
trigger: "maintenance",
|
|
11774
|
+
duration_ms: duration,
|
|
11775
|
+
note_count: cfg.getVaultIndex().notes.size,
|
|
11776
|
+
steps: tracker.steps
|
|
11777
|
+
});
|
|
11778
|
+
}
|
|
11779
|
+
} else {
|
|
11780
|
+
serverLog("maintenance", `All steps fresh, nothing to do (${duration}ms)`);
|
|
11781
|
+
}
|
|
11782
|
+
} catch (err) {
|
|
11783
|
+
const duration = Date.now() - start;
|
|
11784
|
+
serverLog("maintenance", `Failed after ${duration}ms: ${err instanceof Error ? err.message : err}`, "error");
|
|
11785
|
+
if (sd) {
|
|
11786
|
+
recordIndexEvent(sd, {
|
|
11787
|
+
trigger: "maintenance",
|
|
11788
|
+
duration_ms: duration,
|
|
11789
|
+
success: false,
|
|
11790
|
+
error: err instanceof Error ? err.message : String(err),
|
|
11791
|
+
steps: tracker.steps
|
|
11792
|
+
});
|
|
11793
|
+
}
|
|
11794
|
+
}
|
|
11795
|
+
scheduleNext(baseInterval);
|
|
11796
|
+
}
|
|
11797
|
+
|
|
11798
|
+
// src/core/shared/metrics.ts
|
|
11799
|
+
init_wikilinkFeedback();
|
|
11800
|
+
var ALL_METRICS = [
|
|
11801
|
+
"note_count",
|
|
11802
|
+
"link_count",
|
|
11803
|
+
"orphan_count",
|
|
11804
|
+
"tag_count",
|
|
11805
|
+
"entity_count",
|
|
11806
|
+
"avg_links_per_note",
|
|
11807
|
+
"link_density",
|
|
11808
|
+
"connected_ratio",
|
|
11809
|
+
"wikilink_accuracy",
|
|
11810
|
+
"wikilink_feedback_volume",
|
|
11811
|
+
"wikilink_suppressed_count"
|
|
11812
|
+
];
|
|
11813
|
+
function computeMetrics(index, stateDb2) {
|
|
11814
|
+
const noteCount = index.notes.size;
|
|
11815
|
+
let linkCount = 0;
|
|
11816
|
+
for (const note of index.notes.values()) {
|
|
11817
|
+
linkCount += note.outlinks.length;
|
|
11818
|
+
}
|
|
11819
|
+
const connectedNotes = /* @__PURE__ */ new Set();
|
|
11820
|
+
for (const [notePath, note] of index.notes) {
|
|
11821
|
+
if (note.outlinks.length > 0) {
|
|
11822
|
+
connectedNotes.add(notePath);
|
|
11823
|
+
}
|
|
11824
|
+
}
|
|
11825
|
+
for (const [target, backlinks] of index.backlinks) {
|
|
11826
|
+
for (const bl of backlinks) {
|
|
11827
|
+
connectedNotes.add(bl.source);
|
|
11828
|
+
}
|
|
11263
11829
|
const targetPath = index.entities.get(target);
|
|
11264
11830
|
if (targetPath && index.notes.has(targetPath)) {
|
|
11265
11831
|
connectedNotes.add(targetPath);
|
|
@@ -11508,8 +12074,8 @@ function getNoteAccessFrequency(stateDb2, daysBack = 30) {
|
|
|
11508
12074
|
}
|
|
11509
12075
|
}
|
|
11510
12076
|
}
|
|
11511
|
-
return Array.from(noteMap.entries()).map(([
|
|
11512
|
-
path:
|
|
12077
|
+
return Array.from(noteMap.entries()).map(([path40, stats]) => ({
|
|
12078
|
+
path: path40,
|
|
11513
12079
|
access_count: stats.access_count,
|
|
11514
12080
|
last_accessed: stats.last_accessed,
|
|
11515
12081
|
tools_used: Array.from(stats.tools)
|
|
@@ -11569,237 +12135,50 @@ function getSessionHistory(stateDb2, sessionId) {
|
|
|
11569
12135
|
function getSessionDetail(stateDb2, sessionId, options = {}) {
|
|
11570
12136
|
const { include_children = true, limit = 50 } = options;
|
|
11571
12137
|
const rows = include_children ? stateDb2.db.prepare(`
|
|
11572
|
-
SELECT * FROM tool_invocations
|
|
11573
|
-
WHERE session_id = ? OR session_id LIKE ?
|
|
11574
|
-
ORDER BY timestamp
|
|
11575
|
-
LIMIT ?
|
|
11576
|
-
`).all(sessionId, `${sessionId}.%`, limit) : stateDb2.db.prepare(`
|
|
11577
|
-
SELECT * FROM tool_invocations
|
|
11578
|
-
WHERE session_id = ?
|
|
11579
|
-
ORDER BY timestamp
|
|
11580
|
-
LIMIT ?
|
|
11581
|
-
`).all(sessionId, limit);
|
|
11582
|
-
if (rows.length === 0) return null;
|
|
11583
|
-
const tools = /* @__PURE__ */ new Set();
|
|
11584
|
-
const notes = /* @__PURE__ */ new Set();
|
|
11585
|
-
for (const row of rows) {
|
|
11586
|
-
tools.add(row.tool_name);
|
|
11587
|
-
if (row.note_paths) {
|
|
11588
|
-
try {
|
|
11589
|
-
for (const p of JSON.parse(row.note_paths)) notes.add(p);
|
|
11590
|
-
} catch {
|
|
11591
|
-
}
|
|
11592
|
-
}
|
|
11593
|
-
}
|
|
11594
|
-
return {
|
|
11595
|
-
summary: {
|
|
11596
|
-
session_id: sessionId,
|
|
11597
|
-
started_at: rows[0].timestamp,
|
|
11598
|
-
last_activity: rows[rows.length - 1].timestamp,
|
|
11599
|
-
tool_count: rows.length,
|
|
11600
|
-
unique_tools: Array.from(tools),
|
|
11601
|
-
notes_accessed: Array.from(notes)
|
|
11602
|
-
},
|
|
11603
|
-
invocations: rows.map(rowToInvocation)
|
|
11604
|
-
};
|
|
11605
|
-
}
|
|
11606
|
-
function getRecentInvocations(stateDb2, limit = 20) {
|
|
11607
|
-
const rows = stateDb2.db.prepare(
|
|
11608
|
-
"SELECT * FROM tool_invocations ORDER BY timestamp DESC LIMIT ?"
|
|
11609
|
-
).all(limit);
|
|
11610
|
-
return rows.map(rowToInvocation);
|
|
11611
|
-
}
|
|
11612
|
-
function purgeOldInvocations(stateDb2, retentionDays = 90) {
|
|
11613
|
-
const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
11614
|
-
const result = stateDb2.db.prepare(
|
|
11615
|
-
"DELETE FROM tool_invocations WHERE timestamp < ?"
|
|
11616
|
-
).run(cutoff);
|
|
11617
|
-
return result.changes;
|
|
11618
|
-
}
|
|
11619
|
-
|
|
11620
|
-
// src/core/shared/graphSnapshots.ts
|
|
11621
|
-
function computeGraphMetrics(index) {
|
|
11622
|
-
const noteCount = index.notes.size;
|
|
11623
|
-
if (noteCount === 0) {
|
|
11624
|
-
return {
|
|
11625
|
-
avg_degree: 0,
|
|
11626
|
-
max_degree: 0,
|
|
11627
|
-
cluster_count: 0,
|
|
11628
|
-
largest_cluster_size: 0,
|
|
11629
|
-
hub_scores_top10: []
|
|
11630
|
-
};
|
|
11631
|
-
}
|
|
11632
|
-
const degreeMap = /* @__PURE__ */ new Map();
|
|
11633
|
-
const adjacency = /* @__PURE__ */ new Map();
|
|
11634
|
-
for (const [notePath, note] of index.notes) {
|
|
11635
|
-
if (!adjacency.has(notePath)) adjacency.set(notePath, /* @__PURE__ */ new Set());
|
|
11636
|
-
let degree = note.outlinks.length;
|
|
11637
|
-
for (const link of note.outlinks) {
|
|
11638
|
-
const targetLower = link.target.toLowerCase();
|
|
11639
|
-
const resolvedPath = index.entities.get(targetLower);
|
|
11640
|
-
if (resolvedPath && index.notes.has(resolvedPath)) {
|
|
11641
|
-
adjacency.get(notePath).add(resolvedPath);
|
|
11642
|
-
if (!adjacency.has(resolvedPath)) adjacency.set(resolvedPath, /* @__PURE__ */ new Set());
|
|
11643
|
-
adjacency.get(resolvedPath).add(notePath);
|
|
11644
|
-
}
|
|
11645
|
-
}
|
|
11646
|
-
degreeMap.set(notePath, degree);
|
|
11647
|
-
}
|
|
11648
|
-
for (const [target, backlinks] of index.backlinks) {
|
|
11649
|
-
const targetLower = target.toLowerCase();
|
|
11650
|
-
const resolvedPath = index.entities.get(targetLower);
|
|
11651
|
-
if (resolvedPath && degreeMap.has(resolvedPath)) {
|
|
11652
|
-
degreeMap.set(resolvedPath, degreeMap.get(resolvedPath) + backlinks.length);
|
|
11653
|
-
}
|
|
11654
|
-
}
|
|
11655
|
-
let totalDegree = 0;
|
|
11656
|
-
let maxDegree = 0;
|
|
11657
|
-
let maxDegreeNote = "";
|
|
11658
|
-
for (const [notePath, degree] of degreeMap) {
|
|
11659
|
-
totalDegree += degree;
|
|
11660
|
-
if (degree > maxDegree) {
|
|
11661
|
-
maxDegree = degree;
|
|
11662
|
-
maxDegreeNote = notePath;
|
|
11663
|
-
}
|
|
11664
|
-
}
|
|
11665
|
-
const avgDegree = noteCount > 0 ? Math.round(totalDegree / noteCount * 100) / 100 : 0;
|
|
11666
|
-
const visited = /* @__PURE__ */ new Set();
|
|
11667
|
-
const clusters = [];
|
|
11668
|
-
for (const notePath of index.notes.keys()) {
|
|
11669
|
-
if (visited.has(notePath)) continue;
|
|
11670
|
-
const queue = [notePath];
|
|
11671
|
-
visited.add(notePath);
|
|
11672
|
-
let clusterSize = 0;
|
|
11673
|
-
while (queue.length > 0) {
|
|
11674
|
-
const current = queue.shift();
|
|
11675
|
-
clusterSize++;
|
|
11676
|
-
const neighbors = adjacency.get(current);
|
|
11677
|
-
if (neighbors) {
|
|
11678
|
-
for (const neighbor of neighbors) {
|
|
11679
|
-
if (!visited.has(neighbor)) {
|
|
11680
|
-
visited.add(neighbor);
|
|
11681
|
-
queue.push(neighbor);
|
|
11682
|
-
}
|
|
11683
|
-
}
|
|
12138
|
+
SELECT * FROM tool_invocations
|
|
12139
|
+
WHERE session_id = ? OR session_id LIKE ?
|
|
12140
|
+
ORDER BY timestamp
|
|
12141
|
+
LIMIT ?
|
|
12142
|
+
`).all(sessionId, `${sessionId}.%`, limit) : stateDb2.db.prepare(`
|
|
12143
|
+
SELECT * FROM tool_invocations
|
|
12144
|
+
WHERE session_id = ?
|
|
12145
|
+
ORDER BY timestamp
|
|
12146
|
+
LIMIT ?
|
|
12147
|
+
`).all(sessionId, limit);
|
|
12148
|
+
if (rows.length === 0) return null;
|
|
12149
|
+
const tools = /* @__PURE__ */ new Set();
|
|
12150
|
+
const notes = /* @__PURE__ */ new Set();
|
|
12151
|
+
for (const row of rows) {
|
|
12152
|
+
tools.add(row.tool_name);
|
|
12153
|
+
if (row.note_paths) {
|
|
12154
|
+
try {
|
|
12155
|
+
for (const p of JSON.parse(row.note_paths)) notes.add(p);
|
|
12156
|
+
} catch {
|
|
11684
12157
|
}
|
|
11685
12158
|
}
|
|
11686
|
-
clusters.push(clusterSize);
|
|
11687
12159
|
}
|
|
11688
|
-
const clusterCount = clusters.length;
|
|
11689
|
-
const largestClusterSize = clusters.length > 0 ? Math.max(...clusters) : 0;
|
|
11690
|
-
const sorted = Array.from(degreeMap.entries()).sort((a, b) => b[1] - a[1]).slice(0, 10);
|
|
11691
|
-
const hubScoresTop10 = sorted.map(([notePath, degree]) => {
|
|
11692
|
-
const note = index.notes.get(notePath);
|
|
11693
|
-
return {
|
|
11694
|
-
entity: note?.title ?? notePath,
|
|
11695
|
-
degree
|
|
11696
|
-
};
|
|
11697
|
-
});
|
|
11698
12160
|
return {
|
|
11699
|
-
|
|
11700
|
-
|
|
11701
|
-
|
|
11702
|
-
|
|
11703
|
-
|
|
12161
|
+
summary: {
|
|
12162
|
+
session_id: sessionId,
|
|
12163
|
+
started_at: rows[0].timestamp,
|
|
12164
|
+
last_activity: rows[rows.length - 1].timestamp,
|
|
12165
|
+
tool_count: rows.length,
|
|
12166
|
+
unique_tools: Array.from(tools),
|
|
12167
|
+
notes_accessed: Array.from(notes)
|
|
12168
|
+
},
|
|
12169
|
+
invocations: rows.map(rowToInvocation)
|
|
11704
12170
|
};
|
|
11705
12171
|
}
|
|
11706
|
-
function
|
|
11707
|
-
const
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
);
|
|
11711
|
-
const transaction = stateDb2.db.transaction(() => {
|
|
11712
|
-
insert.run(timestamp, "avg_degree", metrics.avg_degree, null);
|
|
11713
|
-
insert.run(timestamp, "max_degree", metrics.max_degree, null);
|
|
11714
|
-
insert.run(timestamp, "cluster_count", metrics.cluster_count, null);
|
|
11715
|
-
insert.run(timestamp, "largest_cluster_size", metrics.largest_cluster_size, null);
|
|
11716
|
-
insert.run(
|
|
11717
|
-
timestamp,
|
|
11718
|
-
"hub_scores_top10",
|
|
11719
|
-
metrics.hub_scores_top10.length,
|
|
11720
|
-
JSON.stringify(metrics.hub_scores_top10)
|
|
11721
|
-
);
|
|
11722
|
-
});
|
|
11723
|
-
transaction();
|
|
11724
|
-
}
|
|
11725
|
-
function getEmergingHubs(stateDb2, daysBack = 30) {
|
|
11726
|
-
const cutoff = Date.now() - daysBack * 24 * 60 * 60 * 1e3;
|
|
11727
|
-
const latestRow = stateDb2.db.prepare(
|
|
11728
|
-
`SELECT details FROM graph_snapshots
|
|
11729
|
-
WHERE metric = 'hub_scores_top10'
|
|
11730
|
-
ORDER BY timestamp DESC LIMIT 1`
|
|
11731
|
-
).get();
|
|
11732
|
-
const previousRow = stateDb2.db.prepare(
|
|
11733
|
-
`SELECT details FROM graph_snapshots
|
|
11734
|
-
WHERE metric = 'hub_scores_top10' AND timestamp >= ?
|
|
11735
|
-
ORDER BY timestamp ASC LIMIT 1`
|
|
11736
|
-
).get(cutoff);
|
|
11737
|
-
if (!latestRow?.details) return [];
|
|
11738
|
-
const currentHubs = JSON.parse(latestRow.details);
|
|
11739
|
-
const previousHubs = previousRow?.details ? JSON.parse(previousRow.details) : [];
|
|
11740
|
-
const previousMap = /* @__PURE__ */ new Map();
|
|
11741
|
-
for (const hub of previousHubs) {
|
|
11742
|
-
previousMap.set(hub.entity, hub.degree);
|
|
11743
|
-
}
|
|
11744
|
-
const emerging = currentHubs.map((hub) => {
|
|
11745
|
-
const prevDegree = previousMap.get(hub.entity) ?? 0;
|
|
11746
|
-
return {
|
|
11747
|
-
entity: hub.entity,
|
|
11748
|
-
current_degree: hub.degree,
|
|
11749
|
-
previous_degree: prevDegree,
|
|
11750
|
-
growth: hub.degree - prevDegree
|
|
11751
|
-
};
|
|
11752
|
-
});
|
|
11753
|
-
emerging.sort((a, b) => b.growth - a.growth);
|
|
11754
|
-
return emerging;
|
|
11755
|
-
}
|
|
11756
|
-
function compareGraphSnapshots(stateDb2, timestampBefore, timestampAfter) {
|
|
11757
|
-
const SCALAR_METRICS = ["avg_degree", "max_degree", "cluster_count", "largest_cluster_size"];
|
|
11758
|
-
function getSnapshotAt(ts) {
|
|
11759
|
-
const row = stateDb2.db.prepare(
|
|
11760
|
-
`SELECT DISTINCT timestamp FROM graph_snapshots WHERE timestamp <= ? ORDER BY timestamp DESC LIMIT 1`
|
|
11761
|
-
).get(ts);
|
|
11762
|
-
if (!row) return null;
|
|
11763
|
-
const rows = stateDb2.db.prepare(
|
|
11764
|
-
`SELECT metric, value, details FROM graph_snapshots WHERE timestamp = ?`
|
|
11765
|
-
).all(row.timestamp);
|
|
11766
|
-
return rows;
|
|
11767
|
-
}
|
|
11768
|
-
const beforeRows = getSnapshotAt(timestampBefore) ?? [];
|
|
11769
|
-
const afterRows = getSnapshotAt(timestampAfter) ?? [];
|
|
11770
|
-
const beforeMap = /* @__PURE__ */ new Map();
|
|
11771
|
-
const afterMap = /* @__PURE__ */ new Map();
|
|
11772
|
-
for (const r of beforeRows) beforeMap.set(r.metric, { value: r.value, details: r.details });
|
|
11773
|
-
for (const r of afterRows) afterMap.set(r.metric, { value: r.value, details: r.details });
|
|
11774
|
-
const metricChanges = SCALAR_METRICS.map((metric) => {
|
|
11775
|
-
const before = beforeMap.get(metric)?.value ?? 0;
|
|
11776
|
-
const after = afterMap.get(metric)?.value ?? 0;
|
|
11777
|
-
const delta = after - before;
|
|
11778
|
-
const deltaPercent = before !== 0 ? Math.round(delta / before * 1e4) / 100 : delta !== 0 ? 100 : 0;
|
|
11779
|
-
return { metric, before, after, delta, deltaPercent };
|
|
11780
|
-
});
|
|
11781
|
-
const beforeHubs = beforeMap.get("hub_scores_top10")?.details ? JSON.parse(beforeMap.get("hub_scores_top10").details) : [];
|
|
11782
|
-
const afterHubs = afterMap.get("hub_scores_top10")?.details ? JSON.parse(afterMap.get("hub_scores_top10").details) : [];
|
|
11783
|
-
const beforeHubMap = /* @__PURE__ */ new Map();
|
|
11784
|
-
for (const h of beforeHubs) beforeHubMap.set(h.entity, h.degree);
|
|
11785
|
-
const afterHubMap = /* @__PURE__ */ new Map();
|
|
11786
|
-
for (const h of afterHubs) afterHubMap.set(h.entity, h.degree);
|
|
11787
|
-
const allHubEntities = /* @__PURE__ */ new Set([...beforeHubMap.keys(), ...afterHubMap.keys()]);
|
|
11788
|
-
const hubScoreChanges = [];
|
|
11789
|
-
for (const entity of allHubEntities) {
|
|
11790
|
-
const before = beforeHubMap.get(entity) ?? 0;
|
|
11791
|
-
const after = afterHubMap.get(entity) ?? 0;
|
|
11792
|
-
if (before !== after) {
|
|
11793
|
-
hubScoreChanges.push({ entity, before, after, delta: after - before });
|
|
11794
|
-
}
|
|
11795
|
-
}
|
|
11796
|
-
hubScoreChanges.sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta));
|
|
11797
|
-
return { metricChanges, hubScoreChanges };
|
|
12172
|
+
function getRecentInvocations(stateDb2, limit = 20) {
|
|
12173
|
+
const rows = stateDb2.db.prepare(
|
|
12174
|
+
"SELECT * FROM tool_invocations ORDER BY timestamp DESC LIMIT ?"
|
|
12175
|
+
).all(limit);
|
|
12176
|
+
return rows.map(rowToInvocation);
|
|
11798
12177
|
}
|
|
11799
|
-
function
|
|
12178
|
+
function purgeOldInvocations(stateDb2, retentionDays = 90) {
|
|
11800
12179
|
const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
11801
12180
|
const result = stateDb2.db.prepare(
|
|
11802
|
-
"DELETE FROM
|
|
12181
|
+
"DELETE FROM tool_invocations WHERE timestamp < ?"
|
|
11803
12182
|
).run(cutoff);
|
|
11804
12183
|
return result.changes;
|
|
11805
12184
|
}
|
|
@@ -11811,7 +12190,7 @@ init_recency();
|
|
|
11811
12190
|
init_prospects();
|
|
11812
12191
|
init_cooccurrence();
|
|
11813
12192
|
init_retrievalCooccurrence();
|
|
11814
|
-
import * as
|
|
12193
|
+
import * as fs35 from "node:fs/promises";
|
|
11815
12194
|
import { createHash as createHash4 } from "node:crypto";
|
|
11816
12195
|
|
|
11817
12196
|
// src/vault-registry.ts
|
|
@@ -12042,7 +12421,6 @@ var TOOL_CATEGORY = {
|
|
|
12042
12421
|
get_common_neighbors: "graph",
|
|
12043
12422
|
get_backlinks: "graph",
|
|
12044
12423
|
get_forward_links: "graph",
|
|
12045
|
-
get_weighted_links: "graph",
|
|
12046
12424
|
get_strong_connections: "graph",
|
|
12047
12425
|
// schema (7 tools) -- schema intelligence + migrations
|
|
12048
12426
|
vault_schema: "schema",
|
|
@@ -12130,7 +12508,6 @@ var TOOL_TIER = {
|
|
|
12130
12508
|
get_common_neighbors: 2,
|
|
12131
12509
|
get_backlinks: 2,
|
|
12132
12510
|
get_forward_links: 2,
|
|
12133
|
-
get_weighted_links: 2,
|
|
12134
12511
|
get_strong_connections: 2,
|
|
12135
12512
|
suggest_wikilinks: 2,
|
|
12136
12513
|
validate_links: 2,
|
|
@@ -12385,10 +12762,10 @@ Use "flywheel_config" to inspect runtime configuration and set "tool_tier_overri
|
|
|
12385
12762
|
}
|
|
12386
12763
|
|
|
12387
12764
|
// src/tool-registry.ts
|
|
12388
|
-
import * as
|
|
12765
|
+
import * as path38 from "path";
|
|
12389
12766
|
import { dirname as dirname5, join as join18 } from "path";
|
|
12390
12767
|
import { statSync as statSync6, readFileSync as readFileSync5 } from "fs";
|
|
12391
|
-
import { fileURLToPath } from "url";
|
|
12768
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12392
12769
|
import { z as z39 } from "zod";
|
|
12393
12770
|
import { CallToolRequestSchema, ErrorCode, McpError } from "@modelcontextprotocol/sdk/types.js";
|
|
12394
12771
|
import { getSessionId } from "@velvetmonkey/vault-core";
|
|
@@ -12684,8 +13061,8 @@ var DEFAULT_CONFIG2 = {
|
|
|
12684
13061
|
maxOutlinksPerHop: 10,
|
|
12685
13062
|
maxBackfill: 10
|
|
12686
13063
|
};
|
|
12687
|
-
function multiHopBackfill(primaryResults, index, stateDb2,
|
|
12688
|
-
const cfg = { ...DEFAULT_CONFIG2, ...
|
|
13064
|
+
function multiHopBackfill(primaryResults, index, stateDb2, config2 = {}) {
|
|
13065
|
+
const cfg = { ...DEFAULT_CONFIG2, ...config2 };
|
|
12689
13066
|
const seen = new Set(primaryResults.map((r) => r.path).filter(Boolean));
|
|
12690
13067
|
const candidates = [];
|
|
12691
13068
|
const hop1Results = [];
|
|
@@ -12737,13 +13114,13 @@ function multiHopBackfill(primaryResults, index, stateDb2, config = {}) {
|
|
|
12737
13114
|
candidates.sort((a, b) => b.score - a.score);
|
|
12738
13115
|
return candidates.slice(0, cfg.maxBackfill).map((c) => c.result);
|
|
12739
13116
|
}
|
|
12740
|
-
function scoreCandidate(
|
|
12741
|
-
const note = index.notes.get(
|
|
13117
|
+
function scoreCandidate(path40, index, stateDb2) {
|
|
13118
|
+
const note = index.notes.get(path40);
|
|
12742
13119
|
const decay = recencyDecay(note?.modified);
|
|
12743
13120
|
let hubScore = 1;
|
|
12744
13121
|
if (stateDb2) {
|
|
12745
13122
|
try {
|
|
12746
|
-
const title = note?.title ??
|
|
13123
|
+
const title = note?.title ?? path40.replace(/\.md$/, "").split("/").pop() ?? "";
|
|
12747
13124
|
const entity = getEntityByName3(stateDb2, title);
|
|
12748
13125
|
if (entity) hubScore = entity.hubScore ?? 1;
|
|
12749
13126
|
} catch {
|
|
@@ -13203,11 +13580,11 @@ function applyEntityBridging(results, stateDb2, maxBridgesPerResult = 5) {
|
|
|
13203
13580
|
const linkMap = /* @__PURE__ */ new Map();
|
|
13204
13581
|
try {
|
|
13205
13582
|
const paths = results.map((r) => r.path).filter(Boolean);
|
|
13206
|
-
for (const
|
|
13583
|
+
for (const path40 of paths) {
|
|
13207
13584
|
const rows = stateDb2.db.prepare(
|
|
13208
13585
|
"SELECT target FROM note_links WHERE note_path = ?"
|
|
13209
|
-
).all(
|
|
13210
|
-
linkMap.set(
|
|
13586
|
+
).all(path40);
|
|
13587
|
+
linkMap.set(path40, new Set(rows.map((r) => r.target)));
|
|
13211
13588
|
}
|
|
13212
13589
|
} catch {
|
|
13213
13590
|
return;
|
|
@@ -14347,50 +14724,6 @@ function registerGraphTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
14347
14724
|
};
|
|
14348
14725
|
}
|
|
14349
14726
|
);
|
|
14350
|
-
server2.tool(
|
|
14351
|
-
"get_weighted_links",
|
|
14352
|
-
"Use when ranking outgoing links from a note by relationship strength. Produces weighted link entries reflecting edge survival, co-session access, and source activity. Returns ranked outgoing links with weight scores. Does not include incoming links \u2014 use get_strong_connections for bidirectional.",
|
|
14353
|
-
{
|
|
14354
|
-
path: z2.string().describe('Path to the note (e.g., "daily/2026-02-24.md")'),
|
|
14355
|
-
min_weight: z2.number().default(1).describe("Minimum weight threshold (default 1.0)"),
|
|
14356
|
-
limit: z2.number().default(20).describe("Maximum number of results to return")
|
|
14357
|
-
},
|
|
14358
|
-
async ({ path: notePath, min_weight, limit: requestedLimit }) => {
|
|
14359
|
-
const stateDb2 = getStateDb4?.();
|
|
14360
|
-
if (!stateDb2) {
|
|
14361
|
-
return { content: [{ type: "text", text: JSON.stringify({ error: "StateDb not initialized" }) }] };
|
|
14362
|
-
}
|
|
14363
|
-
const limit = Math.min(requestedLimit ?? 20, MAX_LIMIT);
|
|
14364
|
-
const now = Date.now();
|
|
14365
|
-
const rows = stateDb2.db.prepare(`
|
|
14366
|
-
SELECT target, weight, weight_updated_at
|
|
14367
|
-
FROM note_links
|
|
14368
|
-
WHERE note_path = ?
|
|
14369
|
-
ORDER BY weight DESC
|
|
14370
|
-
`).all(notePath);
|
|
14371
|
-
const results = rows.map((row) => {
|
|
14372
|
-
const daysSinceUpdated = row.weight_updated_at ? (now - row.weight_updated_at) / (1e3 * 60 * 60 * 24) : 0;
|
|
14373
|
-
const decayFactor = Math.max(0.1, 1 - daysSinceUpdated / 180);
|
|
14374
|
-
const effectiveWeight = Math.round(row.weight * decayFactor * 1e3) / 1e3;
|
|
14375
|
-
return {
|
|
14376
|
-
target: row.target,
|
|
14377
|
-
weight: row.weight,
|
|
14378
|
-
weight_effective: effectiveWeight,
|
|
14379
|
-
last_updated: row.weight_updated_at
|
|
14380
|
-
};
|
|
14381
|
-
}).filter((r) => r.weight_effective >= min_weight).slice(0, limit);
|
|
14382
|
-
return {
|
|
14383
|
-
content: [{
|
|
14384
|
-
type: "text",
|
|
14385
|
-
text: JSON.stringify({
|
|
14386
|
-
note: notePath,
|
|
14387
|
-
count: results.length,
|
|
14388
|
-
links: results
|
|
14389
|
-
}, null, 2)
|
|
14390
|
-
}]
|
|
14391
|
-
};
|
|
14392
|
-
}
|
|
14393
|
-
);
|
|
14394
14727
|
server2.tool(
|
|
14395
14728
|
"get_strong_connections",
|
|
14396
14729
|
"Use when finding the most important relationships for a note in both directions. Produces bidirectional connections ranked by combined edge weight. Returns both incoming and outgoing links sorted by strength. Does not compute path distances \u2014 use get_link_path for shortest paths.",
|
|
@@ -14729,16 +15062,16 @@ function registerWikilinkTools(server2, getIndex, getVaultPath, getStateDb4 = ()
|
|
|
14729
15062
|
const weightedStats = getWeightedEntityStats(stateDb3);
|
|
14730
15063
|
const statsMap = new Map(weightedStats.map((s) => [s.entity.toLowerCase(), s]));
|
|
14731
15064
|
for (const suggestion of scored.detailed) {
|
|
14732
|
-
const
|
|
14733
|
-
if (
|
|
15065
|
+
const stat4 = statsMap.get(suggestion.entity.toLowerCase());
|
|
15066
|
+
if (stat4) {
|
|
14734
15067
|
const effectiveAlpha = isAiConfigEntity(suggestion.entity) ? AI_CONFIG_PRIOR_ALPHA : PRIOR_ALPHA;
|
|
14735
|
-
const posteriorMean = computePosteriorMean(
|
|
14736
|
-
const totalObs = effectiveAlpha +
|
|
15068
|
+
const posteriorMean = computePosteriorMean(stat4.weightedCorrect, stat4.weightedFp, effectiveAlpha);
|
|
15069
|
+
const totalObs = effectiveAlpha + stat4.weightedCorrect + PRIOR_BETA + stat4.weightedFp;
|
|
14737
15070
|
suggestion.suppressionContext = {
|
|
14738
15071
|
posteriorMean: Math.round(posteriorMean * 1e3) / 1e3,
|
|
14739
15072
|
totalObservations: Math.round(totalObs * 10) / 10,
|
|
14740
15073
|
isSuppressed: totalObs >= SUPPRESSION_MIN_OBSERVATIONS && posteriorMean < SUPPRESSION_POSTERIOR_THRESHOLD,
|
|
14741
|
-
falsePositiveRate: Math.round(
|
|
15074
|
+
falsePositiveRate: Math.round(stat4.weightedFpRate * 1e3) / 1e3
|
|
14742
15075
|
};
|
|
14743
15076
|
}
|
|
14744
15077
|
}
|
|
@@ -14783,14 +15116,14 @@ function registerWikilinkTools(server2, getIndex, getVaultPath, getStateDb4 = ()
|
|
|
14783
15116
|
};
|
|
14784
15117
|
function findSimilarEntity2(target, entities) {
|
|
14785
15118
|
const targetLower = target.toLowerCase();
|
|
14786
|
-
for (const [name,
|
|
15119
|
+
for (const [name, path40] of entities) {
|
|
14787
15120
|
if (name.startsWith(targetLower) || targetLower.startsWith(name)) {
|
|
14788
|
-
return
|
|
15121
|
+
return path40;
|
|
14789
15122
|
}
|
|
14790
15123
|
}
|
|
14791
|
-
for (const [name,
|
|
15124
|
+
for (const [name, path40] of entities) {
|
|
14792
15125
|
if (name.includes(targetLower) || targetLower.includes(name)) {
|
|
14793
|
-
return
|
|
15126
|
+
return path40;
|
|
14794
15127
|
}
|
|
14795
15128
|
}
|
|
14796
15129
|
return void 0;
|
|
@@ -15501,7 +15834,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
15501
15834
|
}
|
|
15502
15835
|
const indexBuilt = indexState2 === "ready" && index !== void 0 && index.notes !== void 0;
|
|
15503
15836
|
let lastIndexActivityAt;
|
|
15504
|
-
let
|
|
15837
|
+
let lastFullRebuildAt2;
|
|
15505
15838
|
let lastWatcherBatchAt;
|
|
15506
15839
|
let lastBuild;
|
|
15507
15840
|
let lastManual;
|
|
@@ -15511,13 +15844,13 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
15511
15844
|
if (lastAny) lastIndexActivityAt = lastAny.timestamp;
|
|
15512
15845
|
lastBuild = getLastEventByTrigger(stateDb2, "startup_build") ?? void 0;
|
|
15513
15846
|
lastManual = getLastEventByTrigger(stateDb2, "manual_refresh") ?? void 0;
|
|
15514
|
-
|
|
15847
|
+
lastFullRebuildAt2 = Math.max(lastBuild?.timestamp ?? 0, lastManual?.timestamp ?? 0) || void 0;
|
|
15515
15848
|
const lastWatcher = getLastEventByTrigger(stateDb2, "watcher");
|
|
15516
15849
|
if (lastWatcher) lastWatcherBatchAt = lastWatcher.timestamp;
|
|
15517
15850
|
} catch {
|
|
15518
15851
|
}
|
|
15519
15852
|
}
|
|
15520
|
-
const freshnessTimestamp =
|
|
15853
|
+
const freshnessTimestamp = lastFullRebuildAt2 ?? (indexBuilt && index.builtAt ? index.builtAt.getTime() : void 0);
|
|
15521
15854
|
const indexAge = freshnessTimestamp ? Math.floor((Date.now() - freshnessTimestamp) / 1e3) : -1;
|
|
15522
15855
|
const indexStale = indexBuilt && indexAge > STALE_THRESHOLD_SECONDS;
|
|
15523
15856
|
if (indexState2 === "building") {
|
|
@@ -15564,8 +15897,8 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
15564
15897
|
}
|
|
15565
15898
|
let configInfo;
|
|
15566
15899
|
if (isFull) {
|
|
15567
|
-
const
|
|
15568
|
-
configInfo = Object.keys(
|
|
15900
|
+
const config2 = getConfig2();
|
|
15901
|
+
configInfo = Object.keys(config2).length > 0 ? config2 : void 0;
|
|
15569
15902
|
}
|
|
15570
15903
|
let lastRebuild;
|
|
15571
15904
|
if (stateDb2) {
|
|
@@ -15694,15 +16027,15 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
15694
16027
|
watcher_pending: getWatcherStatus2()?.pendingEvents,
|
|
15695
16028
|
last_index_activity_at: lastIndexActivityAt,
|
|
15696
16029
|
last_index_activity_ago_seconds: lastIndexActivityAt ? Math.floor((Date.now() - lastIndexActivityAt) / 1e3) : void 0,
|
|
15697
|
-
last_full_rebuild_at:
|
|
16030
|
+
last_full_rebuild_at: lastFullRebuildAt2,
|
|
15698
16031
|
last_watcher_batch_at: lastWatcherBatchAt,
|
|
15699
16032
|
pipeline_activity: pipelineActivity,
|
|
15700
16033
|
dead_link_count: isFull ? deadLinkCount : void 0,
|
|
15701
16034
|
top_dead_link_targets: isFull ? topDeadLinkTargets : void 0,
|
|
15702
16035
|
sweep: isFull ? getSweepResults() ?? void 0 : void 0,
|
|
15703
16036
|
proactive_linking: isFull && stateDb2 ? (() => {
|
|
15704
|
-
const
|
|
15705
|
-
const enabled =
|
|
16037
|
+
const config2 = getConfig2();
|
|
16038
|
+
const enabled = config2.proactive_linking !== false;
|
|
15706
16039
|
const queuePending = stateDb2.db.prepare(
|
|
15707
16040
|
`SELECT COUNT(*) as cnt FROM proactive_queue WHERE status = 'pending'`
|
|
15708
16041
|
).get();
|
|
@@ -15810,8 +16143,8 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
15810
16143
|
daily_counts: z4.record(z4.number())
|
|
15811
16144
|
}).describe("Activity summary for the last 7 days")
|
|
15812
16145
|
};
|
|
15813
|
-
function isPeriodicNote3(
|
|
15814
|
-
const filename =
|
|
16146
|
+
function isPeriodicNote3(path40) {
|
|
16147
|
+
const filename = path40.split("/").pop() || "";
|
|
15815
16148
|
const nameWithoutExt = filename.replace(/\.md$/, "");
|
|
15816
16149
|
const patterns = [
|
|
15817
16150
|
/^\d{4}-\d{2}-\d{2}$/,
|
|
@@ -15826,7 +16159,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
15826
16159
|
// YYYY (yearly)
|
|
15827
16160
|
];
|
|
15828
16161
|
const periodicFolders = ["daily", "weekly", "monthly", "quarterly", "yearly", "journal", "journals"];
|
|
15829
|
-
const folder =
|
|
16162
|
+
const folder = path40.split("/")[0]?.toLowerCase() || "";
|
|
15830
16163
|
return patterns.some((p) => p.test(nameWithoutExt)) || periodicFolders.includes(folder);
|
|
15831
16164
|
}
|
|
15832
16165
|
async function runVaultStats() {
|
|
@@ -16348,7 +16681,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
|
|
|
16348
16681
|
|
|
16349
16682
|
// src/tools/read/system.ts
|
|
16350
16683
|
import { z as z5 } from "zod";
|
|
16351
|
-
import { scanVaultEntities as
|
|
16684
|
+
import { scanVaultEntities as scanVaultEntities3, getEntityIndexFromDb as getEntityIndexFromDb2, getAllEntitiesFromDb as getAllEntitiesFromDb3 } from "@velvetmonkey/vault-core";
|
|
16352
16685
|
|
|
16353
16686
|
// src/core/read/aliasSuggestions.ts
|
|
16354
16687
|
import { STOPWORDS_EN as STOPWORDS_EN3 } from "@velvetmonkey/vault-core";
|
|
@@ -16465,11 +16798,11 @@ function registerSystemTools(server2, getIndex, setIndex, getVaultPath, setConfi
|
|
|
16465
16798
|
if (stateDb2) {
|
|
16466
16799
|
tracker.start("entity_sync", {});
|
|
16467
16800
|
try {
|
|
16468
|
-
const
|
|
16469
|
-
const excludeFolders =
|
|
16470
|
-
const entityIndex2 = await
|
|
16801
|
+
const config2 = loadConfig(stateDb2);
|
|
16802
|
+
const excludeFolders = config2.exclude_entity_folders?.length ? config2.exclude_entity_folders : DEFAULT_ENTITY_EXCLUDE_FOLDERS;
|
|
16803
|
+
const entityIndex2 = await scanVaultEntities3(vaultPath2, {
|
|
16471
16804
|
excludeFolders,
|
|
16472
|
-
customCategories:
|
|
16805
|
+
customCategories: config2.custom_categories
|
|
16473
16806
|
});
|
|
16474
16807
|
stateDb2.replaceAllEntities(entityIndex2);
|
|
16475
16808
|
tracker.end({ entities: entityIndex2._metadata.total_entities });
|
|
@@ -16591,7 +16924,7 @@ function registerSystemTools(server2, getIndex, setIndex, getVaultPath, setConfi
|
|
|
16591
16924
|
if (stateDb2) {
|
|
16592
16925
|
tracker.start("recency", {});
|
|
16593
16926
|
try {
|
|
16594
|
-
const entities =
|
|
16927
|
+
const entities = getAllEntitiesFromDb3(stateDb2).map((e) => ({
|
|
16595
16928
|
name: e.name,
|
|
16596
16929
|
path: e.path,
|
|
16597
16930
|
aliases: e.aliases
|
|
@@ -16610,7 +16943,7 @@ function registerSystemTools(server2, getIndex, setIndex, getVaultPath, setConfi
|
|
|
16610
16943
|
if (stateDb2) {
|
|
16611
16944
|
tracker.start("cooccurrence", {});
|
|
16612
16945
|
try {
|
|
16613
|
-
const entityNames =
|
|
16946
|
+
const entityNames = getAllEntitiesFromDb3(stateDb2).map((e) => e.name);
|
|
16614
16947
|
const cooccurrenceIdx = await mineCooccurrences(vaultPath2, entityNames);
|
|
16615
16948
|
setCooccurrenceIndex(cooccurrenceIdx);
|
|
16616
16949
|
saveCooccurrenceToStateDb(stateDb2, cooccurrenceIdx);
|
|
@@ -16655,7 +16988,7 @@ function registerSystemTools(server2, getIndex, setIndex, getVaultPath, setConfi
|
|
|
16655
16988
|
if (stateDb2 && hasEntityEmbeddingsIndex()) {
|
|
16656
16989
|
tracker.start("entity_embeddings", {});
|
|
16657
16990
|
try {
|
|
16658
|
-
const entities =
|
|
16991
|
+
const entities = getAllEntitiesFromDb3(stateDb2);
|
|
16659
16992
|
if (entities.length > 0) {
|
|
16660
16993
|
const entityMap = new Map(entities.map((e) => [e.name, {
|
|
16661
16994
|
name: e.name,
|
|
@@ -16933,13 +17266,13 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
16933
17266
|
max_content_chars: z6.number().default(2e4).describe("Max total chars of section content to include. Sections are truncated at paragraph boundaries.")
|
|
16934
17267
|
}
|
|
16935
17268
|
},
|
|
16936
|
-
async ({ path:
|
|
17269
|
+
async ({ path: path40, include_content, max_content_chars }) => {
|
|
16937
17270
|
const index = getIndex();
|
|
16938
17271
|
const vaultPath2 = getVaultPath();
|
|
16939
|
-
const result = await getNoteStructure(index,
|
|
17272
|
+
const result = await getNoteStructure(index, path40, vaultPath2);
|
|
16940
17273
|
if (!result) {
|
|
16941
17274
|
return {
|
|
16942
|
-
content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path:
|
|
17275
|
+
content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path40 }, null, 2) }]
|
|
16943
17276
|
};
|
|
16944
17277
|
}
|
|
16945
17278
|
let totalChars = 0;
|
|
@@ -16950,7 +17283,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
16950
17283
|
truncated = true;
|
|
16951
17284
|
break;
|
|
16952
17285
|
}
|
|
16953
|
-
const sectionResult = await getSectionContent(index,
|
|
17286
|
+
const sectionResult = await getSectionContent(index, path40, section.heading.text, vaultPath2, true);
|
|
16954
17287
|
if (sectionResult) {
|
|
16955
17288
|
let content = sectionResult.content;
|
|
16956
17289
|
const remaining = max_content_chars - totalChars;
|
|
@@ -16965,13 +17298,13 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
16965
17298
|
}
|
|
16966
17299
|
}
|
|
16967
17300
|
}
|
|
16968
|
-
const note = index.notes.get(
|
|
17301
|
+
const note = index.notes.get(path40);
|
|
16969
17302
|
const enriched = { ...result };
|
|
16970
17303
|
if (note) {
|
|
16971
17304
|
enriched.frontmatter = note.frontmatter;
|
|
16972
17305
|
enriched.tags = note.tags;
|
|
16973
17306
|
enriched.aliases = note.aliases;
|
|
16974
|
-
const normalizedPath =
|
|
17307
|
+
const normalizedPath = path40.toLowerCase().replace(/\.md$/, "");
|
|
16975
17308
|
const backlinks = index.backlinks.get(normalizedPath) || [];
|
|
16976
17309
|
enriched.backlink_count = backlinks.length;
|
|
16977
17310
|
enriched.outlink_count = note.outlinks.length;
|
|
@@ -17009,15 +17342,15 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17009
17342
|
max_content_chars: z6.number().default(1e4).describe("Max chars of section content. Truncated at paragraph boundaries.")
|
|
17010
17343
|
}
|
|
17011
17344
|
},
|
|
17012
|
-
async ({ path:
|
|
17345
|
+
async ({ path: path40, heading, include_subheadings, max_content_chars }) => {
|
|
17013
17346
|
const index = getIndex();
|
|
17014
17347
|
const vaultPath2 = getVaultPath();
|
|
17015
|
-
const result = await getSectionContent(index,
|
|
17348
|
+
const result = await getSectionContent(index, path40, heading, vaultPath2, include_subheadings);
|
|
17016
17349
|
if (!result) {
|
|
17017
17350
|
return {
|
|
17018
17351
|
content: [{ type: "text", text: JSON.stringify({
|
|
17019
17352
|
error: "Section not found",
|
|
17020
|
-
path:
|
|
17353
|
+
path: path40,
|
|
17021
17354
|
heading
|
|
17022
17355
|
}, null, 2) }]
|
|
17023
17356
|
};
|
|
@@ -17078,16 +17411,16 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17078
17411
|
offset: z6.coerce.number().default(0).describe("Number of results to skip (for pagination)")
|
|
17079
17412
|
}
|
|
17080
17413
|
},
|
|
17081
|
-
async ({ path:
|
|
17414
|
+
async ({ path: path40, status, has_due_date, folder, tag, limit: requestedLimit, offset }) => {
|
|
17082
17415
|
const limit = Math.min(requestedLimit ?? 25, MAX_LIMIT);
|
|
17083
17416
|
const index = getIndex();
|
|
17084
17417
|
const vaultPath2 = getVaultPath();
|
|
17085
|
-
const
|
|
17086
|
-
if (
|
|
17087
|
-
const result2 = await getTasksFromNote(index,
|
|
17418
|
+
const config2 = getConfig2();
|
|
17419
|
+
if (path40) {
|
|
17420
|
+
const result2 = await getTasksFromNote(index, path40, vaultPath2, getExcludeTags(config2));
|
|
17088
17421
|
if (!result2) {
|
|
17089
17422
|
return {
|
|
17090
|
-
content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path:
|
|
17423
|
+
content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path40 }, null, 2) }]
|
|
17091
17424
|
};
|
|
17092
17425
|
}
|
|
17093
17426
|
let filtered = result2;
|
|
@@ -17097,7 +17430,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17097
17430
|
const paged2 = filtered.slice(offset, offset + limit);
|
|
17098
17431
|
return {
|
|
17099
17432
|
content: [{ type: "text", text: JSON.stringify({
|
|
17100
|
-
path:
|
|
17433
|
+
path: path40,
|
|
17101
17434
|
total_count: filtered.length,
|
|
17102
17435
|
returned_count: paged2.length,
|
|
17103
17436
|
open: result2.filter((t) => t.status === "open").length,
|
|
@@ -17107,12 +17440,12 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17107
17440
|
};
|
|
17108
17441
|
}
|
|
17109
17442
|
if (isTaskCacheReady()) {
|
|
17110
|
-
refreshIfStale(vaultPath2, index, getExcludeTags(
|
|
17443
|
+
refreshIfStale(vaultPath2, index, getExcludeTags(config2));
|
|
17111
17444
|
if (has_due_date) {
|
|
17112
17445
|
const result3 = queryTasksFromCache({
|
|
17113
17446
|
status,
|
|
17114
17447
|
folder,
|
|
17115
|
-
excludeTags: getExcludeTags(
|
|
17448
|
+
excludeTags: getExcludeTags(config2),
|
|
17116
17449
|
has_due_date: true,
|
|
17117
17450
|
limit,
|
|
17118
17451
|
offset
|
|
@@ -17129,7 +17462,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17129
17462
|
status,
|
|
17130
17463
|
folder,
|
|
17131
17464
|
tag,
|
|
17132
|
-
excludeTags: getExcludeTags(
|
|
17465
|
+
excludeTags: getExcludeTags(config2),
|
|
17133
17466
|
limit,
|
|
17134
17467
|
offset
|
|
17135
17468
|
});
|
|
@@ -17148,7 +17481,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17148
17481
|
const allResults = await getTasksWithDueDates(index, vaultPath2, {
|
|
17149
17482
|
status,
|
|
17150
17483
|
folder,
|
|
17151
|
-
excludeTags: getExcludeTags(
|
|
17484
|
+
excludeTags: getExcludeTags(config2)
|
|
17152
17485
|
});
|
|
17153
17486
|
const paged2 = allResults.slice(offset, offset + limit);
|
|
17154
17487
|
return {
|
|
@@ -17164,7 +17497,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
|
|
|
17164
17497
|
folder,
|
|
17165
17498
|
tag,
|
|
17166
17499
|
limit: limit + offset,
|
|
17167
|
-
excludeTags: getExcludeTags(
|
|
17500
|
+
excludeTags: getExcludeTags(config2)
|
|
17168
17501
|
});
|
|
17169
17502
|
const paged = result.tasks.slice(offset, offset + limit);
|
|
17170
17503
|
return {
|
|
@@ -17831,10 +18164,10 @@ function isTemplatePath(notePath) {
|
|
|
17831
18164
|
const folder = notePath.split("/")[0]?.toLowerCase() || "";
|
|
17832
18165
|
return folder === "templates" || folder === "template";
|
|
17833
18166
|
}
|
|
17834
|
-
function getExcludedPaths(index,
|
|
18167
|
+
function getExcludedPaths(index, config2) {
|
|
17835
18168
|
const excluded = /* @__PURE__ */ new Set();
|
|
17836
|
-
const excludeTags = new Set(getExcludeTags(
|
|
17837
|
-
const excludeEntities = new Set(getExcludeEntities(
|
|
18169
|
+
const excludeTags = new Set(getExcludeTags(config2).map((t) => t.toLowerCase()));
|
|
18170
|
+
const excludeEntities = new Set(getExcludeEntities(config2).map((e) => e.toLowerCase()));
|
|
17838
18171
|
if (excludeTags.size === 0 && excludeEntities.size === 0) return excluded;
|
|
17839
18172
|
for (const note of index.notes.values()) {
|
|
17840
18173
|
if (excludeTags.size > 0) {
|
|
@@ -17881,8 +18214,8 @@ function registerGraphAnalysisTools(server2, getIndex, getVaultPath, getStateDb4
|
|
|
17881
18214
|
requireIndex();
|
|
17882
18215
|
const limit = Math.min(requestedLimit ?? 50, MAX_LIMIT);
|
|
17883
18216
|
const index = getIndex();
|
|
17884
|
-
const
|
|
17885
|
-
const excludedPaths = getExcludedPaths(index,
|
|
18217
|
+
const config2 = getConfig2?.() ?? {};
|
|
18218
|
+
const excludedPaths = getExcludedPaths(index, config2);
|
|
17886
18219
|
switch (analysis) {
|
|
17887
18220
|
case "orphans": {
|
|
17888
18221
|
const allOrphans = findOrphanNotes(index, folder).filter((o) => !isPeriodicNote(o.path) && !excludedPaths.has(o.path));
|
|
@@ -18050,7 +18383,7 @@ function registerGraphAnalysisTools(server2, getIndex, getVaultPath, getStateDb4
|
|
|
18050
18383
|
return !note || !excludedPaths.has(note.path);
|
|
18051
18384
|
});
|
|
18052
18385
|
}
|
|
18053
|
-
const excludeEntities = new Set(getExcludeEntities(
|
|
18386
|
+
const excludeEntities = new Set(getExcludeEntities(config2).map((e) => e.toLowerCase()));
|
|
18054
18387
|
if (excludeEntities.size > 0) {
|
|
18055
18388
|
hubs = hubs.filter((hub) => !excludeEntities.has(hub.entity.toLowerCase()));
|
|
18056
18389
|
}
|
|
@@ -19079,8 +19412,8 @@ function registerNoteIntelligenceTools(server2, getIndex, getVaultPath, getConfi
|
|
|
19079
19412
|
// src/tools/write/mutations.ts
|
|
19080
19413
|
init_writer();
|
|
19081
19414
|
import { z as z12 } from "zod";
|
|
19082
|
-
import
|
|
19083
|
-
import
|
|
19415
|
+
import fs24 from "fs/promises";
|
|
19416
|
+
import path26 from "path";
|
|
19084
19417
|
|
|
19085
19418
|
// src/core/write/validator.ts
|
|
19086
19419
|
var TIMESTAMP_PATTERN = /^\*\*\d{2}:\d{2}\*\*/;
|
|
@@ -19298,63 +19631,63 @@ init_constants2();
|
|
|
19298
19631
|
init_writer();
|
|
19299
19632
|
init_wikilinks();
|
|
19300
19633
|
init_wikilinkFeedback();
|
|
19301
|
-
import
|
|
19302
|
-
import
|
|
19634
|
+
import fs23 from "fs/promises";
|
|
19635
|
+
import path25 from "path";
|
|
19303
19636
|
|
|
19304
19637
|
// src/core/write/policy/policyPaths.ts
|
|
19305
|
-
import
|
|
19306
|
-
import
|
|
19638
|
+
import fs22 from "fs/promises";
|
|
19639
|
+
import path24 from "path";
|
|
19307
19640
|
function getPoliciesDir(vaultPath2) {
|
|
19308
|
-
return
|
|
19641
|
+
return path24.join(vaultPath2, ".flywheel", "policies");
|
|
19309
19642
|
}
|
|
19310
19643
|
function getLegacyPoliciesDir(vaultPath2) {
|
|
19311
|
-
return
|
|
19644
|
+
return path24.join(vaultPath2, ".claude", "policies");
|
|
19312
19645
|
}
|
|
19313
19646
|
async function ensurePoliciesDir(vaultPath2) {
|
|
19314
19647
|
const dir = getPoliciesDir(vaultPath2);
|
|
19315
|
-
await
|
|
19648
|
+
await fs22.mkdir(dir, { recursive: true });
|
|
19316
19649
|
}
|
|
19317
19650
|
async function migratePoliciesIfNeeded(vaultPath2) {
|
|
19318
19651
|
const legacyDir = getLegacyPoliciesDir(vaultPath2);
|
|
19319
19652
|
let files;
|
|
19320
19653
|
try {
|
|
19321
|
-
files = await
|
|
19654
|
+
files = await fs22.readdir(legacyDir);
|
|
19322
19655
|
} catch {
|
|
19323
19656
|
return;
|
|
19324
19657
|
}
|
|
19325
19658
|
const yamlFiles = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
19326
19659
|
if (yamlFiles.length === 0) {
|
|
19327
19660
|
await tryRmdir(legacyDir);
|
|
19328
|
-
await tryRmdir(
|
|
19661
|
+
await tryRmdir(path24.join(vaultPath2, ".claude"));
|
|
19329
19662
|
return;
|
|
19330
19663
|
}
|
|
19331
19664
|
await ensurePoliciesDir(vaultPath2);
|
|
19332
19665
|
const destDir = getPoliciesDir(vaultPath2);
|
|
19333
19666
|
for (const file of yamlFiles) {
|
|
19334
|
-
const src =
|
|
19335
|
-
const dest =
|
|
19667
|
+
const src = path24.join(legacyDir, file);
|
|
19668
|
+
const dest = path24.join(destDir, file);
|
|
19336
19669
|
try {
|
|
19337
|
-
await
|
|
19670
|
+
await fs22.access(dest);
|
|
19338
19671
|
} catch {
|
|
19339
|
-
await
|
|
19672
|
+
await fs22.copyFile(src, dest);
|
|
19340
19673
|
}
|
|
19341
|
-
await
|
|
19674
|
+
await fs22.unlink(src);
|
|
19342
19675
|
}
|
|
19343
19676
|
await tryRmdir(legacyDir);
|
|
19344
|
-
await tryRmdir(
|
|
19677
|
+
await tryRmdir(path24.join(vaultPath2, ".claude"));
|
|
19345
19678
|
}
|
|
19346
19679
|
async function tryRmdir(dir) {
|
|
19347
19680
|
try {
|
|
19348
|
-
const remaining = await
|
|
19681
|
+
const remaining = await fs22.readdir(dir);
|
|
19349
19682
|
if (remaining.length === 0) {
|
|
19350
|
-
await
|
|
19683
|
+
await fs22.rmdir(dir);
|
|
19351
19684
|
}
|
|
19352
19685
|
} catch {
|
|
19353
19686
|
}
|
|
19354
19687
|
}
|
|
19355
19688
|
var migrationCache = /* @__PURE__ */ new Map();
|
|
19356
19689
|
async function ensureMigrated(vaultPath2) {
|
|
19357
|
-
const key =
|
|
19690
|
+
const key = path24.resolve(vaultPath2);
|
|
19358
19691
|
if (!migrationCache.has(key)) {
|
|
19359
19692
|
migrationCache.set(key, migratePoliciesIfNeeded(vaultPath2));
|
|
19360
19693
|
}
|
|
@@ -19410,7 +19743,7 @@ async function handleGitCommit(vaultPath2, notePath, commit, prefix) {
|
|
|
19410
19743
|
async function getPolicyHint(vaultPath2) {
|
|
19411
19744
|
try {
|
|
19412
19745
|
const policiesDir = getPoliciesDir(vaultPath2);
|
|
19413
|
-
const files = await
|
|
19746
|
+
const files = await fs23.readdir(policiesDir);
|
|
19414
19747
|
const yamlFiles = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
|
|
19415
19748
|
if (yamlFiles.length > 0) {
|
|
19416
19749
|
const names = yamlFiles.map((f) => f.replace(/\.ya?ml$/, "")).join(", ");
|
|
@@ -19421,9 +19754,9 @@ async function getPolicyHint(vaultPath2) {
|
|
|
19421
19754
|
return "";
|
|
19422
19755
|
}
|
|
19423
19756
|
async function ensureFileExists(vaultPath2, notePath) {
|
|
19424
|
-
const fullPath =
|
|
19757
|
+
const fullPath = path25.join(vaultPath2, notePath);
|
|
19425
19758
|
try {
|
|
19426
|
-
await
|
|
19759
|
+
await fs23.access(fullPath);
|
|
19427
19760
|
return null;
|
|
19428
19761
|
} catch {
|
|
19429
19762
|
const hint = await getPolicyHint(vaultPath2);
|
|
@@ -19626,17 +19959,17 @@ async function executeCreateNote(options) {
|
|
|
19626
19959
|
if (!pathCheck.valid) {
|
|
19627
19960
|
return { success: false, result: errorResult(notePath, `Path blocked: ${pathCheck.reason}`), filesWritten: [] };
|
|
19628
19961
|
}
|
|
19629
|
-
const fullPath =
|
|
19962
|
+
const fullPath = path25.join(vaultPath2, notePath);
|
|
19630
19963
|
let fileExists = false;
|
|
19631
19964
|
try {
|
|
19632
|
-
await
|
|
19965
|
+
await fs23.access(fullPath);
|
|
19633
19966
|
fileExists = true;
|
|
19634
19967
|
} catch {
|
|
19635
19968
|
}
|
|
19636
19969
|
if (fileExists && !overwrite) {
|
|
19637
19970
|
return { success: false, result: errorResult(notePath, `File already exists: ${notePath}. Use overwrite=true to replace.`), filesWritten: [] };
|
|
19638
19971
|
}
|
|
19639
|
-
await
|
|
19972
|
+
await fs23.mkdir(path25.dirname(fullPath), { recursive: true });
|
|
19640
19973
|
const { maybeApplyWikilinks: maybeApplyWikilinks2 } = await Promise.resolve().then(() => (init_wikilinks(), wikilinks_exports));
|
|
19641
19974
|
const { content: processedContent } = maybeApplyWikilinks2(content, skipWikilinks ?? false, notePath);
|
|
19642
19975
|
let finalFrontmatter = frontmatter;
|
|
@@ -19670,13 +20003,13 @@ async function executeDeleteNote(options) {
|
|
|
19670
20003
|
if (!pathCheck.valid) {
|
|
19671
20004
|
return { success: false, result: errorResult(notePath, `Path blocked: ${pathCheck.reason}`), filesWritten: [] };
|
|
19672
20005
|
}
|
|
19673
|
-
const fullPath =
|
|
20006
|
+
const fullPath = path25.join(vaultPath2, notePath);
|
|
19674
20007
|
try {
|
|
19675
|
-
await
|
|
20008
|
+
await fs23.access(fullPath);
|
|
19676
20009
|
} catch {
|
|
19677
20010
|
return { success: false, result: errorResult(notePath, `File not found: ${notePath}`), filesWritten: [] };
|
|
19678
20011
|
}
|
|
19679
|
-
await
|
|
20012
|
+
await fs23.unlink(fullPath);
|
|
19680
20013
|
const result = successResult(notePath, `Deleted note: ${notePath}`, {});
|
|
19681
20014
|
return { success: true, result, filesWritten: [notePath] };
|
|
19682
20015
|
} catch (error) {
|
|
@@ -19689,15 +20022,15 @@ async function executeDeleteNote(options) {
|
|
|
19689
20022
|
}
|
|
19690
20023
|
|
|
19691
20024
|
// src/tools/write/mutations.ts
|
|
19692
|
-
async function createNoteFromTemplate(vaultPath2, notePath,
|
|
20025
|
+
async function createNoteFromTemplate(vaultPath2, notePath, config2) {
|
|
19693
20026
|
const validation = await validatePathSecure(vaultPath2, notePath);
|
|
19694
20027
|
if (!validation.valid) {
|
|
19695
20028
|
throw new Error(`Path blocked: ${validation.reason}`);
|
|
19696
20029
|
}
|
|
19697
|
-
const fullPath =
|
|
19698
|
-
await
|
|
19699
|
-
const templates =
|
|
19700
|
-
const filename =
|
|
20030
|
+
const fullPath = path26.join(vaultPath2, notePath);
|
|
20031
|
+
await fs24.mkdir(path26.dirname(fullPath), { recursive: true });
|
|
20032
|
+
const templates = config2.templates || {};
|
|
20033
|
+
const filename = path26.basename(notePath, ".md").toLowerCase();
|
|
19701
20034
|
let templatePath;
|
|
19702
20035
|
let periodicType;
|
|
19703
20036
|
const dailyPattern = /^\d{4}-\d{2}-\d{2}/;
|
|
@@ -19730,7 +20063,7 @@ async function createNoteFromTemplate(vaultPath2, notePath, config) {
|
|
|
19730
20063
|
];
|
|
19731
20064
|
for (const candidate of candidates) {
|
|
19732
20065
|
try {
|
|
19733
|
-
await
|
|
20066
|
+
await fs24.access(path26.join(vaultPath2, candidate));
|
|
19734
20067
|
templatePath = candidate;
|
|
19735
20068
|
console.error(`[Flywheel] Template not in config but found at ${candidate} \u2014 using it`);
|
|
19736
20069
|
break;
|
|
@@ -19741,11 +20074,11 @@ async function createNoteFromTemplate(vaultPath2, notePath, config) {
|
|
|
19741
20074
|
let templateContent;
|
|
19742
20075
|
if (templatePath) {
|
|
19743
20076
|
try {
|
|
19744
|
-
const absTemplatePath =
|
|
19745
|
-
templateContent = await
|
|
20077
|
+
const absTemplatePath = path26.join(vaultPath2, templatePath);
|
|
20078
|
+
templateContent = await fs24.readFile(absTemplatePath, "utf-8");
|
|
19746
20079
|
} catch {
|
|
19747
20080
|
console.error(`[Flywheel] Template at ${templatePath} not readable, using minimal fallback`);
|
|
19748
|
-
const title =
|
|
20081
|
+
const title = path26.basename(notePath, ".md");
|
|
19749
20082
|
templateContent = `---
|
|
19750
20083
|
---
|
|
19751
20084
|
|
|
@@ -19757,7 +20090,7 @@ async function createNoteFromTemplate(vaultPath2, notePath, config) {
|
|
|
19757
20090
|
if (periodicType) {
|
|
19758
20091
|
console.error(`[Flywheel] No ${periodicType} template found in config or vault \u2014 using minimal fallback`);
|
|
19759
20092
|
}
|
|
19760
|
-
const title =
|
|
20093
|
+
const title = path26.basename(notePath, ".md");
|
|
19761
20094
|
templateContent = `---
|
|
19762
20095
|
---
|
|
19763
20096
|
|
|
@@ -19766,7 +20099,7 @@ async function createNoteFromTemplate(vaultPath2, notePath, config) {
|
|
|
19766
20099
|
}
|
|
19767
20100
|
const now = /* @__PURE__ */ new Date();
|
|
19768
20101
|
const dateStr = now.toISOString().split("T")[0];
|
|
19769
|
-
templateContent = templateContent.replace(/\{\{date\}\}/g, dateStr).replace(/\{\{title\}\}/g,
|
|
20102
|
+
templateContent = templateContent.replace(/\{\{date\}\}/g, dateStr).replace(/\{\{title\}\}/g, path26.basename(notePath, ".md"));
|
|
19770
20103
|
const matter9 = (await import("gray-matter")).default;
|
|
19771
20104
|
const parsed = matter9(templateContent);
|
|
19772
20105
|
if (!parsed.data.date) {
|
|
@@ -19805,20 +20138,20 @@ function registerMutationTools(server2, getVaultPath, getConfig2 = () => ({})) {
|
|
|
19805
20138
|
let noteCreated = false;
|
|
19806
20139
|
let templateUsed;
|
|
19807
20140
|
if (create_if_missing && !dry_run) {
|
|
19808
|
-
const fullPath =
|
|
20141
|
+
const fullPath = path26.join(vaultPath2, notePath);
|
|
19809
20142
|
try {
|
|
19810
|
-
await
|
|
20143
|
+
await fs24.access(fullPath);
|
|
19811
20144
|
} catch {
|
|
19812
|
-
const
|
|
19813
|
-
const result = await createNoteFromTemplate(vaultPath2, notePath,
|
|
20145
|
+
const config2 = getConfig2();
|
|
20146
|
+
const result = await createNoteFromTemplate(vaultPath2, notePath, config2);
|
|
19814
20147
|
noteCreated = result.created;
|
|
19815
20148
|
templateUsed = result.templateUsed;
|
|
19816
20149
|
}
|
|
19817
20150
|
}
|
|
19818
20151
|
if (create_if_missing && dry_run) {
|
|
19819
|
-
const fullPath =
|
|
20152
|
+
const fullPath = path26.join(vaultPath2, notePath);
|
|
19820
20153
|
try {
|
|
19821
|
-
await
|
|
20154
|
+
await fs24.access(fullPath);
|
|
19822
20155
|
} catch {
|
|
19823
20156
|
return {
|
|
19824
20157
|
content: [{
|
|
@@ -20302,8 +20635,8 @@ function registerFrontmatterTools(server2, getVaultPath) {
|
|
|
20302
20635
|
init_writer();
|
|
20303
20636
|
init_wikilinks();
|
|
20304
20637
|
import { z as z15 } from "zod";
|
|
20305
|
-
import
|
|
20306
|
-
import
|
|
20638
|
+
import fs25 from "fs/promises";
|
|
20639
|
+
import path27 from "path";
|
|
20307
20640
|
function registerNoteTools(server2, getVaultPath, getIndex) {
|
|
20308
20641
|
server2.tool(
|
|
20309
20642
|
"vault_create_note",
|
|
@@ -20329,23 +20662,23 @@ function registerNoteTools(server2, getVaultPath, getIndex) {
|
|
|
20329
20662
|
if (!validatePath(vaultPath2, notePath)) {
|
|
20330
20663
|
return formatMcpResult(errorResult(notePath, "Invalid path: path traversal not allowed"));
|
|
20331
20664
|
}
|
|
20332
|
-
const fullPath =
|
|
20665
|
+
const fullPath = path27.join(vaultPath2, notePath);
|
|
20333
20666
|
const existsCheck = await ensureFileExists(vaultPath2, notePath);
|
|
20334
20667
|
if (existsCheck === null && !overwrite) {
|
|
20335
20668
|
return formatMcpResult(errorResult(notePath, `File already exists: ${notePath}. Use overwrite=true to replace.`));
|
|
20336
20669
|
}
|
|
20337
|
-
const dir =
|
|
20338
|
-
await
|
|
20670
|
+
const dir = path27.dirname(fullPath);
|
|
20671
|
+
await fs25.mkdir(dir, { recursive: true });
|
|
20339
20672
|
let effectiveContent = content;
|
|
20340
20673
|
let effectiveFrontmatter = frontmatter;
|
|
20341
20674
|
if (template) {
|
|
20342
|
-
const templatePath =
|
|
20675
|
+
const templatePath = path27.join(vaultPath2, template);
|
|
20343
20676
|
try {
|
|
20344
|
-
const raw = await
|
|
20677
|
+
const raw = await fs25.readFile(templatePath, "utf-8");
|
|
20345
20678
|
const matter9 = (await import("gray-matter")).default;
|
|
20346
20679
|
const parsed = matter9(raw);
|
|
20347
20680
|
const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
20348
|
-
const title =
|
|
20681
|
+
const title = path27.basename(notePath, ".md");
|
|
20349
20682
|
let templateContent = parsed.content.replace(/\{\{date\}\}/g, dateStr).replace(/\{\{title\}\}/g, title);
|
|
20350
20683
|
if (content) {
|
|
20351
20684
|
templateContent = templateContent.trimEnd() + "\n\n" + content;
|
|
@@ -20364,7 +20697,7 @@ function registerNoteTools(server2, getVaultPath, getIndex) {
|
|
|
20364
20697
|
effectiveFrontmatter.created = now.toISOString();
|
|
20365
20698
|
}
|
|
20366
20699
|
const warnings = [];
|
|
20367
|
-
const noteName =
|
|
20700
|
+
const noteName = path27.basename(notePath, ".md");
|
|
20368
20701
|
const existingAliases = Array.isArray(effectiveFrontmatter?.aliases) ? effectiveFrontmatter.aliases.filter((a) => typeof a === "string") : [];
|
|
20369
20702
|
const preflight = await checkPreflightSimilarity(noteName);
|
|
20370
20703
|
if (preflight.existingEntity) {
|
|
@@ -20505,8 +20838,8 @@ ${sources}`;
|
|
|
20505
20838
|
}
|
|
20506
20839
|
return formatMcpResult(errorResult(notePath, previewLines.join("\n")));
|
|
20507
20840
|
}
|
|
20508
|
-
const fullPath =
|
|
20509
|
-
await
|
|
20841
|
+
const fullPath = path27.join(vaultPath2, notePath);
|
|
20842
|
+
await fs25.unlink(fullPath);
|
|
20510
20843
|
const gitInfo = await handleGitCommit(vaultPath2, notePath, commit, "[Flywheel:Delete]");
|
|
20511
20844
|
const message = backlinkWarning ? `Deleted note: ${notePath}
|
|
20512
20845
|
|
|
@@ -20526,8 +20859,8 @@ init_writer();
|
|
|
20526
20859
|
init_git();
|
|
20527
20860
|
init_wikilinks();
|
|
20528
20861
|
import { z as z16 } from "zod";
|
|
20529
|
-
import
|
|
20530
|
-
import
|
|
20862
|
+
import fs26 from "fs/promises";
|
|
20863
|
+
import path28 from "path";
|
|
20531
20864
|
import matter6 from "gray-matter";
|
|
20532
20865
|
function escapeRegex(str) {
|
|
20533
20866
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -20546,16 +20879,16 @@ function extractWikilinks2(content) {
|
|
|
20546
20879
|
return wikilinks;
|
|
20547
20880
|
}
|
|
20548
20881
|
function getTitleFromPath(filePath) {
|
|
20549
|
-
return
|
|
20882
|
+
return path28.basename(filePath, ".md");
|
|
20550
20883
|
}
|
|
20551
20884
|
async function findBacklinks(vaultPath2, targetTitle, targetAliases) {
|
|
20552
20885
|
const results = [];
|
|
20553
20886
|
const allTargets = [targetTitle, ...targetAliases].map((t) => t.toLowerCase());
|
|
20554
20887
|
async function scanDir(dir) {
|
|
20555
20888
|
const files = [];
|
|
20556
|
-
const entries = await
|
|
20889
|
+
const entries = await fs26.readdir(dir, { withFileTypes: true });
|
|
20557
20890
|
for (const entry of entries) {
|
|
20558
|
-
const fullPath =
|
|
20891
|
+
const fullPath = path28.join(dir, entry.name);
|
|
20559
20892
|
if (entry.isDirectory() && !entry.name.startsWith(".")) {
|
|
20560
20893
|
files.push(...await scanDir(fullPath));
|
|
20561
20894
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -20566,8 +20899,8 @@ async function findBacklinks(vaultPath2, targetTitle, targetAliases) {
|
|
|
20566
20899
|
}
|
|
20567
20900
|
const allFiles = await scanDir(vaultPath2);
|
|
20568
20901
|
for (const filePath of allFiles) {
|
|
20569
|
-
const relativePath =
|
|
20570
|
-
const content = await
|
|
20902
|
+
const relativePath = path28.relative(vaultPath2, filePath);
|
|
20903
|
+
const content = await fs26.readFile(filePath, "utf-8");
|
|
20571
20904
|
const wikilinks = extractWikilinks2(content);
|
|
20572
20905
|
const matchingLinks = [];
|
|
20573
20906
|
for (const link of wikilinks) {
|
|
@@ -20653,10 +20986,10 @@ function registerMoveNoteTools(server2, getVaultPath) {
|
|
|
20653
20986
|
};
|
|
20654
20987
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
20655
20988
|
}
|
|
20656
|
-
const oldFullPath =
|
|
20657
|
-
const newFullPath =
|
|
20989
|
+
const oldFullPath = path28.join(vaultPath2, oldPath);
|
|
20990
|
+
const newFullPath = path28.join(vaultPath2, newPath);
|
|
20658
20991
|
try {
|
|
20659
|
-
await
|
|
20992
|
+
await fs26.access(oldFullPath);
|
|
20660
20993
|
} catch {
|
|
20661
20994
|
const result2 = {
|
|
20662
20995
|
success: false,
|
|
@@ -20666,7 +20999,7 @@ function registerMoveNoteTools(server2, getVaultPath) {
|
|
|
20666
20999
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
20667
21000
|
}
|
|
20668
21001
|
try {
|
|
20669
|
-
await
|
|
21002
|
+
await fs26.access(newFullPath);
|
|
20670
21003
|
const result2 = {
|
|
20671
21004
|
success: false,
|
|
20672
21005
|
message: `Destination already exists: ${newPath}`,
|
|
@@ -20675,7 +21008,7 @@ function registerMoveNoteTools(server2, getVaultPath) {
|
|
|
20675
21008
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
20676
21009
|
} catch {
|
|
20677
21010
|
}
|
|
20678
|
-
const sourceContent = await
|
|
21011
|
+
const sourceContent = await fs26.readFile(oldFullPath, "utf-8");
|
|
20679
21012
|
const parsed = matter6(sourceContent);
|
|
20680
21013
|
const aliases = extractAliases2(parsed.data);
|
|
20681
21014
|
const oldTitle = getTitleFromPath(oldPath);
|
|
@@ -20735,9 +21068,9 @@ function registerMoveNoteTools(server2, getVaultPath) {
|
|
|
20735
21068
|
};
|
|
20736
21069
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
20737
21070
|
}
|
|
20738
|
-
const destDir =
|
|
20739
|
-
await
|
|
20740
|
-
await
|
|
21071
|
+
const destDir = path28.dirname(newFullPath);
|
|
21072
|
+
await fs26.mkdir(destDir, { recursive: true });
|
|
21073
|
+
await fs26.rename(oldFullPath, newFullPath);
|
|
20741
21074
|
let gitCommit;
|
|
20742
21075
|
let undoAvailable;
|
|
20743
21076
|
let staleLockDetected;
|
|
@@ -20811,12 +21144,12 @@ function registerMoveNoteTools(server2, getVaultPath) {
|
|
|
20811
21144
|
if (sanitizedTitle !== newTitle) {
|
|
20812
21145
|
console.error(`[Flywheel] Title sanitized: "${newTitle}" \u2192 "${sanitizedTitle}"`);
|
|
20813
21146
|
}
|
|
20814
|
-
const fullPath =
|
|
20815
|
-
const dir =
|
|
20816
|
-
const newPath = dir === "." ? `${sanitizedTitle}.md` :
|
|
20817
|
-
const newFullPath =
|
|
21147
|
+
const fullPath = path28.join(vaultPath2, notePath);
|
|
21148
|
+
const dir = path28.dirname(notePath);
|
|
21149
|
+
const newPath = dir === "." ? `${sanitizedTitle}.md` : path28.join(dir, `${sanitizedTitle}.md`);
|
|
21150
|
+
const newFullPath = path28.join(vaultPath2, newPath);
|
|
20818
21151
|
try {
|
|
20819
|
-
await
|
|
21152
|
+
await fs26.access(fullPath);
|
|
20820
21153
|
} catch {
|
|
20821
21154
|
const result2 = {
|
|
20822
21155
|
success: false,
|
|
@@ -20827,7 +21160,7 @@ function registerMoveNoteTools(server2, getVaultPath) {
|
|
|
20827
21160
|
}
|
|
20828
21161
|
if (fullPath !== newFullPath) {
|
|
20829
21162
|
try {
|
|
20830
|
-
await
|
|
21163
|
+
await fs26.access(newFullPath);
|
|
20831
21164
|
const result2 = {
|
|
20832
21165
|
success: false,
|
|
20833
21166
|
message: `A note with this title already exists: ${newPath}`,
|
|
@@ -20837,7 +21170,7 @@ function registerMoveNoteTools(server2, getVaultPath) {
|
|
|
20837
21170
|
} catch {
|
|
20838
21171
|
}
|
|
20839
21172
|
}
|
|
20840
|
-
const sourceContent = await
|
|
21173
|
+
const sourceContent = await fs26.readFile(fullPath, "utf-8");
|
|
20841
21174
|
const parsed = matter6(sourceContent);
|
|
20842
21175
|
const aliases = extractAliases2(parsed.data);
|
|
20843
21176
|
const oldTitle = getTitleFromPath(notePath);
|
|
@@ -20897,7 +21230,7 @@ function registerMoveNoteTools(server2, getVaultPath) {
|
|
|
20897
21230
|
return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
|
|
20898
21231
|
}
|
|
20899
21232
|
if (fullPath !== newFullPath) {
|
|
20900
|
-
await
|
|
21233
|
+
await fs26.rename(fullPath, newFullPath);
|
|
20901
21234
|
}
|
|
20902
21235
|
let gitCommit;
|
|
20903
21236
|
let undoAvailable;
|
|
@@ -20945,8 +21278,8 @@ function registerMoveNoteTools(server2, getVaultPath) {
|
|
|
20945
21278
|
init_writer();
|
|
20946
21279
|
init_wikilinks();
|
|
20947
21280
|
import { z as z17 } from "zod";
|
|
20948
|
-
import
|
|
20949
|
-
import
|
|
21281
|
+
import fs27 from "fs/promises";
|
|
21282
|
+
import path29 from "path";
|
|
20950
21283
|
function registerMergeTools(server2, getVaultPath) {
|
|
20951
21284
|
server2.tool(
|
|
20952
21285
|
"merge_entities",
|
|
@@ -21073,7 +21406,7 @@ ${trimmedSource}`;
|
|
|
21073
21406
|
}
|
|
21074
21407
|
await writeVaultFile(vaultPath2, target_path, targetContent, targetFrontmatter, "LF", targetContentHash);
|
|
21075
21408
|
const fullSourcePath = `${vaultPath2}/${source_path}`;
|
|
21076
|
-
await
|
|
21409
|
+
await fs27.unlink(fullSourcePath);
|
|
21077
21410
|
initializeEntityIndex(vaultPath2).catch((err) => {
|
|
21078
21411
|
console.error(`[Flywheel] Entity cache rebuild failed: ${err}`);
|
|
21079
21412
|
});
|
|
@@ -21188,7 +21521,7 @@ ${trimmedSource}`;
|
|
|
21188
21521
|
}
|
|
21189
21522
|
let sourceDeleted = false;
|
|
21190
21523
|
if (sourceNoteFile) {
|
|
21191
|
-
await
|
|
21524
|
+
await fs27.unlink(`${vaultPath2}/${sourceNoteFile}`);
|
|
21192
21525
|
sourceDeleted = true;
|
|
21193
21526
|
}
|
|
21194
21527
|
initializeEntityIndex(vaultPath2).catch((err) => {
|
|
@@ -21237,21 +21570,21 @@ async function findSourceNote(vaultPath2, sourceName, excludePath) {
|
|
|
21237
21570
|
async function scanDir(dir) {
|
|
21238
21571
|
let entries;
|
|
21239
21572
|
try {
|
|
21240
|
-
entries = await
|
|
21573
|
+
entries = await fs27.readdir(dir, { withFileTypes: true });
|
|
21241
21574
|
} catch {
|
|
21242
21575
|
return null;
|
|
21243
21576
|
}
|
|
21244
21577
|
for (const entry of entries) {
|
|
21245
21578
|
if (entry.name.startsWith(".")) continue;
|
|
21246
|
-
const fullPath =
|
|
21579
|
+
const fullPath = path29.join(dir, entry.name);
|
|
21247
21580
|
if (entry.isDirectory()) {
|
|
21248
21581
|
const found = await scanDir(fullPath);
|
|
21249
21582
|
if (found) return found;
|
|
21250
21583
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
21251
|
-
const basename5 =
|
|
21584
|
+
const basename5 = path29.basename(entry.name, ".md");
|
|
21252
21585
|
if (basename5.toLowerCase() === targetLower) {
|
|
21253
|
-
const
|
|
21254
|
-
if (
|
|
21586
|
+
const relative2 = path29.relative(vaultPath2, fullPath).replace(/\\/g, "/");
|
|
21587
|
+
if (relative2 !== excludePath) return relative2;
|
|
21255
21588
|
}
|
|
21256
21589
|
}
|
|
21257
21590
|
}
|
|
@@ -21371,7 +21704,7 @@ Message: ${undoResult.undoneCommit.message}` : void 0
|
|
|
21371
21704
|
}
|
|
21372
21705
|
|
|
21373
21706
|
// src/tools/write/policy.ts
|
|
21374
|
-
import * as
|
|
21707
|
+
import * as path34 from "path";
|
|
21375
21708
|
import { z as z20 } from "zod";
|
|
21376
21709
|
|
|
21377
21710
|
// src/core/write/policy/index.ts
|
|
@@ -21380,8 +21713,8 @@ init_schema();
|
|
|
21380
21713
|
|
|
21381
21714
|
// src/core/write/policy/parser.ts
|
|
21382
21715
|
init_schema();
|
|
21383
|
-
import
|
|
21384
|
-
import
|
|
21716
|
+
import fs28 from "fs/promises";
|
|
21717
|
+
import path30 from "path";
|
|
21385
21718
|
import matter7 from "gray-matter";
|
|
21386
21719
|
function parseYaml(content) {
|
|
21387
21720
|
const parsed = matter7(`---
|
|
@@ -21406,7 +21739,7 @@ function parsePolicyString(yamlContent) {
|
|
|
21406
21739
|
}
|
|
21407
21740
|
async function loadPolicyFile(filePath) {
|
|
21408
21741
|
try {
|
|
21409
|
-
const content = await
|
|
21742
|
+
const content = await fs28.readFile(filePath, "utf-8");
|
|
21410
21743
|
return parsePolicyString(content);
|
|
21411
21744
|
} catch (error) {
|
|
21412
21745
|
if (error.code === "ENOENT") {
|
|
@@ -21432,14 +21765,14 @@ async function loadPolicyFile(filePath) {
|
|
|
21432
21765
|
async function loadPolicy(vaultPath2, policyName) {
|
|
21433
21766
|
await ensureMigrated(vaultPath2);
|
|
21434
21767
|
const policiesDir = getPoliciesDir(vaultPath2);
|
|
21435
|
-
const policyPath =
|
|
21768
|
+
const policyPath = path30.join(policiesDir, `${policyName}.yaml`);
|
|
21436
21769
|
try {
|
|
21437
|
-
await
|
|
21770
|
+
await fs28.access(policyPath);
|
|
21438
21771
|
return loadPolicyFile(policyPath);
|
|
21439
21772
|
} catch {
|
|
21440
|
-
const ymlPath =
|
|
21773
|
+
const ymlPath = path30.join(policiesDir, `${policyName}.yml`);
|
|
21441
21774
|
try {
|
|
21442
|
-
await
|
|
21775
|
+
await fs28.access(ymlPath);
|
|
21443
21776
|
return loadPolicyFile(ymlPath);
|
|
21444
21777
|
} catch {
|
|
21445
21778
|
return {
|
|
@@ -21579,8 +21912,8 @@ init_schema();
|
|
|
21579
21912
|
init_writer();
|
|
21580
21913
|
init_git();
|
|
21581
21914
|
init_wikilinks();
|
|
21582
|
-
import
|
|
21583
|
-
import
|
|
21915
|
+
import fs30 from "fs/promises";
|
|
21916
|
+
import path32 from "path";
|
|
21584
21917
|
init_constants2();
|
|
21585
21918
|
async function executeStep(step, vaultPath2, context, conditionResults, searchFn) {
|
|
21586
21919
|
const { execute, reason } = shouldStepExecute(step.when, conditionResults);
|
|
@@ -21771,12 +22104,12 @@ async function executeCreateNote2(params, vaultPath2, context) {
|
|
|
21771
22104
|
let frontmatter = params.frontmatter || {};
|
|
21772
22105
|
if (params.template) {
|
|
21773
22106
|
try {
|
|
21774
|
-
const templatePath =
|
|
21775
|
-
const raw = await
|
|
22107
|
+
const templatePath = path32.join(vaultPath2, String(params.template));
|
|
22108
|
+
const raw = await fs30.readFile(templatePath, "utf-8");
|
|
21776
22109
|
const matter9 = (await import("gray-matter")).default;
|
|
21777
22110
|
const parsed = matter9(raw);
|
|
21778
22111
|
const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
21779
|
-
const title =
|
|
22112
|
+
const title = path32.basename(notePath, ".md");
|
|
21780
22113
|
let templateContent = parsed.content.replace(/\{\{date\}\}/g, dateStr).replace(/\{\{title\}\}/g, title);
|
|
21781
22114
|
if (content) {
|
|
21782
22115
|
templateContent = templateContent.trimEnd() + "\n\n" + content;
|
|
@@ -21809,9 +22142,9 @@ async function executeToggleTask(params, vaultPath2) {
|
|
|
21809
22142
|
const notePath = String(params.path || "");
|
|
21810
22143
|
const task = String(params.task || "");
|
|
21811
22144
|
const section = params.section ? String(params.section) : void 0;
|
|
21812
|
-
const fullPath =
|
|
22145
|
+
const fullPath = path32.join(vaultPath2, notePath);
|
|
21813
22146
|
try {
|
|
21814
|
-
await
|
|
22147
|
+
await fs30.access(fullPath);
|
|
21815
22148
|
} catch {
|
|
21816
22149
|
return { success: false, message: `File not found: ${notePath}`, path: notePath };
|
|
21817
22150
|
}
|
|
@@ -22092,15 +22425,15 @@ async function rollbackChanges(vaultPath2, originalContents, filesModified) {
|
|
|
22092
22425
|
const pathCheck = await validatePathSecure(vaultPath2, filePath);
|
|
22093
22426
|
if (!pathCheck.valid) continue;
|
|
22094
22427
|
const original = originalContents.get(filePath);
|
|
22095
|
-
const fullPath =
|
|
22428
|
+
const fullPath = path32.join(vaultPath2, filePath);
|
|
22096
22429
|
if (original === null) {
|
|
22097
22430
|
try {
|
|
22098
|
-
await
|
|
22431
|
+
await fs30.unlink(fullPath);
|
|
22099
22432
|
} catch {
|
|
22100
22433
|
}
|
|
22101
22434
|
} else if (original !== void 0) {
|
|
22102
22435
|
try {
|
|
22103
|
-
await
|
|
22436
|
+
await fs30.writeFile(fullPath, original);
|
|
22104
22437
|
} catch {
|
|
22105
22438
|
}
|
|
22106
22439
|
}
|
|
@@ -22146,27 +22479,27 @@ async function previewPolicy(policy, vaultPath2, variables) {
|
|
|
22146
22479
|
}
|
|
22147
22480
|
|
|
22148
22481
|
// src/core/write/policy/storage.ts
|
|
22149
|
-
import
|
|
22150
|
-
import
|
|
22482
|
+
import fs31 from "fs/promises";
|
|
22483
|
+
import path33 from "path";
|
|
22151
22484
|
async function listPolicies(vaultPath2) {
|
|
22152
22485
|
await ensureMigrated(vaultPath2);
|
|
22153
22486
|
const dir = getPoliciesDir(vaultPath2);
|
|
22154
22487
|
const policies = [];
|
|
22155
22488
|
try {
|
|
22156
|
-
const files = await
|
|
22489
|
+
const files = await fs31.readdir(dir);
|
|
22157
22490
|
for (const file of files) {
|
|
22158
22491
|
if (!file.endsWith(".yaml") && !file.endsWith(".yml")) {
|
|
22159
22492
|
continue;
|
|
22160
22493
|
}
|
|
22161
|
-
const filePath =
|
|
22162
|
-
const
|
|
22163
|
-
const content = await
|
|
22494
|
+
const filePath = path33.join(dir, file);
|
|
22495
|
+
const stat4 = await fs31.stat(filePath);
|
|
22496
|
+
const content = await fs31.readFile(filePath, "utf-8");
|
|
22164
22497
|
const metadata = extractPolicyMetadata(content);
|
|
22165
22498
|
policies.push({
|
|
22166
22499
|
name: metadata.name || file.replace(/\.ya?ml$/, ""),
|
|
22167
22500
|
description: metadata.description || "No description",
|
|
22168
22501
|
path: file,
|
|
22169
|
-
lastModified:
|
|
22502
|
+
lastModified: stat4.mtime,
|
|
22170
22503
|
version: metadata.version || "1.0",
|
|
22171
22504
|
requiredVariables: metadata.variables || []
|
|
22172
22505
|
});
|
|
@@ -22184,10 +22517,10 @@ async function writePolicyRaw(vaultPath2, policyName, content, overwrite = false
|
|
|
22184
22517
|
const dir = getPoliciesDir(vaultPath2);
|
|
22185
22518
|
await ensurePoliciesDir(vaultPath2);
|
|
22186
22519
|
const filename = `${policyName}.yaml`;
|
|
22187
|
-
const filePath =
|
|
22520
|
+
const filePath = path33.join(dir, filename);
|
|
22188
22521
|
if (!overwrite) {
|
|
22189
22522
|
try {
|
|
22190
|
-
await
|
|
22523
|
+
await fs31.access(filePath);
|
|
22191
22524
|
return {
|
|
22192
22525
|
success: false,
|
|
22193
22526
|
path: filename,
|
|
@@ -22204,7 +22537,7 @@ async function writePolicyRaw(vaultPath2, policyName, content, overwrite = false
|
|
|
22204
22537
|
message: `Invalid policy: ${validation.errors.map((e) => e.message).join("; ")}`
|
|
22205
22538
|
};
|
|
22206
22539
|
}
|
|
22207
|
-
await
|
|
22540
|
+
await fs31.writeFile(filePath, content, "utf-8");
|
|
22208
22541
|
return {
|
|
22209
22542
|
success: true,
|
|
22210
22543
|
path: filename,
|
|
@@ -22298,7 +22631,7 @@ function registerPolicyTools(server2, getVaultPath, getSearchFn) {
|
|
|
22298
22631
|
const policies = await listPolicies(vaultPath2);
|
|
22299
22632
|
const response = {
|
|
22300
22633
|
success: true,
|
|
22301
|
-
vault:
|
|
22634
|
+
vault: path34.basename(vaultPath2),
|
|
22302
22635
|
vault_path: vaultPath2,
|
|
22303
22636
|
count: policies.length,
|
|
22304
22637
|
policies: policies.map((p) => ({
|
|
@@ -22731,8 +23064,8 @@ function registerPolicyTools(server2, getVaultPath, getSearchFn) {
|
|
|
22731
23064
|
import { z as z21 } from "zod";
|
|
22732
23065
|
|
|
22733
23066
|
// src/core/write/tagRename.ts
|
|
22734
|
-
import * as
|
|
22735
|
-
import * as
|
|
23067
|
+
import * as fs32 from "fs/promises";
|
|
23068
|
+
import * as path35 from "path";
|
|
22736
23069
|
import matter8 from "gray-matter";
|
|
22737
23070
|
import { getProtectedZones as getProtectedZones2 } from "@velvetmonkey/vault-core";
|
|
22738
23071
|
function getNotesInFolder3(index, folder) {
|
|
@@ -22838,10 +23171,10 @@ async function renameTag(index, vaultPath2, oldTag, newTag, options) {
|
|
|
22838
23171
|
const previews = [];
|
|
22839
23172
|
let totalChanges = 0;
|
|
22840
23173
|
for (const note of affectedNotes) {
|
|
22841
|
-
const fullPath =
|
|
23174
|
+
const fullPath = path35.join(vaultPath2, note.path);
|
|
22842
23175
|
let fileContent;
|
|
22843
23176
|
try {
|
|
22844
|
-
fileContent = await
|
|
23177
|
+
fileContent = await fs32.readFile(fullPath, "utf-8");
|
|
22845
23178
|
} catch {
|
|
22846
23179
|
continue;
|
|
22847
23180
|
}
|
|
@@ -22914,7 +23247,7 @@ async function renameTag(index, vaultPath2, oldTag, newTag, options) {
|
|
|
22914
23247
|
previews.push(preview);
|
|
22915
23248
|
if (!dryRun) {
|
|
22916
23249
|
const newContent = matter8.stringify(updatedContent, fm);
|
|
22917
|
-
await
|
|
23250
|
+
await fs32.writeFile(fullPath, newContent, "utf-8");
|
|
22918
23251
|
}
|
|
22919
23252
|
}
|
|
22920
23253
|
}
|
|
@@ -23916,9 +24249,9 @@ function registerConfigTools(server2, getConfig2, setConfig, getStateDb4) {
|
|
|
23916
24249
|
async ({ mode, key, value }) => {
|
|
23917
24250
|
switch (mode) {
|
|
23918
24251
|
case "get": {
|
|
23919
|
-
const
|
|
24252
|
+
const config2 = getConfig2();
|
|
23920
24253
|
return {
|
|
23921
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
24254
|
+
content: [{ type: "text", text: JSON.stringify(config2, null, 2) }]
|
|
23922
24255
|
};
|
|
23923
24256
|
}
|
|
23924
24257
|
case "set": {
|
|
@@ -23967,9 +24300,9 @@ function registerConfigTools(server2, getConfig2, setConfig, getStateDb4) {
|
|
|
23967
24300
|
init_wikilinks();
|
|
23968
24301
|
init_wikilinkFeedback();
|
|
23969
24302
|
import { z as z28 } from "zod";
|
|
23970
|
-
import * as
|
|
23971
|
-
import * as
|
|
23972
|
-
import { scanVaultEntities as
|
|
24303
|
+
import * as fs33 from "fs/promises";
|
|
24304
|
+
import * as path36 from "path";
|
|
24305
|
+
import { scanVaultEntities as scanVaultEntities4, SCHEMA_VERSION as SCHEMA_VERSION2 } from "@velvetmonkey/vault-core";
|
|
23973
24306
|
init_embeddings();
|
|
23974
24307
|
function hasSkipWikilinks(content) {
|
|
23975
24308
|
if (!content.startsWith("---")) return false;
|
|
@@ -23981,16 +24314,16 @@ function hasSkipWikilinks(content) {
|
|
|
23981
24314
|
async function collectMarkdownFiles(dirPath, basePath, excludeFolders) {
|
|
23982
24315
|
const results = [];
|
|
23983
24316
|
try {
|
|
23984
|
-
const entries = await
|
|
24317
|
+
const entries = await fs33.readdir(dirPath, { withFileTypes: true });
|
|
23985
24318
|
for (const entry of entries) {
|
|
23986
24319
|
if (entry.name.startsWith(".")) continue;
|
|
23987
|
-
const fullPath =
|
|
24320
|
+
const fullPath = path36.join(dirPath, entry.name);
|
|
23988
24321
|
if (entry.isDirectory()) {
|
|
23989
24322
|
if (excludeFolders.some((f) => entry.name.toLowerCase() === f.toLowerCase())) continue;
|
|
23990
24323
|
const sub = await collectMarkdownFiles(fullPath, basePath, excludeFolders);
|
|
23991
24324
|
results.push(...sub);
|
|
23992
24325
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
23993
|
-
results.push(
|
|
24326
|
+
results.push(path36.relative(basePath, fullPath));
|
|
23994
24327
|
}
|
|
23995
24328
|
}
|
|
23996
24329
|
} catch {
|
|
@@ -24020,7 +24353,7 @@ var EXCLUDE_FOLDERS = [
|
|
|
24020
24353
|
];
|
|
24021
24354
|
function buildStatusReport(stateDb2, vaultPath2) {
|
|
24022
24355
|
const recommendations = [];
|
|
24023
|
-
const dbPath =
|
|
24356
|
+
const dbPath = path36.join(vaultPath2, ".flywheel", "state.db");
|
|
24024
24357
|
const statedbExists = stateDb2 !== null;
|
|
24025
24358
|
if (!statedbExists) {
|
|
24026
24359
|
recommendations.push("StateDb not initialized \u2014 server needs restart");
|
|
@@ -24087,8 +24420,8 @@ async function executeRun(stateDb2, vaultPath2) {
|
|
|
24087
24420
|
if (entityCount === 0) {
|
|
24088
24421
|
const start = Date.now();
|
|
24089
24422
|
try {
|
|
24090
|
-
const
|
|
24091
|
-
const entityIndex2 = await
|
|
24423
|
+
const config2 = loadConfig(stateDb2);
|
|
24424
|
+
const entityIndex2 = await scanVaultEntities4(vaultPath2, { excludeFolders: EXCLUDE_FOLDERS, customCategories: config2.custom_categories });
|
|
24092
24425
|
stateDb2.replaceAllEntities(entityIndex2);
|
|
24093
24426
|
const newCount = entityIndex2._metadata.total_entities;
|
|
24094
24427
|
steps.push({
|
|
@@ -24147,10 +24480,10 @@ async function executeRun(stateDb2, vaultPath2) {
|
|
|
24147
24480
|
const allFiles = await collectMarkdownFiles(vaultPath2, vaultPath2, EXCLUDE_FOLDERS);
|
|
24148
24481
|
let eligible = 0;
|
|
24149
24482
|
for (const relativePath of allFiles) {
|
|
24150
|
-
const fullPath =
|
|
24483
|
+
const fullPath = path36.join(vaultPath2, relativePath);
|
|
24151
24484
|
let content;
|
|
24152
24485
|
try {
|
|
24153
|
-
content = await
|
|
24486
|
+
content = await fs33.readFile(fullPath, "utf-8");
|
|
24154
24487
|
} catch {
|
|
24155
24488
|
continue;
|
|
24156
24489
|
}
|
|
@@ -24205,10 +24538,10 @@ async function executeEnrich(stateDb2, vaultPath2, dryRun, batchSize, offset) {
|
|
|
24205
24538
|
const eligible = [];
|
|
24206
24539
|
let notesSkipped = 0;
|
|
24207
24540
|
for (const relativePath of allFiles) {
|
|
24208
|
-
const fullPath =
|
|
24541
|
+
const fullPath = path36.join(vaultPath2, relativePath);
|
|
24209
24542
|
let content;
|
|
24210
24543
|
try {
|
|
24211
|
-
content = await
|
|
24544
|
+
content = await fs33.readFile(fullPath, "utf-8");
|
|
24212
24545
|
} catch {
|
|
24213
24546
|
continue;
|
|
24214
24547
|
}
|
|
@@ -24235,8 +24568,8 @@ async function executeEnrich(stateDb2, vaultPath2, dryRun, batchSize, offset) {
|
|
|
24235
24568
|
match_count: result.linksAdded
|
|
24236
24569
|
});
|
|
24237
24570
|
if (!dryRun) {
|
|
24238
|
-
const fullPath =
|
|
24239
|
-
await
|
|
24571
|
+
const fullPath = path36.join(vaultPath2, relativePath);
|
|
24572
|
+
await fs33.writeFile(fullPath, result.content, "utf-8");
|
|
24240
24573
|
notesModified++;
|
|
24241
24574
|
if (stateDb2) {
|
|
24242
24575
|
trackWikilinkApplications(stateDb2, relativePath, entities, "enrichment");
|
|
@@ -24413,8 +24746,8 @@ function registerMetricsTools(server2, getIndex, getStateDb4) {
|
|
|
24413
24746
|
import { z as z30 } from "zod";
|
|
24414
24747
|
|
|
24415
24748
|
// src/core/read/similarity.ts
|
|
24416
|
-
import * as
|
|
24417
|
-
import * as
|
|
24749
|
+
import * as fs34 from "fs";
|
|
24750
|
+
import * as path37 from "path";
|
|
24418
24751
|
init_embeddings();
|
|
24419
24752
|
|
|
24420
24753
|
// src/core/read/mmr.ts
|
|
@@ -24484,10 +24817,10 @@ function extractKeyTerms(content, maxTerms = 15) {
|
|
|
24484
24817
|
}
|
|
24485
24818
|
function findSimilarNotes(db4, vaultPath2, index, sourcePath, options = {}) {
|
|
24486
24819
|
const limit = options.limit ?? 10;
|
|
24487
|
-
const absPath =
|
|
24820
|
+
const absPath = path37.join(vaultPath2, sourcePath);
|
|
24488
24821
|
let content;
|
|
24489
24822
|
try {
|
|
24490
|
-
content =
|
|
24823
|
+
content = fs34.readFileSync(absPath, "utf-8");
|
|
24491
24824
|
} catch {
|
|
24492
24825
|
return [];
|
|
24493
24826
|
}
|
|
@@ -24626,7 +24959,7 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
24626
24959
|
diversity: z30.number().min(0).max(1).optional().describe("Relevance vs diversity tradeoff (0=max diversity, 1=pure relevance, default: 0.7)")
|
|
24627
24960
|
}
|
|
24628
24961
|
},
|
|
24629
|
-
async ({ path:
|
|
24962
|
+
async ({ path: path40, limit, diversity }) => {
|
|
24630
24963
|
const index = getIndex();
|
|
24631
24964
|
const vaultPath2 = getVaultPath();
|
|
24632
24965
|
const stateDb2 = getStateDb4();
|
|
@@ -24635,10 +24968,10 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
24635
24968
|
content: [{ type: "text", text: JSON.stringify({ error: "StateDb not available" }) }]
|
|
24636
24969
|
};
|
|
24637
24970
|
}
|
|
24638
|
-
if (!index.notes.has(
|
|
24971
|
+
if (!index.notes.has(path40)) {
|
|
24639
24972
|
return {
|
|
24640
24973
|
content: [{ type: "text", text: JSON.stringify({
|
|
24641
|
-
error: `Note not found: ${
|
|
24974
|
+
error: `Note not found: ${path40}`,
|
|
24642
24975
|
hint: "Use the full relative path including .md extension"
|
|
24643
24976
|
}, null, 2) }]
|
|
24644
24977
|
};
|
|
@@ -24650,12 +24983,12 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
24650
24983
|
};
|
|
24651
24984
|
const useHybrid = hasEmbeddingsIndex();
|
|
24652
24985
|
const method = useHybrid ? "hybrid" : "bm25";
|
|
24653
|
-
const results = useHybrid ? await findHybridSimilarNotes(stateDb2.db, vaultPath2, index,
|
|
24986
|
+
const results = useHybrid ? await findHybridSimilarNotes(stateDb2.db, vaultPath2, index, path40, opts) : findSimilarNotes(stateDb2.db, vaultPath2, index, path40, opts);
|
|
24654
24987
|
return {
|
|
24655
24988
|
content: [{
|
|
24656
24989
|
type: "text",
|
|
24657
24990
|
text: JSON.stringify({
|
|
24658
|
-
source:
|
|
24991
|
+
source: path40,
|
|
24659
24992
|
method,
|
|
24660
24993
|
count: results.length,
|
|
24661
24994
|
similar: results
|
|
@@ -24669,7 +25002,7 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb4) {
|
|
|
24669
25002
|
// src/tools/read/semantic.ts
|
|
24670
25003
|
init_embeddings();
|
|
24671
25004
|
import { z as z31 } from "zod";
|
|
24672
|
-
import { getAllEntitiesFromDb as
|
|
25005
|
+
import { getAllEntitiesFromDb as getAllEntitiesFromDb4 } from "@velvetmonkey/vault-core";
|
|
24673
25006
|
function registerSemanticTools(server2, getVaultPath, getStateDb4) {
|
|
24674
25007
|
server2.registerTool(
|
|
24675
25008
|
"init_semantic",
|
|
@@ -24734,7 +25067,7 @@ function registerSemanticTools(server2, getVaultPath, getStateDb4) {
|
|
|
24734
25067
|
const embedded = progress.total - progress.skipped;
|
|
24735
25068
|
let entityEmbedded = 0;
|
|
24736
25069
|
try {
|
|
24737
|
-
const allEntities =
|
|
25070
|
+
const allEntities = getAllEntitiesFromDb4(stateDb2);
|
|
24738
25071
|
const entityMap = /* @__PURE__ */ new Map();
|
|
24739
25072
|
for (const e of allEntities) {
|
|
24740
25073
|
entityMap.set(e.name, {
|
|
@@ -24796,7 +25129,7 @@ function registerSemanticTools(server2, getVaultPath, getStateDb4) {
|
|
|
24796
25129
|
// src/tools/read/merges.ts
|
|
24797
25130
|
init_levenshtein();
|
|
24798
25131
|
import { z as z32 } from "zod";
|
|
24799
|
-
import { getAllEntitiesFromDb as
|
|
25132
|
+
import { getAllEntitiesFromDb as getAllEntitiesFromDb5, getDismissedMergePairs, recordMergeDismissal } from "@velvetmonkey/vault-core";
|
|
24800
25133
|
function normalizeName(name) {
|
|
24801
25134
|
return name.toLowerCase().replace(/[.\-_]/g, "").replace(/js$/, "").replace(/ts$/, "");
|
|
24802
25135
|
}
|
|
@@ -24814,7 +25147,7 @@ function registerMergeTools2(server2, getStateDb4) {
|
|
|
24814
25147
|
content: [{ type: "text", text: JSON.stringify({ suggestions: [], error: "StateDb not available" }) }]
|
|
24815
25148
|
};
|
|
24816
25149
|
}
|
|
24817
|
-
const entities =
|
|
25150
|
+
const entities = getAllEntitiesFromDb5(stateDb2);
|
|
24818
25151
|
if (entities.length === 0) {
|
|
24819
25152
|
return {
|
|
24820
25153
|
content: [{ type: "text", text: JSON.stringify({ suggestions: [] }) }]
|
|
@@ -26297,7 +26630,7 @@ function queryFlywheelAgeDays(stateDb2) {
|
|
|
26297
26630
|
if (!row?.first_ts) return 0;
|
|
26298
26631
|
return Math.floor((Date.now() - row.first_ts) / (24 * 60 * 60 * 1e3));
|
|
26299
26632
|
}
|
|
26300
|
-
function getCalibrationExport(stateDb2, metrics,
|
|
26633
|
+
function getCalibrationExport(stateDb2, metrics, config2, daysBack = 30, includeVaultId = true) {
|
|
26301
26634
|
const now = /* @__PURE__ */ new Date();
|
|
26302
26635
|
const start = new Date(now);
|
|
26303
26636
|
start.setDate(start.getDate() - daysBack + 1);
|
|
@@ -26320,8 +26653,8 @@ function getCalibrationExport(stateDb2, metrics, config, daysBack = 30, includeV
|
|
|
26320
26653
|
connected_ratio: round(metrics.connected_ratio),
|
|
26321
26654
|
semantic_enabled: hasEmbeddingsIndex(),
|
|
26322
26655
|
flywheel_age_days: queryFlywheelAgeDays(stateDb2),
|
|
26323
|
-
strictness_mode:
|
|
26324
|
-
adaptive_strictness:
|
|
26656
|
+
strictness_mode: config2.wikilink_strictness ?? "balanced",
|
|
26657
|
+
adaptive_strictness: config2.adaptive_strictness ?? true
|
|
26325
26658
|
},
|
|
26326
26659
|
entity_distribution: queryEntityDistribution(stateDb2),
|
|
26327
26660
|
funnel: queryFunnel2(stateDb2, startMs, startIso, endIso),
|
|
@@ -26355,11 +26688,11 @@ function registerCalibrationExportTools(server2, getIndex, getStateDb4, getConfi
|
|
|
26355
26688
|
}
|
|
26356
26689
|
const index = getIndex();
|
|
26357
26690
|
const metrics = computeMetrics(index, stateDb2);
|
|
26358
|
-
const
|
|
26691
|
+
const config2 = getConfig2();
|
|
26359
26692
|
const report = getCalibrationExport(
|
|
26360
26693
|
stateDb2,
|
|
26361
26694
|
metrics,
|
|
26362
|
-
|
|
26695
|
+
config2,
|
|
26363
26696
|
args.days_back ?? 30,
|
|
26364
26697
|
args.include_vault_id ?? true
|
|
26365
26698
|
);
|
|
@@ -26529,7 +26862,7 @@ function registerVaultResources(server2, getIndex) {
|
|
|
26529
26862
|
}
|
|
26530
26863
|
|
|
26531
26864
|
// src/tool-registry.ts
|
|
26532
|
-
var __trFilename =
|
|
26865
|
+
var __trFilename = fileURLToPath2(import.meta.url);
|
|
26533
26866
|
var __trDirname = dirname5(__trFilename);
|
|
26534
26867
|
var trPkg = JSON.parse(readFileSync5(join18(__trDirname, "../package.json"), "utf-8"));
|
|
26535
26868
|
var ACTIVATION_PATTERNS = [
|
|
@@ -26613,7 +26946,7 @@ function extractSearchMethod(result) {
|
|
|
26613
26946
|
}
|
|
26614
26947
|
return void 0;
|
|
26615
26948
|
}
|
|
26616
|
-
function applyToolGating(targetServer, categories, getDb4, registry, getVaultPath, vaultCallbacks, tierMode = "off", onTierStateChange, isFullToolset = false) {
|
|
26949
|
+
function applyToolGating(targetServer, categories, getDb4, registry, getVaultPath, vaultCallbacks, tierMode = "off", onTierStateChange, isFullToolset = false, onToolCall) {
|
|
26617
26950
|
let _registered = 0;
|
|
26618
26951
|
let _skipped = 0;
|
|
26619
26952
|
let tierOverride = "auto";
|
|
@@ -26706,6 +27039,7 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
26706
27039
|
}
|
|
26707
27040
|
function wrapWithTracking(toolName, handler) {
|
|
26708
27041
|
return async (...args) => {
|
|
27042
|
+
onToolCall?.();
|
|
26709
27043
|
const start = Date.now();
|
|
26710
27044
|
let success = true;
|
|
26711
27045
|
let notePaths;
|
|
@@ -26760,7 +27094,7 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
26760
27094
|
let totalBytes = 0;
|
|
26761
27095
|
for (const p of notePaths) {
|
|
26762
27096
|
try {
|
|
26763
|
-
totalBytes += statSync6(
|
|
27097
|
+
totalBytes += statSync6(path38.join(vp, p)).size;
|
|
26764
27098
|
} catch {
|
|
26765
27099
|
}
|
|
26766
27100
|
}
|
|
@@ -26934,7 +27268,7 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
|
|
|
26934
27268
|
const serverAny = targetServer;
|
|
26935
27269
|
serverAny.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
|
|
26936
27270
|
try {
|
|
26937
|
-
const tool =
|
|
27271
|
+
const tool = toolHandles.get(request.params.name);
|
|
26938
27272
|
if (!tool) {
|
|
26939
27273
|
throw new McpError(ErrorCode.InvalidParams, `Tool ${request.params.name} not found`);
|
|
26940
27274
|
}
|
|
@@ -27111,8 +27445,8 @@ function registerAllTools(targetServer, ctx, controller) {
|
|
|
27111
27445
|
}
|
|
27112
27446
|
|
|
27113
27447
|
// src/index.ts
|
|
27114
|
-
var
|
|
27115
|
-
var __dirname = dirname7(
|
|
27448
|
+
var __filename2 = fileURLToPath3(import.meta.url);
|
|
27449
|
+
var __dirname = dirname7(__filename2);
|
|
27116
27450
|
var pkg = JSON.parse(readFileSync6(join20(__dirname, "../package.json"), "utf-8"));
|
|
27117
27451
|
var vaultPath;
|
|
27118
27452
|
var resolvedVaultPath;
|
|
@@ -27125,6 +27459,10 @@ var httpListener = null;
|
|
|
27125
27459
|
var watchdogTimer = null;
|
|
27126
27460
|
var serverReady = false;
|
|
27127
27461
|
var shutdownRequested = false;
|
|
27462
|
+
var lastMcpRequestAt = 0;
|
|
27463
|
+
var lastFullRebuildAt = 0;
|
|
27464
|
+
var startupScanFiles = null;
|
|
27465
|
+
var deferredScheduler = null;
|
|
27128
27466
|
function getWatcherStatus() {
|
|
27129
27467
|
if (vaultRegistry) {
|
|
27130
27468
|
const name = globalThis.__flywheel_active_vault;
|
|
@@ -27158,8 +27496,8 @@ function handleTierStateChange(controller) {
|
|
|
27158
27496
|
syncRuntimeTierState(controller);
|
|
27159
27497
|
invalidateHttpPool();
|
|
27160
27498
|
}
|
|
27161
|
-
function getConfigToolTierOverride(
|
|
27162
|
-
return
|
|
27499
|
+
function getConfigToolTierOverride(config2) {
|
|
27500
|
+
return config2.tool_tier_override ?? "auto";
|
|
27163
27501
|
}
|
|
27164
27502
|
function buildRegistryContext() {
|
|
27165
27503
|
return {
|
|
@@ -27191,7 +27529,10 @@ function createConfiguredServer() {
|
|
|
27191
27529
|
buildVaultCallbacks(),
|
|
27192
27530
|
toolTierMode,
|
|
27193
27531
|
handleTierStateChange,
|
|
27194
|
-
toolConfig.isFullToolset
|
|
27532
|
+
toolConfig.isFullToolset,
|
|
27533
|
+
() => {
|
|
27534
|
+
lastMcpRequestAt = Date.now();
|
|
27535
|
+
}
|
|
27195
27536
|
);
|
|
27196
27537
|
registerAllTools(s, ctx, toolTierController);
|
|
27197
27538
|
toolTierController.setOverride(runtimeToolTierOverride);
|
|
@@ -27245,7 +27586,10 @@ var _gatingResult = applyToolGating(
|
|
|
27245
27586
|
buildVaultCallbacks(),
|
|
27246
27587
|
toolTierMode,
|
|
27247
27588
|
handleTierStateChange,
|
|
27248
|
-
toolConfig.isFullToolset
|
|
27589
|
+
toolConfig.isFullToolset,
|
|
27590
|
+
() => {
|
|
27591
|
+
lastMcpRequestAt = Date.now();
|
|
27592
|
+
}
|
|
27249
27593
|
);
|
|
27250
27594
|
registerAllTools(server, _registryCtx, _gatingResult);
|
|
27251
27595
|
_gatingResult.setOverride(runtimeToolTierOverride);
|
|
@@ -27326,7 +27670,7 @@ function buildVaultScope(ctx) {
|
|
|
27326
27670
|
pipelineActivity: ctx.pipelineActivity
|
|
27327
27671
|
};
|
|
27328
27672
|
}
|
|
27329
|
-
function activateVault(ctx) {
|
|
27673
|
+
function activateVault(ctx, skipEmbeddingLoad = false) {
|
|
27330
27674
|
globalThis.__flywheel_active_vault = ctx.name;
|
|
27331
27675
|
if (ctx.stateDb) {
|
|
27332
27676
|
setWriteStateDb(ctx.stateDb);
|
|
@@ -27335,7 +27679,9 @@ function activateVault(ctx) {
|
|
|
27335
27679
|
setProspectStateDb(ctx.stateDb);
|
|
27336
27680
|
setTaskCacheDatabase(ctx.stateDb.db);
|
|
27337
27681
|
setEmbeddingsDatabase(ctx.stateDb.db);
|
|
27338
|
-
|
|
27682
|
+
if (!skipEmbeddingLoad) {
|
|
27683
|
+
loadEntityEmbeddingsToMemory();
|
|
27684
|
+
}
|
|
27339
27685
|
}
|
|
27340
27686
|
setWikilinkConfig(ctx.flywheelConfig);
|
|
27341
27687
|
setCooccurrenceIndex(ctx.cooccurrenceIndex);
|
|
@@ -27368,17 +27714,17 @@ function updateVaultIndex(index) {
|
|
|
27368
27714
|
const ctx = getActiveVaultContext();
|
|
27369
27715
|
if (ctx) ctx.vaultIndex = index;
|
|
27370
27716
|
}
|
|
27371
|
-
function updateFlywheelConfig(
|
|
27372
|
-
flywheelConfig =
|
|
27373
|
-
setWikilinkConfig(
|
|
27717
|
+
function updateFlywheelConfig(config2) {
|
|
27718
|
+
flywheelConfig = config2;
|
|
27719
|
+
setWikilinkConfig(config2);
|
|
27374
27720
|
if (toolTierMode === "tiered" && primaryToolTierController) {
|
|
27375
|
-
primaryToolTierController.setOverride(getConfigToolTierOverride(
|
|
27721
|
+
primaryToolTierController.setOverride(getConfigToolTierOverride(config2));
|
|
27376
27722
|
syncRuntimeTierState(primaryToolTierController);
|
|
27377
27723
|
invalidateHttpPool();
|
|
27378
27724
|
}
|
|
27379
27725
|
const ctx = getActiveVaultContext();
|
|
27380
27726
|
if (ctx) {
|
|
27381
|
-
ctx.flywheelConfig =
|
|
27727
|
+
ctx.flywheelConfig = config2;
|
|
27382
27728
|
setActiveScope(buildVaultScope(ctx));
|
|
27383
27729
|
}
|
|
27384
27730
|
}
|
|
@@ -27412,6 +27758,7 @@ async function bootVault(ctx, startTime) {
|
|
|
27412
27758
|
if (sd) {
|
|
27413
27759
|
try {
|
|
27414
27760
|
const files = await scanVault(vp);
|
|
27761
|
+
startupScanFiles = files;
|
|
27415
27762
|
const noteCount = files.length;
|
|
27416
27763
|
serverLog("index", `[${ctx.name}] Found ${noteCount} markdown files`);
|
|
27417
27764
|
const newestMtime = files.reduce((max, f) => f.modified > max ? f.modified : max, /* @__PURE__ */ new Date(0));
|
|
@@ -27433,6 +27780,7 @@ async function bootVault(ctx, startTime) {
|
|
|
27433
27780
|
note_count: cachedIndex.notes.size
|
|
27434
27781
|
});
|
|
27435
27782
|
}
|
|
27783
|
+
lastFullRebuildAt = Date.now();
|
|
27436
27784
|
await runPostIndexWork(ctx);
|
|
27437
27785
|
} else {
|
|
27438
27786
|
serverLog("index", `[${ctx.name}] Cache miss: building from scratch`);
|
|
@@ -27457,6 +27805,7 @@ async function bootVault(ctx, startTime) {
|
|
|
27457
27805
|
serverLog("index", `[${ctx.name}] Failed to save index cache: ${err instanceof Error ? err.message : err}`, "error");
|
|
27458
27806
|
}
|
|
27459
27807
|
}
|
|
27808
|
+
lastFullRebuildAt = Date.now();
|
|
27460
27809
|
await runPostIndexWork(ctx);
|
|
27461
27810
|
} catch (err) {
|
|
27462
27811
|
updateIndexState("error", err instanceof Error ? err : new Error(String(err)));
|
|
@@ -27495,13 +27844,13 @@ async function main() {
|
|
|
27495
27844
|
const primaryCtx2 = await initializeVault(vaultConfigs[0].name, vaultConfigs[0].path);
|
|
27496
27845
|
vaultRegistry.addContext(primaryCtx2);
|
|
27497
27846
|
stateDb = primaryCtx2.stateDb;
|
|
27498
|
-
activateVault(primaryCtx2);
|
|
27847
|
+
activateVault(primaryCtx2, true);
|
|
27499
27848
|
} else {
|
|
27500
27849
|
vaultRegistry = new VaultRegistry("default");
|
|
27501
27850
|
const ctx = await initializeVault("default", vaultPath);
|
|
27502
27851
|
vaultRegistry.addContext(ctx);
|
|
27503
27852
|
stateDb = ctx.stateDb;
|
|
27504
|
-
activateVault(ctx);
|
|
27853
|
+
activateVault(ctx, true);
|
|
27505
27854
|
}
|
|
27506
27855
|
await initToolRouting();
|
|
27507
27856
|
if (stateDb) {
|
|
@@ -27677,11 +28026,11 @@ async function updateEntitiesInStateDb(vp, sd) {
|
|
|
27677
28026
|
const vault = vp ?? vaultPath;
|
|
27678
28027
|
if (!db4) return;
|
|
27679
28028
|
try {
|
|
27680
|
-
const
|
|
27681
|
-
const excludeFolders =
|
|
27682
|
-
const entityIndex2 = await
|
|
28029
|
+
const config2 = loadConfig(db4);
|
|
28030
|
+
const excludeFolders = config2.exclude_entity_folders?.length ? config2.exclude_entity_folders : DEFAULT_ENTITY_EXCLUDE_FOLDERS;
|
|
28031
|
+
const entityIndex2 = await scanVaultEntities5(vault, {
|
|
27683
28032
|
excludeFolders,
|
|
27684
|
-
customCategories:
|
|
28033
|
+
customCategories: config2.custom_categories
|
|
27685
28034
|
});
|
|
27686
28035
|
db4.replaceAllEntities(entityIndex2);
|
|
27687
28036
|
serverLog("index", `Updated ${entityIndex2._metadata.total_entities} entities in StateDb`);
|
|
@@ -27689,37 +28038,11 @@ async function updateEntitiesInStateDb(vp, sd) {
|
|
|
27689
28038
|
serverLog("index", `Failed to update entities in StateDb: ${e instanceof Error ? e.message : e}`, "error");
|
|
27690
28039
|
}
|
|
27691
28040
|
}
|
|
27692
|
-
|
|
27693
|
-
|
|
27694
|
-
|
|
27695
|
-
let entries;
|
|
27696
|
-
try {
|
|
27697
|
-
entries = await fs34.readdir(dir, { withFileTypes: true });
|
|
27698
|
-
} catch {
|
|
27699
|
-
return;
|
|
27700
|
-
}
|
|
27701
|
-
for (const entry of entries) {
|
|
27702
|
-
const fullPath = path38.join(dir, entry.name);
|
|
27703
|
-
if (entry.isDirectory()) {
|
|
27704
|
-
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
27705
|
-
await scanDir(fullPath);
|
|
27706
|
-
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
27707
|
-
try {
|
|
27708
|
-
const stat5 = await fs34.stat(fullPath);
|
|
27709
|
-
if (stat5.mtimeMs > sinceMs) {
|
|
27710
|
-
events.push({
|
|
27711
|
-
type: "upsert",
|
|
27712
|
-
path: path38.relative(vaultPath2, fullPath),
|
|
27713
|
-
originalEvents: []
|
|
27714
|
-
});
|
|
27715
|
-
}
|
|
27716
|
-
} catch {
|
|
27717
|
-
}
|
|
27718
|
-
}
|
|
27719
|
-
}
|
|
28041
|
+
function buildStartupCatchupBatch(vaultPath2, sinceMs, preScannedFiles) {
|
|
28042
|
+
if (preScannedFiles) {
|
|
28043
|
+
return preScannedFiles.filter((f) => f.modified.getTime() > sinceMs).map((f) => ({ type: "upsert", path: f.path, originalEvents: [] }));
|
|
27720
28044
|
}
|
|
27721
|
-
|
|
27722
|
-
return events;
|
|
28045
|
+
return [];
|
|
27723
28046
|
}
|
|
27724
28047
|
var lastPurgeAt = Date.now();
|
|
27725
28048
|
function runPeriodicMaintenance(db4) {
|
|
@@ -27828,7 +28151,7 @@ async function runPostIndexWork(ctx) {
|
|
|
27828
28151
|
serverLog("semantic", "Embeddings up-to-date, skipping build");
|
|
27829
28152
|
loadEntityEmbeddingsToMemory();
|
|
27830
28153
|
if (sd) {
|
|
27831
|
-
const entities =
|
|
28154
|
+
const entities = getAllEntitiesFromDb6(sd);
|
|
27832
28155
|
if (entities.length > 0) {
|
|
27833
28156
|
saveInferredCategories(classifyUncategorizedEntities(
|
|
27834
28157
|
entities.map((entity) => ({
|
|
@@ -27865,7 +28188,7 @@ async function runPostIndexWork(ctx) {
|
|
|
27865
28188
|
}
|
|
27866
28189
|
});
|
|
27867
28190
|
if (sd) {
|
|
27868
|
-
const entities =
|
|
28191
|
+
const entities = getAllEntitiesFromDb6(sd);
|
|
27869
28192
|
if (entities.length > 0) {
|
|
27870
28193
|
const entityMap = new Map(entities.map((e) => [e.name, {
|
|
27871
28194
|
name: e.name,
|
|
@@ -27880,7 +28203,7 @@ async function runPostIndexWork(ctx) {
|
|
|
27880
28203
|
activateVault(ctx);
|
|
27881
28204
|
loadEntityEmbeddingsToMemory();
|
|
27882
28205
|
if (sd) {
|
|
27883
|
-
const entities =
|
|
28206
|
+
const entities = getAllEntitiesFromDb6(sd);
|
|
27884
28207
|
if (entities.length > 0) {
|
|
27885
28208
|
saveInferredCategories(classifyUncategorizedEntities(
|
|
27886
28209
|
entities.map((entity) => ({
|
|
@@ -27901,7 +28224,7 @@ async function runPostIndexWork(ctx) {
|
|
|
27901
28224
|
if (attempt < MAX_BUILD_RETRIES) {
|
|
27902
28225
|
const delay = 1e4;
|
|
27903
28226
|
serverLog("semantic", `Build failed (attempt ${attempt}/${MAX_BUILD_RETRIES}): ${msg}. Retrying in ${delay / 1e3}s...`, "error");
|
|
27904
|
-
await new Promise((
|
|
28227
|
+
await new Promise((resolve3) => setTimeout(resolve3, delay));
|
|
27905
28228
|
return attemptBuild(attempt + 1);
|
|
27906
28229
|
}
|
|
27907
28230
|
serverLog("semantic", `Embeddings build failed after ${MAX_BUILD_RETRIES} attempts: ${msg}`, "error");
|
|
@@ -27918,7 +28241,7 @@ async function runPostIndexWork(ctx) {
|
|
|
27918
28241
|
serverLog("semantic", "Skipping \u2014 FLYWHEEL_SKIP_EMBEDDINGS");
|
|
27919
28242
|
}
|
|
27920
28243
|
if (process.env.FLYWHEEL_WATCH !== "false") {
|
|
27921
|
-
const
|
|
28244
|
+
const config2 = parseWatcherConfig();
|
|
27922
28245
|
const lastContentHashes = /* @__PURE__ */ new Map();
|
|
27923
28246
|
if (sd) {
|
|
27924
28247
|
const persisted = loadContentHashes(sd);
|
|
@@ -27927,7 +28250,15 @@ async function runPostIndexWork(ctx) {
|
|
|
27927
28250
|
serverLog("watcher", `Loaded ${persisted.size} persisted content hashes`);
|
|
27928
28251
|
}
|
|
27929
28252
|
}
|
|
27930
|
-
serverLog("watcher", `File watcher enabled (debounce: ${
|
|
28253
|
+
serverLog("watcher", `File watcher enabled (debounce: ${config2.debounceMs}ms)`);
|
|
28254
|
+
deferredScheduler = new DeferredStepScheduler();
|
|
28255
|
+
deferredScheduler.setExecutor({
|
|
28256
|
+
ctx,
|
|
28257
|
+
vp,
|
|
28258
|
+
sd,
|
|
28259
|
+
getVaultIndex: () => vaultIndex,
|
|
28260
|
+
updateEntitiesInStateDb
|
|
28261
|
+
});
|
|
27931
28262
|
const handleBatch = async (batch) => {
|
|
27932
28263
|
const vaultPrefixes = /* @__PURE__ */ new Set([
|
|
27933
28264
|
normalizePath(vp),
|
|
@@ -27949,8 +28280,8 @@ async function runPostIndexWork(ctx) {
|
|
|
27949
28280
|
}
|
|
27950
28281
|
} catch {
|
|
27951
28282
|
try {
|
|
27952
|
-
const dir =
|
|
27953
|
-
const base =
|
|
28283
|
+
const dir = path39.dirname(rawPath);
|
|
28284
|
+
const base = path39.basename(rawPath);
|
|
27954
28285
|
const resolvedDir = realpathSync(dir).replace(/\\/g, "/");
|
|
27955
28286
|
for (const prefix of vaultPrefixes) {
|
|
27956
28287
|
if (resolvedDir.startsWith(prefix + "/") || resolvedDir === prefix) {
|
|
@@ -27982,7 +28313,7 @@ async function runPostIndexWork(ctx) {
|
|
|
27982
28313
|
continue;
|
|
27983
28314
|
}
|
|
27984
28315
|
try {
|
|
27985
|
-
const content = await
|
|
28316
|
+
const content = await fs35.readFile(path39.join(vp, event.path), "utf-8");
|
|
27986
28317
|
const hash = createHash4("sha256").update(content).digest("hex").slice(0, 16);
|
|
27987
28318
|
if (lastContentHashes.get(event.path) === hash) {
|
|
27988
28319
|
serverLog("watcher", `Hash unchanged, skipping: ${event.path}`);
|
|
@@ -28068,13 +28399,14 @@ async function runPostIndexWork(ctx) {
|
|
|
28068
28399
|
updateVaultIndex,
|
|
28069
28400
|
updateEntitiesInStateDb,
|
|
28070
28401
|
getVaultIndex: () => vaultIndex,
|
|
28071
|
-
buildVaultIndex
|
|
28402
|
+
buildVaultIndex,
|
|
28403
|
+
deferredScheduler: deferredScheduler ?? void 0
|
|
28072
28404
|
});
|
|
28073
28405
|
await runner.run();
|
|
28074
28406
|
};
|
|
28075
28407
|
const watcher = createVaultWatcher({
|
|
28076
28408
|
vaultPath: vp,
|
|
28077
|
-
config,
|
|
28409
|
+
config: config2,
|
|
28078
28410
|
onBatch: handleBatch,
|
|
28079
28411
|
onStateChange: (status) => {
|
|
28080
28412
|
if (status.state === "dirty") {
|
|
@@ -28090,7 +28422,7 @@ async function runPostIndexWork(ctx) {
|
|
|
28090
28422
|
if (sd) {
|
|
28091
28423
|
const lastPipelineEvent = getRecentPipelineEvent(sd);
|
|
28092
28424
|
if (lastPipelineEvent) {
|
|
28093
|
-
const catchupEvents =
|
|
28425
|
+
const catchupEvents = buildStartupCatchupBatch(vp, lastPipelineEvent.timestamp, startupScanFiles);
|
|
28094
28426
|
if (catchupEvents.length > 0) {
|
|
28095
28427
|
console.error(`[Flywheel] Startup catch-up: ${catchupEvents.length} file(s) modified while offline`);
|
|
28096
28428
|
await handleBatch({ events: catchupEvents, renames: [], timestamp: Date.now() });
|
|
@@ -28110,11 +28442,24 @@ async function runPostIndexWork(ctx) {
|
|
|
28110
28442
|
watcher.start();
|
|
28111
28443
|
serverLog("watcher", "File watcher started");
|
|
28112
28444
|
}
|
|
28445
|
+
startupScanFiles = null;
|
|
28113
28446
|
if (process.env.FLYWHEEL_WATCH !== "false") {
|
|
28114
28447
|
startSweepTimer(() => ctx.vaultIndex, void 0, () => {
|
|
28115
28448
|
if (sd) runPeriodicMaintenance(sd);
|
|
28116
28449
|
});
|
|
28117
28450
|
serverLog("server", "Sweep timer started (5 min interval)");
|
|
28451
|
+
const maintenanceIntervalMs = parseInt(process.env.FLYWHEEL_MAINTENANCE_INTERVAL_MINUTES ?? "120", 10) * 60 * 1e3;
|
|
28452
|
+
startMaintenanceTimer({
|
|
28453
|
+
ctx,
|
|
28454
|
+
vp,
|
|
28455
|
+
sd,
|
|
28456
|
+
getVaultIndex: () => ctx.vaultIndex,
|
|
28457
|
+
updateEntitiesInStateDb,
|
|
28458
|
+
updateFlywheelConfig,
|
|
28459
|
+
getLastMcpRequestAt: () => lastMcpRequestAt,
|
|
28460
|
+
getLastFullRebuildAt: () => lastFullRebuildAt
|
|
28461
|
+
}, maintenanceIntervalMs);
|
|
28462
|
+
serverLog("server", `Maintenance timer started (~${Math.round(maintenanceIntervalMs / 6e4)}min interval)`);
|
|
28118
28463
|
}
|
|
28119
28464
|
const postDuration = Date.now() - postStart;
|
|
28120
28465
|
serverLog("server", `Post-index work complete in ${postDuration}ms`);
|
|
@@ -28162,7 +28507,12 @@ function gracefulShutdown(signal) {
|
|
|
28162
28507
|
watcherInstance?.stop();
|
|
28163
28508
|
} catch {
|
|
28164
28509
|
}
|
|
28510
|
+
try {
|
|
28511
|
+
deferredScheduler?.cancelAll();
|
|
28512
|
+
} catch {
|
|
28513
|
+
}
|
|
28165
28514
|
stopSweepTimer();
|
|
28515
|
+
stopMaintenanceTimer();
|
|
28166
28516
|
flushLogs().catch(() => {
|
|
28167
28517
|
}).finally(() => process.exit(0));
|
|
28168
28518
|
setTimeout(() => process.exit(0), 2e3).unref();
|