@velvetmonkey/flywheel-memory 2.0.125 → 2.0.127

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +432 -329
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -895,17 +895,17 @@ function updateSuppressionList(stateDb2, now) {
895
895
  `DELETE FROM wikilink_suppressions
896
896
  WHERE datetime(updated_at, '+' || ? || ' days') <= datetime('now')`
897
897
  ).run(SUPPRESSION_TTL_DAYS);
898
- for (const stat4 of weightedStats) {
899
- const posteriorMean = computePosteriorMean(stat4.weightedCorrect, stat4.weightedFp);
900
- const totalObs = PRIOR_ALPHA + stat4.weightedCorrect + PRIOR_BETA + stat4.weightedFp;
898
+ for (const stat5 of weightedStats) {
899
+ const posteriorMean = computePosteriorMean(stat5.weightedCorrect, stat5.weightedFp);
900
+ const totalObs = PRIOR_ALPHA + stat5.weightedCorrect + PRIOR_BETA + stat5.weightedFp;
901
901
  if (totalObs < SUPPRESSION_MIN_OBSERVATIONS) {
902
902
  continue;
903
903
  }
904
904
  if (posteriorMean < SUPPRESSION_POSTERIOR_THRESHOLD) {
905
- upsert.run(stat4.entity, 1 - posteriorMean);
905
+ upsert.run(stat5.entity, 1 - posteriorMean);
906
906
  updated++;
907
907
  } else {
908
- remove.run(stat4.entity);
908
+ remove.run(stat5.entity);
909
909
  }
910
910
  }
911
911
  });
@@ -974,31 +974,31 @@ function getAllFeedbackBoosts(stateDb2, folder, now) {
974
974
  if (folder !== void 0) {
975
975
  folderStatsMap = /* @__PURE__ */ new Map();
976
976
  for (const gs of globalStats) {
977
- const fs33 = getWeightedFolderStats(stateDb2, gs.entity, folder, now);
978
- if (fs33.rawTotal >= FEEDBACK_BOOST_MIN_SAMPLES) {
977
+ const fs34 = getWeightedFolderStats(stateDb2, gs.entity, folder, now);
978
+ if (fs34.rawTotal >= FEEDBACK_BOOST_MIN_SAMPLES) {
979
979
  folderStatsMap.set(gs.entity, {
980
- weightedAccuracy: fs33.weightedAccuracy,
981
- rawCount: fs33.rawTotal
980
+ weightedAccuracy: fs34.weightedAccuracy,
981
+ rawCount: fs34.rawTotal
982
982
  });
983
983
  }
984
984
  }
985
985
  }
986
986
  const boosts = /* @__PURE__ */ new Map();
987
- for (const stat4 of globalStats) {
988
- if (stat4.rawTotal < FEEDBACK_BOOST_MIN_SAMPLES) continue;
987
+ for (const stat5 of globalStats) {
988
+ if (stat5.rawTotal < FEEDBACK_BOOST_MIN_SAMPLES) continue;
989
989
  let accuracy;
990
990
  let sampleCount;
991
- const fs33 = folderStatsMap?.get(stat4.entity);
992
- if (fs33 && fs33.rawCount >= FEEDBACK_BOOST_MIN_SAMPLES) {
993
- accuracy = fs33.weightedAccuracy;
994
- sampleCount = fs33.rawCount;
991
+ const fs34 = folderStatsMap?.get(stat5.entity);
992
+ if (fs34 && fs34.rawCount >= FEEDBACK_BOOST_MIN_SAMPLES) {
993
+ accuracy = fs34.weightedAccuracy;
994
+ sampleCount = fs34.rawCount;
995
995
  } else {
996
- accuracy = stat4.weightedAccuracy;
997
- sampleCount = stat4.rawTotal;
996
+ accuracy = stat5.weightedAccuracy;
997
+ sampleCount = stat5.rawTotal;
998
998
  }
999
999
  const boost = computeBoostFromAccuracy(accuracy, sampleCount);
1000
1000
  if (boost !== 0) {
1001
- boosts.set(stat4.entity, boost);
1001
+ boosts.set(stat5.entity, boost);
1002
1002
  }
1003
1003
  }
1004
1004
  return boosts;
@@ -1006,13 +1006,13 @@ function getAllFeedbackBoosts(stateDb2, folder, now) {
1006
1006
  function getAllSuppressionPenalties(stateDb2, now) {
1007
1007
  const penalties = /* @__PURE__ */ new Map();
1008
1008
  const weightedStats = getWeightedEntityStats(stateDb2, now);
1009
- for (const stat4 of weightedStats) {
1010
- const posteriorMean = computePosteriorMean(stat4.weightedCorrect, stat4.weightedFp);
1011
- const totalObs = PRIOR_ALPHA + stat4.weightedCorrect + PRIOR_BETA + stat4.weightedFp;
1009
+ for (const stat5 of weightedStats) {
1010
+ const posteriorMean = computePosteriorMean(stat5.weightedCorrect, stat5.weightedFp);
1011
+ const totalObs = PRIOR_ALPHA + stat5.weightedCorrect + PRIOR_BETA + stat5.weightedFp;
1012
1012
  if (totalObs >= SUPPRESSION_MIN_OBSERVATIONS && posteriorMean < SUPPRESSION_POSTERIOR_THRESHOLD) {
1013
1013
  const penalty = Math.round(MAX_SUPPRESSION_PENALTY * (1 - posteriorMean / SUPPRESSION_POSTERIOR_THRESHOLD));
1014
1014
  if (penalty < 0) {
1015
- penalties.set(stat4.entity, penalty);
1015
+ penalties.set(stat5.entity, penalty);
1016
1016
  }
1017
1017
  }
1018
1018
  }
@@ -1554,8 +1554,8 @@ function clearLastMutationCommit() {
1554
1554
  async function checkGitLock(vaultPath2) {
1555
1555
  const lockPath = path8.join(vaultPath2, ".git/index.lock");
1556
1556
  try {
1557
- const stat4 = await fs6.stat(lockPath);
1558
- const ageMs = Date.now() - stat4.mtimeMs;
1557
+ const stat5 = await fs6.stat(lockPath);
1558
+ const ageMs = Date.now() - stat5.mtimeMs;
1559
1559
  return {
1560
1560
  locked: true,
1561
1561
  stale: ageMs > STALE_LOCK_THRESHOLD_MS,
@@ -1577,8 +1577,8 @@ async function isGitRepo(vaultPath2) {
1577
1577
  async function checkLockFile(vaultPath2) {
1578
1578
  const lockPath = path8.join(vaultPath2, ".git/index.lock");
1579
1579
  try {
1580
- const stat4 = await fs6.stat(lockPath);
1581
- const ageMs = Date.now() - stat4.mtimeMs;
1580
+ const stat5 = await fs6.stat(lockPath);
1581
+ const ageMs = Date.now() - stat5.mtimeMs;
1582
1582
  return { stale: ageMs > STALE_LOCK_THRESHOLD_MS, ageMs };
1583
1583
  } catch {
1584
1584
  return null;
@@ -3282,6 +3282,7 @@ var init_edgeWeights = __esm({
3282
3282
  // src/core/write/wikilinks.ts
3283
3283
  var wikilinks_exports = {};
3284
3284
  __export(wikilinks_exports, {
3285
+ applyProactiveSuggestions: () => applyProactiveSuggestions,
3285
3286
  checkAndRefreshIfStale: () => checkAndRefreshIfStale,
3286
3287
  checkPreflightSimilarity: () => checkPreflightSimilarity,
3287
3288
  detectAliasCollisions: () => detectAliasCollisions,
@@ -3289,6 +3290,7 @@ __export(wikilinks_exports, {
3289
3290
  getCooccurrenceIndex: () => getCooccurrenceIndex,
3290
3291
  getEntityIndex: () => getEntityIndex,
3291
3292
  getEntityIndexStats: () => getEntityIndexStats,
3293
+ getNoteContext: () => getNoteContext,
3292
3294
  getWikilinkStrictness: () => getWikilinkStrictness,
3293
3295
  getWriteStateDb: () => getWriteStateDb,
3294
3296
  initializeEntityIndex: () => initializeEntityIndex,
@@ -3317,6 +3319,8 @@ import {
3317
3319
  getEntitiesByAlias,
3318
3320
  searchEntities as searchEntitiesDb
3319
3321
  } from "@velvetmonkey/vault-core";
3322
+ import path11 from "path";
3323
+ import * as fs7 from "fs/promises";
3320
3324
  function setWriteStateDb(stateDb2) {
3321
3325
  moduleStateDb5 = stateDb2;
3322
3326
  setGitStateDb(stateDb2);
@@ -4233,6 +4237,76 @@ async function checkPreflightSimilarity(noteName) {
4233
4237
  }
4234
4238
  return result;
4235
4239
  }
4240
+ async function applyProactiveSuggestions(filePath, vaultPath2, suggestions, config) {
4241
+ const stateDb2 = getWriteStateDb();
4242
+ const candidates = suggestions.filter((s) => s.score >= config.minScore && s.confidence === "high").slice(0, config.maxPerFile);
4243
+ if (candidates.length === 0) {
4244
+ return { applied: [], skipped: [] };
4245
+ }
4246
+ const fullPath = path11.join(vaultPath2, filePath);
4247
+ try {
4248
+ const stat5 = await fs7.stat(fullPath);
4249
+ if (Date.now() - stat5.mtimeMs < 3e4) {
4250
+ return { applied: [], skipped: candidates.map((c) => c.entity) };
4251
+ }
4252
+ } catch {
4253
+ return { applied: [], skipped: candidates.map((c) => c.entity) };
4254
+ }
4255
+ let content;
4256
+ try {
4257
+ content = await fs7.readFile(fullPath, "utf-8");
4258
+ } catch {
4259
+ return { applied: [], skipped: candidates.map((c) => c.entity) };
4260
+ }
4261
+ const entityObjects = [];
4262
+ for (const candidate of candidates) {
4263
+ if (stateDb2 && isSuppressed(stateDb2, candidate.entity)) continue;
4264
+ if (stateDb2) {
4265
+ const entityObj = getEntityByName(stateDb2, candidate.entity);
4266
+ if (entityObj) {
4267
+ entityObjects.push({
4268
+ name: entityObj.name,
4269
+ path: entityObj.path,
4270
+ aliases: entityObj.aliases ?? []
4271
+ });
4272
+ continue;
4273
+ }
4274
+ }
4275
+ entityObjects.push(candidate.entity);
4276
+ }
4277
+ if (entityObjects.length === 0) {
4278
+ return { applied: [], skipped: candidates.map((c) => c.entity) };
4279
+ }
4280
+ const result = applyWikilinks(content, entityObjects, {
4281
+ firstOccurrenceOnly: true,
4282
+ caseInsensitive: true
4283
+ });
4284
+ if (result.linksAdded === 0) {
4285
+ return { applied: [], skipped: candidates.map((c) => c.entity) };
4286
+ }
4287
+ try {
4288
+ await fs7.writeFile(fullPath, result.content, "utf-8");
4289
+ } catch {
4290
+ return { applied: [], skipped: candidates.map((c) => c.entity) };
4291
+ }
4292
+ if (stateDb2) {
4293
+ trackWikilinkApplications(stateDb2, filePath, result.linkedEntities);
4294
+ try {
4295
+ const markApplied = stateDb2.db.prepare(
4296
+ `UPDATE suggestion_events SET applied = 1
4297
+ WHERE note_path = ? AND entity = ? AND applied = 0`
4298
+ );
4299
+ for (const entity of result.linkedEntities) {
4300
+ markApplied.run(filePath, entity);
4301
+ }
4302
+ } catch {
4303
+ }
4304
+ }
4305
+ return {
4306
+ applied: result.linkedEntities,
4307
+ skipped: candidates.map((c) => c.entity).filter((e) => !result.linkedEntities.includes(e))
4308
+ };
4309
+ }
4236
4310
  var moduleStateDb5, moduleConfig, ALL_IMPLICIT_PATTERNS, entityIndex, indexReady, indexError2, lastLoadedAt, cooccurrenceIndex, recencyIndex, DEFAULT_EXCLUDE_FOLDERS, SUGGESTION_PATTERN, GENERIC_WORDS, MAX_ENTITY_LENGTH, MAX_ENTITY_WORDS, ARTICLE_PATTERNS, STRICTNESS_CONFIGS, TYPE_BOOST, CROSS_FOLDER_BOOST, SEMANTIC_MIN_SIMILARITY, SEMANTIC_MAX_BOOST, CONTEXT_BOOST, MIN_SUGGESTION_SCORE, MIN_MATCH_RATIO, FULL_ALIAS_MATCH_BONUS;
4237
4311
  var init_wikilinks = __esm({
4238
4312
  "src/core/write/wikilinks.ts"() {
@@ -4478,8 +4552,8 @@ var init_constants = __esm({
4478
4552
  });
4479
4553
 
4480
4554
  // src/core/write/writer.ts
4481
- import fs18 from "fs/promises";
4482
- import path20 from "path";
4555
+ import fs19 from "fs/promises";
4556
+ import path21 from "path";
4483
4557
  import matter5 from "gray-matter";
4484
4558
  import { createHash as createHash2 } from "node:crypto";
4485
4559
  function isSensitivePath(filePath) {
@@ -4807,13 +4881,13 @@ function validatePath(vaultPath2, notePath) {
4807
4881
  if (notePath.startsWith("\\")) {
4808
4882
  return false;
4809
4883
  }
4810
- const resolvedVault = path20.resolve(vaultPath2);
4811
- const resolvedNote = path20.resolve(vaultPath2, notePath);
4884
+ const resolvedVault = path21.resolve(vaultPath2);
4885
+ const resolvedNote = path21.resolve(vaultPath2, notePath);
4812
4886
  return resolvedNote.startsWith(resolvedVault);
4813
4887
  }
4814
4888
  function sanitizeNotePath(notePath) {
4815
- const dir = path20.dirname(notePath);
4816
- let filename = path20.basename(notePath);
4889
+ const dir = path21.dirname(notePath);
4890
+ let filename = path21.basename(notePath);
4817
4891
  const ext = filename.endsWith(".md") ? ".md" : "";
4818
4892
  let stem2 = ext ? filename.slice(0, -ext.length) : filename;
4819
4893
  stem2 = stem2.replace(/\s+/g, "-");
@@ -4822,7 +4896,7 @@ function sanitizeNotePath(notePath) {
4822
4896
  stem2 = stem2.replace(/-{2,}/g, "-");
4823
4897
  stem2 = stem2.replace(/^-+|-+$/g, "");
4824
4898
  filename = stem2 + (ext || ".md");
4825
- return dir === "." ? filename : path20.join(dir, filename).replace(/\\/g, "/");
4899
+ return dir === "." ? filename : path21.join(dir, filename).replace(/\\/g, "/");
4826
4900
  }
4827
4901
  async function validatePathSecure(vaultPath2, notePath) {
4828
4902
  if (notePath.startsWith("/")) {
@@ -4849,8 +4923,8 @@ async function validatePathSecure(vaultPath2, notePath) {
4849
4923
  reason: "Path traversal not allowed"
4850
4924
  };
4851
4925
  }
4852
- const resolvedVault = path20.resolve(vaultPath2);
4853
- const resolvedNote = path20.resolve(vaultPath2, notePath);
4926
+ const resolvedVault = path21.resolve(vaultPath2);
4927
+ const resolvedNote = path21.resolve(vaultPath2, notePath);
4854
4928
  if (!resolvedNote.startsWith(resolvedVault)) {
4855
4929
  return {
4856
4930
  valid: false,
@@ -4864,18 +4938,18 @@ async function validatePathSecure(vaultPath2, notePath) {
4864
4938
  };
4865
4939
  }
4866
4940
  try {
4867
- const fullPath = path20.join(vaultPath2, notePath);
4941
+ const fullPath = path21.join(vaultPath2, notePath);
4868
4942
  try {
4869
- await fs18.access(fullPath);
4870
- const realPath = await fs18.realpath(fullPath);
4871
- const realVaultPath = await fs18.realpath(vaultPath2);
4943
+ await fs19.access(fullPath);
4944
+ const realPath = await fs19.realpath(fullPath);
4945
+ const realVaultPath = await fs19.realpath(vaultPath2);
4872
4946
  if (!realPath.startsWith(realVaultPath)) {
4873
4947
  return {
4874
4948
  valid: false,
4875
4949
  reason: "Symlink target is outside vault"
4876
4950
  };
4877
4951
  }
4878
- const relativePath = path20.relative(realVaultPath, realPath);
4952
+ const relativePath = path21.relative(realVaultPath, realPath);
4879
4953
  if (isSensitivePath(relativePath)) {
4880
4954
  return {
4881
4955
  valid: false,
@@ -4883,11 +4957,11 @@ async function validatePathSecure(vaultPath2, notePath) {
4883
4957
  };
4884
4958
  }
4885
4959
  } catch {
4886
- const parentDir = path20.dirname(fullPath);
4960
+ const parentDir = path21.dirname(fullPath);
4887
4961
  try {
4888
- await fs18.access(parentDir);
4889
- const realParentPath = await fs18.realpath(parentDir);
4890
- const realVaultPath = await fs18.realpath(vaultPath2);
4962
+ await fs19.access(parentDir);
4963
+ const realParentPath = await fs19.realpath(parentDir);
4964
+ const realVaultPath = await fs19.realpath(vaultPath2);
4891
4965
  if (!realParentPath.startsWith(realVaultPath)) {
4892
4966
  return {
4893
4967
  valid: false,
@@ -4912,10 +4986,10 @@ async function readVaultFile(vaultPath2, notePath) {
4912
4986
  if (!validatePath(vaultPath2, notePath)) {
4913
4987
  throw new Error("Invalid path: path traversal not allowed");
4914
4988
  }
4915
- const fullPath = path20.join(vaultPath2, notePath);
4916
- const [rawContent, stat4] = await Promise.all([
4917
- fs18.readFile(fullPath, "utf-8"),
4918
- fs18.stat(fullPath)
4989
+ const fullPath = path21.join(vaultPath2, notePath);
4990
+ const [rawContent, stat5] = await Promise.all([
4991
+ fs19.readFile(fullPath, "utf-8"),
4992
+ fs19.stat(fullPath)
4919
4993
  ]);
4920
4994
  const contentHash2 = computeContentHash(rawContent);
4921
4995
  const lineEnding = detectLineEnding(rawContent);
@@ -4927,7 +5001,7 @@ async function readVaultFile(vaultPath2, notePath) {
4927
5001
  frontmatter,
4928
5002
  rawContent,
4929
5003
  lineEnding,
4930
- mtimeMs: stat4.mtimeMs,
5004
+ mtimeMs: stat5.mtimeMs,
4931
5005
  contentHash: contentHash2
4932
5006
  };
4933
5007
  }
@@ -4967,9 +5041,9 @@ async function writeVaultFile(vaultPath2, notePath, content, frontmatter, lineEn
4967
5041
  if (!validation.valid) {
4968
5042
  throw new Error(`Invalid path: ${validation.reason}`);
4969
5043
  }
4970
- const fullPath = path20.join(vaultPath2, notePath);
5044
+ const fullPath = path21.join(vaultPath2, notePath);
4971
5045
  if (expectedHash) {
4972
- const currentRaw = await fs18.readFile(fullPath, "utf-8");
5046
+ const currentRaw = await fs19.readFile(fullPath, "utf-8");
4973
5047
  const currentHash = computeContentHash(currentRaw);
4974
5048
  if (currentHash !== expectedHash) {
4975
5049
  throw new WriteConflictError(notePath);
@@ -4978,7 +5052,7 @@ async function writeVaultFile(vaultPath2, notePath, content, frontmatter, lineEn
4978
5052
  let output = matter5.stringify(content, frontmatter);
4979
5053
  output = normalizeTrailingNewline(output);
4980
5054
  output = convertLineEndings(output, lineEnding);
4981
- await fs18.writeFile(fullPath, output, "utf-8");
5055
+ await fs19.writeFile(fullPath, output, "utf-8");
4982
5056
  }
4983
5057
  function removeFromSection(content, section, pattern, mode = "first", useRegex = false) {
4984
5058
  const lines = content.split("\n");
@@ -5367,8 +5441,8 @@ function createContext(variables = {}) {
5367
5441
  steps: {}
5368
5442
  };
5369
5443
  }
5370
- function resolvePath(obj, path33) {
5371
- const parts = path33.split(".");
5444
+ function resolvePath(obj, path34) {
5445
+ const parts = path34.split(".");
5372
5446
  let current = obj;
5373
5447
  for (const part of parts) {
5374
5448
  if (current === void 0 || current === null) {
@@ -5825,8 +5899,8 @@ __export(conditions_exports, {
5825
5899
  evaluateCondition: () => evaluateCondition,
5826
5900
  shouldStepExecute: () => shouldStepExecute
5827
5901
  });
5828
- import fs25 from "fs/promises";
5829
- import path26 from "path";
5902
+ import fs26 from "fs/promises";
5903
+ import path27 from "path";
5830
5904
  async function evaluateCondition(condition, vaultPath2, context) {
5831
5905
  const interpolatedPath = condition.path ? interpolate(condition.path, context) : void 0;
5832
5906
  const interpolatedSection = condition.section ? interpolate(condition.section, context) : void 0;
@@ -5879,9 +5953,9 @@ async function evaluateCondition(condition, vaultPath2, context) {
5879
5953
  }
5880
5954
  }
5881
5955
  async function evaluateFileExists(vaultPath2, notePath, expectExists) {
5882
- const fullPath = path26.join(vaultPath2, notePath);
5956
+ const fullPath = path27.join(vaultPath2, notePath);
5883
5957
  try {
5884
- await fs25.access(fullPath);
5958
+ await fs26.access(fullPath);
5885
5959
  return {
5886
5960
  met: expectExists,
5887
5961
  reason: expectExists ? `File exists: ${notePath}` : `File exists (expected not to): ${notePath}`
@@ -5894,9 +5968,9 @@ async function evaluateFileExists(vaultPath2, notePath, expectExists) {
5894
5968
  }
5895
5969
  }
5896
5970
  async function evaluateSectionExists(vaultPath2, notePath, sectionName, expectExists) {
5897
- const fullPath = path26.join(vaultPath2, notePath);
5971
+ const fullPath = path27.join(vaultPath2, notePath);
5898
5972
  try {
5899
- await fs25.access(fullPath);
5973
+ await fs26.access(fullPath);
5900
5974
  } catch {
5901
5975
  return {
5902
5976
  met: !expectExists,
@@ -5925,9 +5999,9 @@ async function evaluateSectionExists(vaultPath2, notePath, sectionName, expectEx
5925
5999
  }
5926
6000
  }
5927
6001
  async function evaluateFrontmatterExists(vaultPath2, notePath, fieldName, expectExists) {
5928
- const fullPath = path26.join(vaultPath2, notePath);
6002
+ const fullPath = path27.join(vaultPath2, notePath);
5929
6003
  try {
5930
- await fs25.access(fullPath);
6004
+ await fs26.access(fullPath);
5931
6005
  } catch {
5932
6006
  return {
5933
6007
  met: !expectExists,
@@ -5956,9 +6030,9 @@ async function evaluateFrontmatterExists(vaultPath2, notePath, fieldName, expect
5956
6030
  }
5957
6031
  }
5958
6032
  async function evaluateFrontmatterEquals(vaultPath2, notePath, fieldName, expectedValue) {
5959
- const fullPath = path26.join(vaultPath2, notePath);
6033
+ const fullPath = path27.join(vaultPath2, notePath);
5960
6034
  try {
5961
- await fs25.access(fullPath);
6035
+ await fs26.access(fullPath);
5962
6036
  } catch {
5963
6037
  return {
5964
6038
  met: false,
@@ -6100,7 +6174,7 @@ var init_taskHelpers = __esm({
6100
6174
  });
6101
6175
 
6102
6176
  // src/index.ts
6103
- import * as path32 from "path";
6177
+ import * as path33 from "path";
6104
6178
  import { readFileSync as readFileSync5, realpathSync, existsSync as existsSync3, statSync as statSync5 } from "fs";
6105
6179
  import { fileURLToPath } from "url";
6106
6180
  import { dirname as dirname6, join as join17 } from "path";
@@ -6339,8 +6413,8 @@ function updateIndexProgress(parsed, total) {
6339
6413
  function normalizeTarget(target) {
6340
6414
  return target.toLowerCase().replace(/\.md$/, "");
6341
6415
  }
6342
- function normalizeNotePath(path33) {
6343
- return path33.toLowerCase().replace(/\.md$/, "");
6416
+ function normalizeNotePath(path34) {
6417
+ return path34.toLowerCase().replace(/\.md$/, "");
6344
6418
  }
6345
6419
  async function buildVaultIndex(vaultPath2, options = {}) {
6346
6420
  const { timeoutMs = DEFAULT_TIMEOUT_MS, onProgress } = options;
@@ -6509,7 +6583,7 @@ function findSimilarEntity(index, target) {
6509
6583
  }
6510
6584
  const maxDist = normalizedLen <= 10 ? 1 : 2;
6511
6585
  let bestMatch;
6512
- for (const [entity, path33] of index.entities) {
6586
+ for (const [entity, path34] of index.entities) {
6513
6587
  const lenDiff = Math.abs(entity.length - normalizedLen);
6514
6588
  if (lenDiff > maxDist) {
6515
6589
  continue;
@@ -6517,7 +6591,7 @@ function findSimilarEntity(index, target) {
6517
6591
  const dist = levenshteinDistance(normalized, entity);
6518
6592
  if (dist > 0 && dist <= maxDist) {
6519
6593
  if (!bestMatch || dist < bestMatch.distance) {
6520
- bestMatch = { path: path33, entity, distance: dist };
6594
+ bestMatch = { path: path34, entity, distance: dist };
6521
6595
  if (dist === 1) {
6522
6596
  return bestMatch;
6523
6597
  }
@@ -7048,30 +7122,30 @@ var EventQueue = class {
7048
7122
  * Add a new event to the queue
7049
7123
  */
7050
7124
  push(type, rawPath) {
7051
- const path33 = normalizePath(rawPath);
7125
+ const path34 = normalizePath(rawPath);
7052
7126
  const now = Date.now();
7053
7127
  const event = {
7054
7128
  type,
7055
- path: path33,
7129
+ path: path34,
7056
7130
  timestamp: now
7057
7131
  };
7058
- let pending = this.pending.get(path33);
7132
+ let pending = this.pending.get(path34);
7059
7133
  if (!pending) {
7060
7134
  pending = {
7061
7135
  events: [],
7062
7136
  timer: null,
7063
7137
  lastEvent: now
7064
7138
  };
7065
- this.pending.set(path33, pending);
7139
+ this.pending.set(path34, pending);
7066
7140
  }
7067
7141
  pending.events.push(event);
7068
7142
  pending.lastEvent = now;
7069
- console.error(`[flywheel] QUEUE: pushed ${type} for ${path33}, pending=${this.pending.size}`);
7143
+ console.error(`[flywheel] QUEUE: pushed ${type} for ${path34}, pending=${this.pending.size}`);
7070
7144
  if (pending.timer) {
7071
7145
  clearTimeout(pending.timer);
7072
7146
  }
7073
7147
  pending.timer = setTimeout(() => {
7074
- this.flushPath(path33);
7148
+ this.flushPath(path34);
7075
7149
  }, this.config.debounceMs);
7076
7150
  if (this.pending.size >= this.config.batchSize) {
7077
7151
  this.flush();
@@ -7092,10 +7166,10 @@ var EventQueue = class {
7092
7166
  /**
7093
7167
  * Flush a single path's events
7094
7168
  */
7095
- flushPath(path33) {
7096
- const pending = this.pending.get(path33);
7169
+ flushPath(path34) {
7170
+ const pending = this.pending.get(path34);
7097
7171
  if (!pending || pending.events.length === 0) return;
7098
- console.error(`[flywheel] QUEUE: flushing ${path33}, events=${pending.events.length}`);
7172
+ console.error(`[flywheel] QUEUE: flushing ${path34}, events=${pending.events.length}`);
7099
7173
  if (pending.timer) {
7100
7174
  clearTimeout(pending.timer);
7101
7175
  pending.timer = null;
@@ -7104,7 +7178,7 @@ var EventQueue = class {
7104
7178
  if (coalescedType) {
7105
7179
  const coalesced = {
7106
7180
  type: coalescedType,
7107
- path: path33,
7181
+ path: path34,
7108
7182
  originalEvents: [...pending.events]
7109
7183
  };
7110
7184
  this.onBatch({
@@ -7113,7 +7187,7 @@ var EventQueue = class {
7113
7187
  timestamp: Date.now()
7114
7188
  });
7115
7189
  }
7116
- this.pending.delete(path33);
7190
+ this.pending.delete(path34);
7117
7191
  }
7118
7192
  /**
7119
7193
  * Flush all pending events
@@ -7125,7 +7199,7 @@ var EventQueue = class {
7125
7199
  }
7126
7200
  if (this.pending.size === 0) return;
7127
7201
  const events = [];
7128
- for (const [path33, pending] of this.pending) {
7202
+ for (const [path34, pending] of this.pending) {
7129
7203
  if (pending.timer) {
7130
7204
  clearTimeout(pending.timer);
7131
7205
  }
@@ -7133,7 +7207,7 @@ var EventQueue = class {
7133
7207
  if (coalescedType) {
7134
7208
  events.push({
7135
7209
  type: coalescedType,
7136
- path: path33,
7210
+ path: path34,
7137
7211
  originalEvents: [...pending.events]
7138
7212
  });
7139
7213
  }
@@ -7326,8 +7400,8 @@ async function upsertNote(index, vaultPath2, notePath) {
7326
7400
  releasedKeys = removeNoteFromIndex(index, notePath);
7327
7401
  }
7328
7402
  const fullPath = path7.join(vaultPath2, notePath);
7329
- const fs33 = await import("fs/promises");
7330
- const stats = await fs33.stat(fullPath);
7403
+ const fs34 = await import("fs/promises");
7404
+ const stats = await fs34.stat(fullPath);
7331
7405
  const vaultFile = {
7332
7406
  path: notePath,
7333
7407
  absolutePath: fullPath,
@@ -7519,31 +7593,31 @@ function createVaultWatcher(options) {
7519
7593
  usePolling: config.usePolling,
7520
7594
  interval: config.usePolling ? config.pollInterval : void 0
7521
7595
  });
7522
- watcher.on("add", (path33) => {
7523
- console.error(`[flywheel] RAW EVENT: add ${path33}`);
7524
- if (shouldWatch(path33, vaultPath2)) {
7525
- console.error(`[flywheel] ACCEPTED: add ${path33}`);
7526
- eventQueue.push("add", path33);
7596
+ watcher.on("add", (path34) => {
7597
+ console.error(`[flywheel] RAW EVENT: add ${path34}`);
7598
+ if (shouldWatch(path34, vaultPath2)) {
7599
+ console.error(`[flywheel] ACCEPTED: add ${path34}`);
7600
+ eventQueue.push("add", path34);
7527
7601
  } else {
7528
- console.error(`[flywheel] FILTERED: add ${path33}`);
7602
+ console.error(`[flywheel] FILTERED: add ${path34}`);
7529
7603
  }
7530
7604
  });
7531
- watcher.on("change", (path33) => {
7532
- console.error(`[flywheel] RAW EVENT: change ${path33}`);
7533
- if (shouldWatch(path33, vaultPath2)) {
7534
- console.error(`[flywheel] ACCEPTED: change ${path33}`);
7535
- eventQueue.push("change", path33);
7605
+ watcher.on("change", (path34) => {
7606
+ console.error(`[flywheel] RAW EVENT: change ${path34}`);
7607
+ if (shouldWatch(path34, vaultPath2)) {
7608
+ console.error(`[flywheel] ACCEPTED: change ${path34}`);
7609
+ eventQueue.push("change", path34);
7536
7610
  } else {
7537
- console.error(`[flywheel] FILTERED: change ${path33}`);
7611
+ console.error(`[flywheel] FILTERED: change ${path34}`);
7538
7612
  }
7539
7613
  });
7540
- watcher.on("unlink", (path33) => {
7541
- console.error(`[flywheel] RAW EVENT: unlink ${path33}`);
7542
- if (shouldWatch(path33, vaultPath2)) {
7543
- console.error(`[flywheel] ACCEPTED: unlink ${path33}`);
7544
- eventQueue.push("unlink", path33);
7614
+ watcher.on("unlink", (path34) => {
7615
+ console.error(`[flywheel] RAW EVENT: unlink ${path34}`);
7616
+ if (shouldWatch(path34, vaultPath2)) {
7617
+ console.error(`[flywheel] ACCEPTED: unlink ${path34}`);
7618
+ eventQueue.push("unlink", path34);
7545
7619
  } else {
7546
- console.error(`[flywheel] FILTERED: unlink ${path33}`);
7620
+ console.error(`[flywheel] FILTERED: unlink ${path34}`);
7547
7621
  }
7548
7622
  });
7549
7623
  watcher.on("ready", () => {
@@ -7722,7 +7796,7 @@ async function flushLogs() {
7722
7796
  init_vault();
7723
7797
  init_serverLog();
7724
7798
  init_vault_scope();
7725
- import * as fs7 from "fs";
7799
+ import * as fs8 from "fs";
7726
7800
  var EXCLUDED_DIRS3 = /* @__PURE__ */ new Set([
7727
7801
  ".obsidian",
7728
7802
  ".trash",
@@ -7790,11 +7864,11 @@ async function buildFTS5Index(vaultPath2) {
7790
7864
  const rows = [];
7791
7865
  for (const file of indexableFiles) {
7792
7866
  try {
7793
- const stats = fs7.statSync(file.absolutePath);
7867
+ const stats = fs8.statSync(file.absolutePath);
7794
7868
  if (stats.size > MAX_INDEX_FILE_SIZE) {
7795
7869
  continue;
7796
7870
  }
7797
- const raw = fs7.readFileSync(file.absolutePath, "utf-8");
7871
+ const raw = fs8.readFileSync(file.absolutePath, "utf-8");
7798
7872
  const { frontmatter, body } = splitFrontmatter(raw);
7799
7873
  const title = file.path.replace(/\.md$/, "").split("/").pop() || file.path;
7800
7874
  rows.push([file.path, title, frontmatter, body]);
@@ -7921,11 +7995,11 @@ function countFTS5Mentions(term) {
7921
7995
  init_embeddings();
7922
7996
 
7923
7997
  // src/core/read/taskCache.ts
7924
- import * as path12 from "path";
7998
+ import * as path13 from "path";
7925
7999
 
7926
8000
  // src/tools/read/tasks.ts
7927
- import * as fs8 from "fs";
7928
- import * as path11 from "path";
8001
+ import * as fs9 from "fs";
8002
+ import * as path12 from "path";
7929
8003
  var TASK_REGEX = /^(\s*)- \[([ xX\-])\]\s+(.+)$/;
7930
8004
  var TAG_REGEX2 = /#([a-zA-Z][a-zA-Z0-9_/-]*)/g;
7931
8005
  var DATE_REGEX = /\b(\d{4}-\d{2}-\d{2}|\d{1,2}\/\d{1,2}\/\d{2,4})\b/;
@@ -7951,7 +8025,7 @@ function extractDueDate(text) {
7951
8025
  async function extractTasksFromNote(notePath, absolutePath) {
7952
8026
  let content;
7953
8027
  try {
7954
- content = await fs8.promises.readFile(absolutePath, "utf-8");
8028
+ content = await fs9.promises.readFile(absolutePath, "utf-8");
7955
8029
  } catch {
7956
8030
  return [];
7957
8031
  }
@@ -7994,7 +8068,7 @@ async function getAllTasks(index, vaultPath2, options = {}) {
7994
8068
  const allTasks = [];
7995
8069
  for (const note of index.notes.values()) {
7996
8070
  if (folder && !note.path.startsWith(folder)) continue;
7997
- const absolutePath = path11.join(vaultPath2, note.path);
8071
+ const absolutePath = path12.join(vaultPath2, note.path);
7998
8072
  const tasks = await extractTasksFromNote(note.path, absolutePath);
7999
8073
  allTasks.push(...tasks);
8000
8074
  }
@@ -8038,7 +8112,7 @@ async function getAllTasks(index, vaultPath2, options = {}) {
8038
8112
  async function getTasksFromNote(index, notePath, vaultPath2, excludeTags = []) {
8039
8113
  const note = index.notes.get(notePath);
8040
8114
  if (!note) return null;
8041
- const absolutePath = path11.join(vaultPath2, notePath);
8115
+ const absolutePath = path12.join(vaultPath2, notePath);
8042
8116
  let tasks = await extractTasksFromNote(notePath, absolutePath);
8043
8117
  if (excludeTags.length > 0) {
8044
8118
  tasks = tasks.filter(
@@ -8100,7 +8174,7 @@ async function buildTaskCache(vaultPath2, index, excludeTags) {
8100
8174
  }
8101
8175
  const allRows = [];
8102
8176
  for (const notePath of notePaths) {
8103
- const absolutePath = path12.join(vaultPath2, notePath);
8177
+ const absolutePath = path13.join(vaultPath2, notePath);
8104
8178
  const tasks = await extractTasksFromNote(notePath, absolutePath);
8105
8179
  for (const task of tasks) {
8106
8180
  if (excludeTags?.length && excludeTags.some((t) => task.tags.includes(t))) {
@@ -8143,7 +8217,7 @@ async function updateTaskCacheForFile(vaultPath2, relativePath) {
8143
8217
  const db4 = getDb3();
8144
8218
  if (!db4) return;
8145
8219
  db4.prepare("DELETE FROM tasks WHERE path = ?").run(relativePath);
8146
- const absolutePath = path12.join(vaultPath2, relativePath);
8220
+ const absolutePath = path13.join(vaultPath2, relativePath);
8147
8221
  const tasks = await extractTasksFromNote(relativePath, absolutePath);
8148
8222
  if (tasks.length > 0) {
8149
8223
  const insertStmt = db4.prepare(`
@@ -8285,8 +8359,8 @@ function refreshIfStale(vaultPath2, index, excludeTags) {
8285
8359
  import { openStateDb, scanVaultEntities as scanVaultEntities4, getSessionId, getAllEntitiesFromDb as getAllEntitiesFromDb3, findEntityMatches as findEntityMatches2, getProtectedZones as getProtectedZones2, rangeOverlapsProtectedZone, detectImplicitEntities as detectImplicitEntities3, loadContentHashes, saveContentHashBatch, renameContentHash } from "@velvetmonkey/vault-core";
8286
8360
 
8287
8361
  // src/tools/read/graph.ts
8288
- import * as fs9 from "fs";
8289
- import * as path13 from "path";
8362
+ import * as fs10 from "fs";
8363
+ import * as path14 from "path";
8290
8364
  import { z } from "zod";
8291
8365
 
8292
8366
  // src/core/read/constants.ts
@@ -8737,8 +8811,8 @@ function requireIndex() {
8737
8811
  // src/tools/read/graph.ts
8738
8812
  async function getContext(vaultPath2, sourcePath, line, contextLines = 1) {
8739
8813
  try {
8740
- const fullPath = path13.join(vaultPath2, sourcePath);
8741
- const content = await fs9.promises.readFile(fullPath, "utf-8");
8814
+ const fullPath = path14.join(vaultPath2, sourcePath);
8815
+ const content = await fs10.promises.readFile(fullPath, "utf-8");
8742
8816
  const allLines = content.split("\n");
8743
8817
  let fmLines = 0;
8744
8818
  if (allLines[0]?.trimEnd() === "---") {
@@ -9211,15 +9285,15 @@ function registerWikilinkTools(server2, getIndex, getVaultPath, getStateDb2 = ()
9211
9285
  const weightedStats = getWeightedEntityStats(stateDb2);
9212
9286
  const statsMap = new Map(weightedStats.map((s) => [s.entity.toLowerCase(), s]));
9213
9287
  for (const suggestion of scored.detailed) {
9214
- const stat4 = statsMap.get(suggestion.entity.toLowerCase());
9215
- if (stat4) {
9216
- const posteriorMean = computePosteriorMean(stat4.weightedCorrect, stat4.weightedFp);
9217
- const totalObs = PRIOR_ALPHA + stat4.weightedCorrect + PRIOR_BETA + stat4.weightedFp;
9288
+ const stat5 = statsMap.get(suggestion.entity.toLowerCase());
9289
+ if (stat5) {
9290
+ const posteriorMean = computePosteriorMean(stat5.weightedCorrect, stat5.weightedFp);
9291
+ const totalObs = PRIOR_ALPHA + stat5.weightedCorrect + PRIOR_BETA + stat5.weightedFp;
9218
9292
  suggestion.suppressionContext = {
9219
9293
  posteriorMean: Math.round(posteriorMean * 1e3) / 1e3,
9220
9294
  totalObservations: Math.round(totalObs * 10) / 10,
9221
9295
  isSuppressed: totalObs >= SUPPRESSION_MIN_OBSERVATIONS && posteriorMean < SUPPRESSION_POSTERIOR_THRESHOLD,
9222
- falsePositiveRate: Math.round(stat4.weightedFpRate * 1e3) / 1e3
9296
+ falsePositiveRate: Math.round(stat5.weightedFpRate * 1e3) / 1e3
9223
9297
  };
9224
9298
  }
9225
9299
  }
@@ -9264,14 +9338,14 @@ function registerWikilinkTools(server2, getIndex, getVaultPath, getStateDb2 = ()
9264
9338
  };
9265
9339
  function findSimilarEntity2(target, entities) {
9266
9340
  const targetLower = target.toLowerCase();
9267
- for (const [name, path33] of entities) {
9341
+ for (const [name, path34] of entities) {
9268
9342
  if (name.startsWith(targetLower) || targetLower.startsWith(name)) {
9269
- return path33;
9343
+ return path34;
9270
9344
  }
9271
9345
  }
9272
- for (const [name, path33] of entities) {
9346
+ for (const [name, path34] of entities) {
9273
9347
  if (name.includes(targetLower) || targetLower.includes(name)) {
9274
- return path33;
9348
+ return path34;
9275
9349
  }
9276
9350
  }
9277
9351
  return void 0;
@@ -9493,7 +9567,7 @@ function registerWikilinkTools(server2, getIndex, getVaultPath, getStateDb2 = ()
9493
9567
  }
9494
9568
 
9495
9569
  // src/tools/read/health.ts
9496
- import * as fs10 from "fs";
9570
+ import * as fs11 from "fs";
9497
9571
  import { z as z3 } from "zod";
9498
9572
 
9499
9573
  // src/tools/read/periodic.ts
@@ -10112,7 +10186,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
10112
10186
  const indexErrorObj = getIndexError();
10113
10187
  let vaultAccessible = false;
10114
10188
  try {
10115
- fs10.accessSync(vaultPath2, fs10.constants.R_OK);
10189
+ fs11.accessSync(vaultPath2, fs11.constants.R_OK);
10116
10190
  vaultAccessible = true;
10117
10191
  } catch {
10118
10192
  vaultAccessible = false;
@@ -10354,8 +10428,8 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
10354
10428
  daily_counts: z3.record(z3.number())
10355
10429
  }).describe("Activity summary for the last 7 days")
10356
10430
  };
10357
- function isPeriodicNote3(path33) {
10358
- const filename = path33.split("/").pop() || "";
10431
+ function isPeriodicNote3(path34) {
10432
+ const filename = path34.split("/").pop() || "";
10359
10433
  const nameWithoutExt = filename.replace(/\.md$/, "");
10360
10434
  const patterns = [
10361
10435
  /^\d{4}-\d{2}-\d{2}$/,
@@ -10370,7 +10444,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
10370
10444
  // YYYY (yearly)
10371
10445
  ];
10372
10446
  const periodicFolders = ["daily", "weekly", "monthly", "quarterly", "yearly", "journal", "journals"];
10373
- const folder = path33.split("/")[0]?.toLowerCase() || "";
10447
+ const folder = path34.split("/")[0]?.toLowerCase() || "";
10374
10448
  return patterns.some((p) => p.test(nameWithoutExt)) || periodicFolders.includes(folder);
10375
10449
  }
10376
10450
  server2.registerTool(
@@ -10519,7 +10593,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
10519
10593
  detail: `Schema version ${SCHEMA_VERSION}`
10520
10594
  });
10521
10595
  try {
10522
- fs10.accessSync(vaultPath2, fs10.constants.R_OK | fs10.constants.W_OK);
10596
+ fs11.accessSync(vaultPath2, fs11.constants.R_OK | fs11.constants.W_OK);
10523
10597
  checks.push({ name: "vault_access", status: "ok", detail: `Vault readable and writable at ${vaultPath2}` });
10524
10598
  } catch {
10525
10599
  checks.push({ name: "vault_access", status: "error", detail: `Vault not accessible at ${vaultPath2}`, fix: "Check PROJECT_PATH environment variable and directory permissions" });
@@ -10605,11 +10679,11 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig2 = () =>
10605
10679
  try {
10606
10680
  const dbPath = stateDb2.db.name;
10607
10681
  if (dbPath && dbPath !== ":memory:") {
10608
- const dbStat = fs10.statSync(dbPath);
10682
+ const dbStat = fs11.statSync(dbPath);
10609
10683
  const dbSizeMb = Math.round(dbStat.size / 1024 / 1024 * 10) / 10;
10610
10684
  let walSizeMb = 0;
10611
10685
  try {
10612
- const walStat = fs10.statSync(dbPath + "-wal");
10686
+ const walStat = fs11.statSync(dbPath + "-wal");
10613
10687
  walSizeMb = Math.round(walStat.size / 1024 / 1024 * 10) / 10;
10614
10688
  } catch {
10615
10689
  }
@@ -10969,6 +11043,7 @@ function enrichEntityCompact(entityName, stateDb2, index) {
10969
11043
  const backlinks = index.backlinks.get(normalizedPath) || [];
10970
11044
  enriched.backlink_count = backlinks.length;
10971
11045
  if (note) {
11046
+ if (Object.keys(note.frontmatter).length > 0) enriched.frontmatter = note.frontmatter;
10972
11047
  if (note.tags.length > 0) enriched.tags = note.tags;
10973
11048
  if (note.outlinks.length > 0) {
10974
11049
  enriched.outlink_names = getOutlinkNames(note.outlinks, entityPath, index, stateDb2, COMPACT_OUTLINK_NAMES);
@@ -10985,6 +11060,7 @@ function enrichNoteCompact(notePath, stateDb2, index) {
10985
11060
  if (!note) return enriched;
10986
11061
  const normalizedPath = notePath.toLowerCase().replace(/\.md$/, "");
10987
11062
  const backlinks = index.backlinks.get(normalizedPath) || [];
11063
+ if (Object.keys(note.frontmatter).length > 0) enriched.frontmatter = note.frontmatter;
10988
11064
  if (note.tags.length > 0) enriched.tags = note.tags;
10989
11065
  enriched.backlink_count = backlinks.length;
10990
11066
  enriched.modified = note.modified.toISOString();
@@ -11065,13 +11141,13 @@ function multiHopBackfill(primaryResults, index, stateDb2, config = {}) {
11065
11141
  candidates.sort((a, b) => b.score - a.score);
11066
11142
  return candidates.slice(0, cfg.maxBackfill).map((c) => c.result);
11067
11143
  }
11068
- function scoreCandidate(path33, index, stateDb2) {
11069
- const note = index.notes.get(path33);
11144
+ function scoreCandidate(path34, index, stateDb2) {
11145
+ const note = index.notes.get(path34);
11070
11146
  const decay = recencyDecay(note?.modified);
11071
11147
  let hubScore = 1;
11072
11148
  if (stateDb2) {
11073
11149
  try {
11074
- const title = note?.title ?? path33.replace(/\.md$/, "").split("/").pop() ?? "";
11150
+ const title = note?.title ?? path34.replace(/\.md$/, "").split("/").pop() ?? "";
11075
11151
  const entity = getEntityByName3(stateDb2, title);
11076
11152
  if (entity) hubScore = entity.hubScore ?? 1;
11077
11153
  } catch {
@@ -11449,8 +11525,8 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb2) {
11449
11525
  }
11450
11526
 
11451
11527
  // src/tools/read/system.ts
11452
- import * as fs11 from "fs";
11453
- import * as path14 from "path";
11528
+ import * as fs12 from "fs";
11529
+ import * as path15 from "path";
11454
11530
  import { z as z5 } from "zod";
11455
11531
  import { scanVaultEntities as scanVaultEntities2, getEntityIndexFromDb as getEntityIndexFromDb2 } from "@velvetmonkey/vault-core";
11456
11532
 
@@ -11763,8 +11839,8 @@ function registerSystemTools(server2, getIndex, setIndex, getVaultPath, setConfi
11763
11839
  continue;
11764
11840
  }
11765
11841
  try {
11766
- const fullPath = path14.join(vaultPath2, note.path);
11767
- const content = await fs11.promises.readFile(fullPath, "utf-8");
11842
+ const fullPath = path15.join(vaultPath2, note.path);
11843
+ const content = await fs12.promises.readFile(fullPath, "utf-8");
11768
11844
  const lines = content.split("\n");
11769
11845
  for (let i = 0; i < lines.length; i++) {
11770
11846
  const line = lines[i];
@@ -12023,8 +12099,8 @@ function registerSystemTools(server2, getIndex, setIndex, getVaultPath, setConfi
12023
12099
  import { z as z6 } from "zod";
12024
12100
 
12025
12101
  // src/tools/read/structure.ts
12026
- import * as fs12 from "fs";
12027
- import * as path15 from "path";
12102
+ import * as fs13 from "fs";
12103
+ import * as path16 from "path";
12028
12104
  var HEADING_REGEX2 = /^(#{1,6})\s+(.+)$/;
12029
12105
  function extractHeadings2(content) {
12030
12106
  const lines = content.split("\n");
@@ -12078,10 +12154,10 @@ function buildSections(headings, totalLines) {
12078
12154
  async function getNoteStructure(index, notePath, vaultPath2) {
12079
12155
  const note = index.notes.get(notePath);
12080
12156
  if (!note) return null;
12081
- const absolutePath = path15.join(vaultPath2, notePath);
12157
+ const absolutePath = path16.join(vaultPath2, notePath);
12082
12158
  let content;
12083
12159
  try {
12084
- content = await fs12.promises.readFile(absolutePath, "utf-8");
12160
+ content = await fs13.promises.readFile(absolutePath, "utf-8");
12085
12161
  } catch {
12086
12162
  return null;
12087
12163
  }
@@ -12101,10 +12177,10 @@ async function getNoteStructure(index, notePath, vaultPath2) {
12101
12177
  async function getSectionContent(index, notePath, headingText, vaultPath2, includeSubheadings = true) {
12102
12178
  const note = index.notes.get(notePath);
12103
12179
  if (!note) return null;
12104
- const absolutePath = path15.join(vaultPath2, notePath);
12180
+ const absolutePath = path16.join(vaultPath2, notePath);
12105
12181
  let content;
12106
12182
  try {
12107
- content = await fs12.promises.readFile(absolutePath, "utf-8");
12183
+ content = await fs13.promises.readFile(absolutePath, "utf-8");
12108
12184
  } catch {
12109
12185
  return null;
12110
12186
  }
@@ -12143,10 +12219,10 @@ async function findSections(index, headingPattern, vaultPath2, folder) {
12143
12219
  const results = [];
12144
12220
  for (const note of index.notes.values()) {
12145
12221
  if (folder && !note.path.startsWith(folder)) continue;
12146
- const absolutePath = path15.join(vaultPath2, note.path);
12222
+ const absolutePath = path16.join(vaultPath2, note.path);
12147
12223
  let content;
12148
12224
  try {
12149
- content = await fs12.promises.readFile(absolutePath, "utf-8");
12225
+ content = await fs13.promises.readFile(absolutePath, "utf-8");
12150
12226
  } catch {
12151
12227
  continue;
12152
12228
  }
@@ -12178,30 +12254,30 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
12178
12254
  include_content: z6.boolean().default(true).describe("Include the text content under each top-level section. Set false to get structure only.")
12179
12255
  }
12180
12256
  },
12181
- async ({ path: path33, include_content }) => {
12257
+ async ({ path: path34, include_content }) => {
12182
12258
  const index = getIndex();
12183
12259
  const vaultPath2 = getVaultPath();
12184
- const result = await getNoteStructure(index, path33, vaultPath2);
12260
+ const result = await getNoteStructure(index, path34, vaultPath2);
12185
12261
  if (!result) {
12186
12262
  return {
12187
- content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path33 }, null, 2) }]
12263
+ content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path34 }, null, 2) }]
12188
12264
  };
12189
12265
  }
12190
12266
  if (include_content) {
12191
12267
  for (const section of result.sections) {
12192
- const sectionResult = await getSectionContent(index, path33, section.heading.text, vaultPath2, true);
12268
+ const sectionResult = await getSectionContent(index, path34, section.heading.text, vaultPath2, true);
12193
12269
  if (sectionResult) {
12194
12270
  section.content = sectionResult.content;
12195
12271
  }
12196
12272
  }
12197
12273
  }
12198
- const note = index.notes.get(path33);
12274
+ const note = index.notes.get(path34);
12199
12275
  const enriched = { ...result };
12200
12276
  if (note) {
12201
12277
  enriched.frontmatter = note.frontmatter;
12202
12278
  enriched.tags = note.tags;
12203
12279
  enriched.aliases = note.aliases;
12204
- const normalizedPath = path33.toLowerCase().replace(/\.md$/, "");
12280
+ const normalizedPath = path34.toLowerCase().replace(/\.md$/, "");
12205
12281
  const backlinks = index.backlinks.get(normalizedPath) || [];
12206
12282
  enriched.backlink_count = backlinks.length;
12207
12283
  enriched.outlink_count = note.outlinks.length;
@@ -12234,15 +12310,15 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
12234
12310
  include_subheadings: z6.boolean().default(true).describe("Include content under subheadings")
12235
12311
  }
12236
12312
  },
12237
- async ({ path: path33, heading, include_subheadings }) => {
12313
+ async ({ path: path34, heading, include_subheadings }) => {
12238
12314
  const index = getIndex();
12239
12315
  const vaultPath2 = getVaultPath();
12240
- const result = await getSectionContent(index, path33, heading, vaultPath2, include_subheadings);
12316
+ const result = await getSectionContent(index, path34, heading, vaultPath2, include_subheadings);
12241
12317
  if (!result) {
12242
12318
  return {
12243
12319
  content: [{ type: "text", text: JSON.stringify({
12244
12320
  error: "Section not found",
12245
- path: path33,
12321
+ path: path34,
12246
12322
  heading
12247
12323
  }, null, 2) }]
12248
12324
  };
@@ -12296,16 +12372,16 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
12296
12372
  offset: z6.coerce.number().default(0).describe("Number of results to skip (for pagination)")
12297
12373
  }
12298
12374
  },
12299
- async ({ path: path33, status, has_due_date, folder, tag, limit: requestedLimit, offset }) => {
12375
+ async ({ path: path34, status, has_due_date, folder, tag, limit: requestedLimit, offset }) => {
12300
12376
  const limit = Math.min(requestedLimit ?? 25, MAX_LIMIT);
12301
12377
  const index = getIndex();
12302
12378
  const vaultPath2 = getVaultPath();
12303
12379
  const config = getConfig2();
12304
- if (path33) {
12305
- const result2 = await getTasksFromNote(index, path33, vaultPath2, config.exclude_task_tags || []);
12380
+ if (path34) {
12381
+ const result2 = await getTasksFromNote(index, path34, vaultPath2, config.exclude_task_tags || []);
12306
12382
  if (!result2) {
12307
12383
  return {
12308
- content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path33 }, null, 2) }]
12384
+ content: [{ type: "text", text: JSON.stringify({ error: "Note not found", path: path34 }, null, 2) }]
12309
12385
  };
12310
12386
  }
12311
12387
  let filtered = result2;
@@ -12315,7 +12391,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
12315
12391
  const paged2 = filtered.slice(offset, offset + limit);
12316
12392
  return {
12317
12393
  content: [{ type: "text", text: JSON.stringify({
12318
- path: path33,
12394
+ path: path34,
12319
12395
  total_count: filtered.length,
12320
12396
  returned_count: paged2.length,
12321
12397
  open: result2.filter((t) => t.status === "open").length,
@@ -12470,8 +12546,8 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig2 = ()
12470
12546
 
12471
12547
  // src/tools/read/migrations.ts
12472
12548
  import { z as z7 } from "zod";
12473
- import * as fs13 from "fs/promises";
12474
- import * as path16 from "path";
12549
+ import * as fs14 from "fs/promises";
12550
+ import * as path17 from "path";
12475
12551
  import matter2 from "gray-matter";
12476
12552
  function getNotesInFolder(index, folder) {
12477
12553
  const notes = [];
@@ -12484,17 +12560,17 @@ function getNotesInFolder(index, folder) {
12484
12560
  return notes;
12485
12561
  }
12486
12562
  async function readFileContent(notePath, vaultPath2) {
12487
- const fullPath = path16.join(vaultPath2, notePath);
12563
+ const fullPath = path17.join(vaultPath2, notePath);
12488
12564
  try {
12489
- return await fs13.readFile(fullPath, "utf-8");
12565
+ return await fs14.readFile(fullPath, "utf-8");
12490
12566
  } catch {
12491
12567
  return null;
12492
12568
  }
12493
12569
  }
12494
12570
  async function writeFileContent(notePath, vaultPath2, content) {
12495
- const fullPath = path16.join(vaultPath2, notePath);
12571
+ const fullPath = path17.join(vaultPath2, notePath);
12496
12572
  try {
12497
- await fs13.writeFile(fullPath, content, "utf-8");
12573
+ await fs14.writeFile(fullPath, content, "utf-8");
12498
12574
  return true;
12499
12575
  } catch {
12500
12576
  return false;
@@ -12672,8 +12748,8 @@ function registerMigrationTools(server2, getIndex, getVaultPath) {
12672
12748
  }
12673
12749
 
12674
12750
  // src/tools/read/graphAnalysis.ts
12675
- import fs14 from "node:fs";
12676
- import path17 from "node:path";
12751
+ import fs15 from "node:fs";
12752
+ import path18 from "node:path";
12677
12753
  import { z as z8 } from "zod";
12678
12754
 
12679
12755
  // src/tools/read/schema.ts
@@ -13386,7 +13462,7 @@ function registerGraphAnalysisTools(server2, getIndex, getVaultPath, getStateDb2
13386
13462
  const scored = allNotes.map((note) => {
13387
13463
  let wordCount = 0;
13388
13464
  try {
13389
- const content = fs14.readFileSync(path17.join(vaultPath2, note.path), "utf-8");
13465
+ const content = fs15.readFileSync(path18.join(vaultPath2, note.path), "utf-8");
13390
13466
  const body = content.replace(/^---[\s\S]*?---\n?/, "");
13391
13467
  wordCount = body.split(/\s+/).filter((w) => w.length > 0).length;
13392
13468
  } catch {
@@ -14016,15 +14092,15 @@ function registerSemanticAnalysisTools(server2, getIndex, getVaultPath) {
14016
14092
  import { z as z11 } from "zod";
14017
14093
 
14018
14094
  // src/tools/read/bidirectional.ts
14019
- import * as fs15 from "fs/promises";
14020
- import * as path18 from "path";
14095
+ import * as fs16 from "fs/promises";
14096
+ import * as path19 from "path";
14021
14097
  import matter3 from "gray-matter";
14022
14098
  var PROSE_PATTERN_REGEX = /^([A-Za-z][A-Za-z0-9 _-]*):\s*(?:\[\[([^\]]+)\]\]|"([^"]+)"|([^\n]+?))\s*$/gm;
14023
14099
  var CODE_BLOCK_REGEX2 = /```[\s\S]*?```|`[^`\n]+`/g;
14024
14100
  async function readFileContent2(notePath, vaultPath2) {
14025
- const fullPath = path18.join(vaultPath2, notePath);
14101
+ const fullPath = path19.join(vaultPath2, notePath);
14026
14102
  try {
14027
- return await fs15.readFile(fullPath, "utf-8");
14103
+ return await fs16.readFile(fullPath, "utf-8");
14028
14104
  } catch {
14029
14105
  return null;
14030
14106
  }
@@ -14200,21 +14276,21 @@ async function suggestWikilinksInFrontmatter(index, notePath, vaultPath2) {
14200
14276
  }
14201
14277
 
14202
14278
  // src/tools/read/computed.ts
14203
- import * as fs16 from "fs/promises";
14204
- import * as path19 from "path";
14279
+ import * as fs17 from "fs/promises";
14280
+ import * as path20 from "path";
14205
14281
  import matter4 from "gray-matter";
14206
14282
  async function readFileContent3(notePath, vaultPath2) {
14207
- const fullPath = path19.join(vaultPath2, notePath);
14283
+ const fullPath = path20.join(vaultPath2, notePath);
14208
14284
  try {
14209
- return await fs16.readFile(fullPath, "utf-8");
14285
+ return await fs17.readFile(fullPath, "utf-8");
14210
14286
  } catch {
14211
14287
  return null;
14212
14288
  }
14213
14289
  }
14214
14290
  async function getFileStats(notePath, vaultPath2) {
14215
- const fullPath = path19.join(vaultPath2, notePath);
14291
+ const fullPath = path20.join(vaultPath2, notePath);
14216
14292
  try {
14217
- const stats = await fs16.stat(fullPath);
14293
+ const stats = await fs17.stat(fullPath);
14218
14294
  return {
14219
14295
  modified: stats.mtime,
14220
14296
  created: stats.birthtime
@@ -14344,7 +14420,7 @@ async function computeFrontmatter(index, notePath, vaultPath2, fields) {
14344
14420
 
14345
14421
  // src/tools/read/noteIntelligence.ts
14346
14422
  init_embeddings();
14347
- import fs17 from "node:fs";
14423
+ import fs18 from "node:fs";
14348
14424
  import nodePath from "node:path";
14349
14425
  function registerNoteIntelligenceTools(server2, getIndex, getVaultPath, getConfig2) {
14350
14426
  server2.registerTool(
@@ -14404,7 +14480,7 @@ function registerNoteIntelligenceTools(server2, getIndex, getVaultPath, getConfi
14404
14480
  }
14405
14481
  let noteContent;
14406
14482
  try {
14407
- noteContent = fs17.readFileSync(nodePath.join(vaultPath2, notePath), "utf-8");
14483
+ noteContent = fs18.readFileSync(nodePath.join(vaultPath2, notePath), "utf-8");
14408
14484
  } catch {
14409
14485
  return {
14410
14486
  content: [{ type: "text", text: JSON.stringify({
@@ -14483,8 +14559,8 @@ function registerNoteIntelligenceTools(server2, getIndex, getVaultPath, getConfi
14483
14559
  // src/tools/write/mutations.ts
14484
14560
  init_writer();
14485
14561
  import { z as z12 } from "zod";
14486
- import fs20 from "fs/promises";
14487
- import path22 from "path";
14562
+ import fs21 from "fs/promises";
14563
+ import path23 from "path";
14488
14564
 
14489
14565
  // src/core/write/validator.ts
14490
14566
  var TIMESTAMP_PATTERN = /^\*\*\d{2}:\d{2}\*\*/;
@@ -14702,8 +14778,8 @@ init_constants();
14702
14778
  init_writer();
14703
14779
  init_wikilinks();
14704
14780
  init_wikilinkFeedback();
14705
- import fs19 from "fs/promises";
14706
- import path21 from "path";
14781
+ import fs20 from "fs/promises";
14782
+ import path22 from "path";
14707
14783
  function formatMcpResult(result) {
14708
14784
  if (result.tokensEstimate === void 0 || result.tokensEstimate === 0) {
14709
14785
  result.tokensEstimate = estimateTokens(result);
@@ -14751,8 +14827,8 @@ async function handleGitCommit(vaultPath2, notePath, commit, prefix) {
14751
14827
  }
14752
14828
  async function getPolicyHint(vaultPath2) {
14753
14829
  try {
14754
- const policiesDir = path21.join(vaultPath2, ".claude", "policies");
14755
- const files = await fs19.readdir(policiesDir);
14830
+ const policiesDir = path22.join(vaultPath2, ".claude", "policies");
14831
+ const files = await fs20.readdir(policiesDir);
14756
14832
  const yamlFiles = files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
14757
14833
  if (yamlFiles.length > 0) {
14758
14834
  const names = yamlFiles.map((f) => f.replace(/\.ya?ml$/, "")).join(", ");
@@ -14763,9 +14839,9 @@ async function getPolicyHint(vaultPath2) {
14763
14839
  return "";
14764
14840
  }
14765
14841
  async function ensureFileExists(vaultPath2, notePath) {
14766
- const fullPath = path21.join(vaultPath2, notePath);
14842
+ const fullPath = path22.join(vaultPath2, notePath);
14767
14843
  try {
14768
- await fs19.access(fullPath);
14844
+ await fs20.access(fullPath);
14769
14845
  return null;
14770
14846
  } catch {
14771
14847
  const hint = await getPolicyHint(vaultPath2);
@@ -14941,17 +15017,17 @@ async function executeCreateNote(options) {
14941
15017
  if (!pathCheck.valid) {
14942
15018
  return { success: false, result: errorResult(notePath, `Path blocked: ${pathCheck.reason}`), filesWritten: [] };
14943
15019
  }
14944
- const fullPath = path21.join(vaultPath2, notePath);
15020
+ const fullPath = path22.join(vaultPath2, notePath);
14945
15021
  let fileExists = false;
14946
15022
  try {
14947
- await fs19.access(fullPath);
15023
+ await fs20.access(fullPath);
14948
15024
  fileExists = true;
14949
15025
  } catch {
14950
15026
  }
14951
15027
  if (fileExists && !overwrite) {
14952
15028
  return { success: false, result: errorResult(notePath, `File already exists: ${notePath}. Use overwrite=true to replace.`), filesWritten: [] };
14953
15029
  }
14954
- await fs19.mkdir(path21.dirname(fullPath), { recursive: true });
15030
+ await fs20.mkdir(path22.dirname(fullPath), { recursive: true });
14955
15031
  const { maybeApplyWikilinks: maybeApplyWikilinks2 } = await Promise.resolve().then(() => (init_wikilinks(), wikilinks_exports));
14956
15032
  const { content: processedContent } = maybeApplyWikilinks2(content, skipWikilinks ?? false, notePath);
14957
15033
  let finalFrontmatter = frontmatter;
@@ -14985,13 +15061,13 @@ async function executeDeleteNote(options) {
14985
15061
  if (!pathCheck.valid) {
14986
15062
  return { success: false, result: errorResult(notePath, `Path blocked: ${pathCheck.reason}`), filesWritten: [] };
14987
15063
  }
14988
- const fullPath = path21.join(vaultPath2, notePath);
15064
+ const fullPath = path22.join(vaultPath2, notePath);
14989
15065
  try {
14990
- await fs19.access(fullPath);
15066
+ await fs20.access(fullPath);
14991
15067
  } catch {
14992
15068
  return { success: false, result: errorResult(notePath, `File not found: ${notePath}`), filesWritten: [] };
14993
15069
  }
14994
- await fs19.unlink(fullPath);
15070
+ await fs20.unlink(fullPath);
14995
15071
  const result = successResult(notePath, `Deleted note: ${notePath}`, {});
14996
15072
  return { success: true, result, filesWritten: [notePath] };
14997
15073
  } catch (error) {
@@ -15009,10 +15085,10 @@ async function createNoteFromTemplate(vaultPath2, notePath, config) {
15009
15085
  if (!validation.valid) {
15010
15086
  throw new Error(`Path blocked: ${validation.reason}`);
15011
15087
  }
15012
- const fullPath = path22.join(vaultPath2, notePath);
15013
- await fs20.mkdir(path22.dirname(fullPath), { recursive: true });
15088
+ const fullPath = path23.join(vaultPath2, notePath);
15089
+ await fs21.mkdir(path23.dirname(fullPath), { recursive: true });
15014
15090
  const templates = config.templates || {};
15015
- const filename = path22.basename(notePath, ".md").toLowerCase();
15091
+ const filename = path23.basename(notePath, ".md").toLowerCase();
15016
15092
  let templatePath;
15017
15093
  const dailyPattern = /^\d{4}-\d{2}-\d{2}/;
15018
15094
  const weeklyPattern = /^\d{4}-W\d{2}/;
@@ -15033,10 +15109,10 @@ async function createNoteFromTemplate(vaultPath2, notePath, config) {
15033
15109
  let templateContent;
15034
15110
  if (templatePath) {
15035
15111
  try {
15036
- const absTemplatePath = path22.join(vaultPath2, templatePath);
15037
- templateContent = await fs20.readFile(absTemplatePath, "utf-8");
15112
+ const absTemplatePath = path23.join(vaultPath2, templatePath);
15113
+ templateContent = await fs21.readFile(absTemplatePath, "utf-8");
15038
15114
  } catch {
15039
- const title = path22.basename(notePath, ".md");
15115
+ const title = path23.basename(notePath, ".md");
15040
15116
  templateContent = `---
15041
15117
  ---
15042
15118
 
@@ -15045,7 +15121,7 @@ async function createNoteFromTemplate(vaultPath2, notePath, config) {
15045
15121
  templatePath = void 0;
15046
15122
  }
15047
15123
  } else {
15048
- const title = path22.basename(notePath, ".md");
15124
+ const title = path23.basename(notePath, ".md");
15049
15125
  templateContent = `---
15050
15126
  ---
15051
15127
 
@@ -15054,7 +15130,7 @@ async function createNoteFromTemplate(vaultPath2, notePath, config) {
15054
15130
  }
15055
15131
  const now = /* @__PURE__ */ new Date();
15056
15132
  const dateStr = now.toISOString().split("T")[0];
15057
- templateContent = templateContent.replace(/\{\{date\}\}/g, dateStr).replace(/\{\{title\}\}/g, path22.basename(notePath, ".md"));
15133
+ templateContent = templateContent.replace(/\{\{date\}\}/g, dateStr).replace(/\{\{title\}\}/g, path23.basename(notePath, ".md"));
15058
15134
  const matter9 = (await import("gray-matter")).default;
15059
15135
  const parsed = matter9(templateContent);
15060
15136
  if (!parsed.data.date) {
@@ -15095,9 +15171,9 @@ Example: vault_add_to_section({ path: "daily/2026-02-15.md", section: "Log", con
15095
15171
  let noteCreated = false;
15096
15172
  let templateUsed;
15097
15173
  if (create_if_missing && !dry_run) {
15098
- const fullPath = path22.join(vaultPath2, notePath);
15174
+ const fullPath = path23.join(vaultPath2, notePath);
15099
15175
  try {
15100
- await fs20.access(fullPath);
15176
+ await fs21.access(fullPath);
15101
15177
  } catch {
15102
15178
  const config = getConfig2();
15103
15179
  const result = await createNoteFromTemplate(vaultPath2, notePath, config);
@@ -15106,9 +15182,9 @@ Example: vault_add_to_section({ path: "daily/2026-02-15.md", section: "Log", con
15106
15182
  }
15107
15183
  }
15108
15184
  if (create_if_missing && dry_run) {
15109
- const fullPath = path22.join(vaultPath2, notePath);
15185
+ const fullPath = path23.join(vaultPath2, notePath);
15110
15186
  try {
15111
- await fs20.access(fullPath);
15187
+ await fs21.access(fullPath);
15112
15188
  } catch {
15113
15189
  return {
15114
15190
  content: [{
@@ -15593,8 +15669,8 @@ Example: vault_update_frontmatter({ path: "projects/alpha.md", frontmatter: { st
15593
15669
  init_writer();
15594
15670
  init_wikilinks();
15595
15671
  import { z as z15 } from "zod";
15596
- import fs21 from "fs/promises";
15597
- import path23 from "path";
15672
+ import fs22 from "fs/promises";
15673
+ import path24 from "path";
15598
15674
  function registerNoteTools(server2, getVaultPath, getIndex) {
15599
15675
  server2.tool(
15600
15676
  "vault_create_note",
@@ -15620,23 +15696,23 @@ function registerNoteTools(server2, getVaultPath, getIndex) {
15620
15696
  if (!validatePath(vaultPath2, notePath)) {
15621
15697
  return formatMcpResult(errorResult(notePath, "Invalid path: path traversal not allowed"));
15622
15698
  }
15623
- const fullPath = path23.join(vaultPath2, notePath);
15699
+ const fullPath = path24.join(vaultPath2, notePath);
15624
15700
  const existsCheck = await ensureFileExists(vaultPath2, notePath);
15625
15701
  if (existsCheck === null && !overwrite) {
15626
15702
  return formatMcpResult(errorResult(notePath, `File already exists: ${notePath}. Use overwrite=true to replace.`));
15627
15703
  }
15628
- const dir = path23.dirname(fullPath);
15629
- await fs21.mkdir(dir, { recursive: true });
15704
+ const dir = path24.dirname(fullPath);
15705
+ await fs22.mkdir(dir, { recursive: true });
15630
15706
  let effectiveContent = content;
15631
15707
  let effectiveFrontmatter = frontmatter;
15632
15708
  if (template) {
15633
- const templatePath = path23.join(vaultPath2, template);
15709
+ const templatePath = path24.join(vaultPath2, template);
15634
15710
  try {
15635
- const raw = await fs21.readFile(templatePath, "utf-8");
15711
+ const raw = await fs22.readFile(templatePath, "utf-8");
15636
15712
  const matter9 = (await import("gray-matter")).default;
15637
15713
  const parsed = matter9(raw);
15638
15714
  const dateStr = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
15639
- const title = path23.basename(notePath, ".md");
15715
+ const title = path24.basename(notePath, ".md");
15640
15716
  let templateContent = parsed.content.replace(/\{\{date\}\}/g, dateStr).replace(/\{\{title\}\}/g, title);
15641
15717
  if (content) {
15642
15718
  templateContent = templateContent.trimEnd() + "\n\n" + content;
@@ -15655,7 +15731,7 @@ function registerNoteTools(server2, getVaultPath, getIndex) {
15655
15731
  effectiveFrontmatter.created = now.toISOString();
15656
15732
  }
15657
15733
  const warnings = [];
15658
- const noteName = path23.basename(notePath, ".md");
15734
+ const noteName = path24.basename(notePath, ".md");
15659
15735
  const existingAliases = Array.isArray(effectiveFrontmatter?.aliases) ? effectiveFrontmatter.aliases.filter((a) => typeof a === "string") : [];
15660
15736
  const preflight = await checkPreflightSimilarity(noteName);
15661
15737
  if (preflight.existingEntity) {
@@ -15796,8 +15872,8 @@ ${sources}`;
15796
15872
  }
15797
15873
  return formatMcpResult(errorResult(notePath, previewLines.join("\n")));
15798
15874
  }
15799
- const fullPath = path23.join(vaultPath2, notePath);
15800
- await fs21.unlink(fullPath);
15875
+ const fullPath = path24.join(vaultPath2, notePath);
15876
+ await fs22.unlink(fullPath);
15801
15877
  const gitInfo = await handleGitCommit(vaultPath2, notePath, commit, "[Flywheel:Delete]");
15802
15878
  const message = backlinkWarning ? `Deleted note: ${notePath}
15803
15879
 
@@ -15817,8 +15893,8 @@ init_writer();
15817
15893
  init_git();
15818
15894
  init_wikilinks();
15819
15895
  import { z as z16 } from "zod";
15820
- import fs22 from "fs/promises";
15821
- import path24 from "path";
15896
+ import fs23 from "fs/promises";
15897
+ import path25 from "path";
15822
15898
  import matter6 from "gray-matter";
15823
15899
  function escapeRegex(str) {
15824
15900
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -15837,16 +15913,16 @@ function extractWikilinks2(content) {
15837
15913
  return wikilinks;
15838
15914
  }
15839
15915
  function getTitleFromPath(filePath) {
15840
- return path24.basename(filePath, ".md");
15916
+ return path25.basename(filePath, ".md");
15841
15917
  }
15842
15918
  async function findBacklinks(vaultPath2, targetTitle, targetAliases) {
15843
15919
  const results = [];
15844
15920
  const allTargets = [targetTitle, ...targetAliases].map((t) => t.toLowerCase());
15845
15921
  async function scanDir(dir) {
15846
15922
  const files = [];
15847
- const entries = await fs22.readdir(dir, { withFileTypes: true });
15923
+ const entries = await fs23.readdir(dir, { withFileTypes: true });
15848
15924
  for (const entry of entries) {
15849
- const fullPath = path24.join(dir, entry.name);
15925
+ const fullPath = path25.join(dir, entry.name);
15850
15926
  if (entry.isDirectory() && !entry.name.startsWith(".")) {
15851
15927
  files.push(...await scanDir(fullPath));
15852
15928
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -15857,8 +15933,8 @@ async function findBacklinks(vaultPath2, targetTitle, targetAliases) {
15857
15933
  }
15858
15934
  const allFiles = await scanDir(vaultPath2);
15859
15935
  for (const filePath of allFiles) {
15860
- const relativePath = path24.relative(vaultPath2, filePath);
15861
- const content = await fs22.readFile(filePath, "utf-8");
15936
+ const relativePath = path25.relative(vaultPath2, filePath);
15937
+ const content = await fs23.readFile(filePath, "utf-8");
15862
15938
  const wikilinks = extractWikilinks2(content);
15863
15939
  const matchingLinks = [];
15864
15940
  for (const link of wikilinks) {
@@ -15944,10 +16020,10 @@ function registerMoveNoteTools(server2, getVaultPath) {
15944
16020
  };
15945
16021
  return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
15946
16022
  }
15947
- const oldFullPath = path24.join(vaultPath2, oldPath);
15948
- const newFullPath = path24.join(vaultPath2, newPath);
16023
+ const oldFullPath = path25.join(vaultPath2, oldPath);
16024
+ const newFullPath = path25.join(vaultPath2, newPath);
15949
16025
  try {
15950
- await fs22.access(oldFullPath);
16026
+ await fs23.access(oldFullPath);
15951
16027
  } catch {
15952
16028
  const result2 = {
15953
16029
  success: false,
@@ -15957,7 +16033,7 @@ function registerMoveNoteTools(server2, getVaultPath) {
15957
16033
  return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
15958
16034
  }
15959
16035
  try {
15960
- await fs22.access(newFullPath);
16036
+ await fs23.access(newFullPath);
15961
16037
  const result2 = {
15962
16038
  success: false,
15963
16039
  message: `Destination already exists: ${newPath}`,
@@ -15966,7 +16042,7 @@ function registerMoveNoteTools(server2, getVaultPath) {
15966
16042
  return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
15967
16043
  } catch {
15968
16044
  }
15969
- const sourceContent = await fs22.readFile(oldFullPath, "utf-8");
16045
+ const sourceContent = await fs23.readFile(oldFullPath, "utf-8");
15970
16046
  const parsed = matter6(sourceContent);
15971
16047
  const aliases = extractAliases2(parsed.data);
15972
16048
  const oldTitle = getTitleFromPath(oldPath);
@@ -16026,9 +16102,9 @@ function registerMoveNoteTools(server2, getVaultPath) {
16026
16102
  };
16027
16103
  return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
16028
16104
  }
16029
- const destDir = path24.dirname(newFullPath);
16030
- await fs22.mkdir(destDir, { recursive: true });
16031
- await fs22.rename(oldFullPath, newFullPath);
16105
+ const destDir = path25.dirname(newFullPath);
16106
+ await fs23.mkdir(destDir, { recursive: true });
16107
+ await fs23.rename(oldFullPath, newFullPath);
16032
16108
  let gitCommit;
16033
16109
  let undoAvailable;
16034
16110
  let staleLockDetected;
@@ -16102,12 +16178,12 @@ function registerMoveNoteTools(server2, getVaultPath) {
16102
16178
  if (sanitizedTitle !== newTitle) {
16103
16179
  console.error(`[Flywheel] Title sanitized: "${newTitle}" \u2192 "${sanitizedTitle}"`);
16104
16180
  }
16105
- const fullPath = path24.join(vaultPath2, notePath);
16106
- const dir = path24.dirname(notePath);
16107
- const newPath = dir === "." ? `${sanitizedTitle}.md` : path24.join(dir, `${sanitizedTitle}.md`);
16108
- const newFullPath = path24.join(vaultPath2, newPath);
16181
+ const fullPath = path25.join(vaultPath2, notePath);
16182
+ const dir = path25.dirname(notePath);
16183
+ const newPath = dir === "." ? `${sanitizedTitle}.md` : path25.join(dir, `${sanitizedTitle}.md`);
16184
+ const newFullPath = path25.join(vaultPath2, newPath);
16109
16185
  try {
16110
- await fs22.access(fullPath);
16186
+ await fs23.access(fullPath);
16111
16187
  } catch {
16112
16188
  const result2 = {
16113
16189
  success: false,
@@ -16118,7 +16194,7 @@ function registerMoveNoteTools(server2, getVaultPath) {
16118
16194
  }
16119
16195
  if (fullPath !== newFullPath) {
16120
16196
  try {
16121
- await fs22.access(newFullPath);
16197
+ await fs23.access(newFullPath);
16122
16198
  const result2 = {
16123
16199
  success: false,
16124
16200
  message: `A note with this title already exists: ${newPath}`,
@@ -16128,7 +16204,7 @@ function registerMoveNoteTools(server2, getVaultPath) {
16128
16204
  } catch {
16129
16205
  }
16130
16206
  }
16131
- const sourceContent = await fs22.readFile(fullPath, "utf-8");
16207
+ const sourceContent = await fs23.readFile(fullPath, "utf-8");
16132
16208
  const parsed = matter6(sourceContent);
16133
16209
  const aliases = extractAliases2(parsed.data);
16134
16210
  const oldTitle = getTitleFromPath(notePath);
@@ -16188,7 +16264,7 @@ function registerMoveNoteTools(server2, getVaultPath) {
16188
16264
  return { content: [{ type: "text", text: JSON.stringify(result2, null, 2) }] };
16189
16265
  }
16190
16266
  if (fullPath !== newFullPath) {
16191
- await fs22.rename(fullPath, newFullPath);
16267
+ await fs23.rename(fullPath, newFullPath);
16192
16268
  }
16193
16269
  let gitCommit;
16194
16270
  let undoAvailable;
@@ -16236,7 +16312,7 @@ function registerMoveNoteTools(server2, getVaultPath) {
16236
16312
  init_writer();
16237
16313
  init_wikilinks();
16238
16314
  import { z as z17 } from "zod";
16239
- import fs23 from "fs/promises";
16315
+ import fs24 from "fs/promises";
16240
16316
  function registerMergeTools(server2, getVaultPath) {
16241
16317
  server2.tool(
16242
16318
  "merge_entities",
@@ -16363,7 +16439,7 @@ ${trimmedSource}`;
16363
16439
  }
16364
16440
  await writeVaultFile(vaultPath2, target_path, targetContent, targetFrontmatter, "LF", targetContentHash);
16365
16441
  const fullSourcePath = `${vaultPath2}/${source_path}`;
16366
- await fs23.unlink(fullSourcePath);
16442
+ await fs24.unlink(fullSourcePath);
16367
16443
  initializeEntityIndex(vaultPath2).catch((err) => {
16368
16444
  console.error(`[Flywheel] Entity cache rebuild failed: ${err}`);
16369
16445
  });
@@ -16635,8 +16711,8 @@ init_schema();
16635
16711
 
16636
16712
  // src/core/write/policy/parser.ts
16637
16713
  init_schema();
16638
- import fs24 from "fs/promises";
16639
- import path25 from "path";
16714
+ import fs25 from "fs/promises";
16715
+ import path26 from "path";
16640
16716
  import matter7 from "gray-matter";
16641
16717
  function parseYaml(content) {
16642
16718
  const parsed = matter7(`---
@@ -16661,7 +16737,7 @@ function parsePolicyString(yamlContent) {
16661
16737
  }
16662
16738
  async function loadPolicyFile(filePath) {
16663
16739
  try {
16664
- const content = await fs24.readFile(filePath, "utf-8");
16740
+ const content = await fs25.readFile(filePath, "utf-8");
16665
16741
  return parsePolicyString(content);
16666
16742
  } catch (error) {
16667
16743
  if (error.code === "ENOENT") {
@@ -16685,15 +16761,15 @@ async function loadPolicyFile(filePath) {
16685
16761
  }
16686
16762
  }
16687
16763
  async function loadPolicy(vaultPath2, policyName) {
16688
- const policiesDir = path25.join(vaultPath2, ".claude", "policies");
16689
- const policyPath = path25.join(policiesDir, `${policyName}.yaml`);
16764
+ const policiesDir = path26.join(vaultPath2, ".claude", "policies");
16765
+ const policyPath = path26.join(policiesDir, `${policyName}.yaml`);
16690
16766
  try {
16691
- await fs24.access(policyPath);
16767
+ await fs25.access(policyPath);
16692
16768
  return loadPolicyFile(policyPath);
16693
16769
  } catch {
16694
- const ymlPath = path25.join(policiesDir, `${policyName}.yml`);
16770
+ const ymlPath = path26.join(policiesDir, `${policyName}.yml`);
16695
16771
  try {
16696
- await fs24.access(ymlPath);
16772
+ await fs25.access(ymlPath);
16697
16773
  return loadPolicyFile(ymlPath);
16698
16774
  } catch {
16699
16775
  return {
@@ -16833,8 +16909,8 @@ init_schema();
16833
16909
  init_writer();
16834
16910
  init_git();
16835
16911
  init_wikilinks();
16836
- import fs26 from "fs/promises";
16837
- import path27 from "path";
16912
+ import fs27 from "fs/promises";
16913
+ import path28 from "path";
16838
16914
  init_constants();
16839
16915
  async function executeStep(step, vaultPath2, context, conditionResults, searchFn) {
16840
16916
  const { execute, reason } = shouldStepExecute(step.when, conditionResults);
@@ -17042,9 +17118,9 @@ async function executeToggleTask(params, vaultPath2) {
17042
17118
  const notePath = String(params.path || "");
17043
17119
  const task = String(params.task || "");
17044
17120
  const section = params.section ? String(params.section) : void 0;
17045
- const fullPath = path27.join(vaultPath2, notePath);
17121
+ const fullPath = path28.join(vaultPath2, notePath);
17046
17122
  try {
17047
- await fs26.access(fullPath);
17123
+ await fs27.access(fullPath);
17048
17124
  } catch {
17049
17125
  return { success: false, message: `File not found: ${notePath}`, path: notePath };
17050
17126
  }
@@ -17325,15 +17401,15 @@ async function rollbackChanges(vaultPath2, originalContents, filesModified) {
17325
17401
  const pathCheck = await validatePathSecure(vaultPath2, filePath);
17326
17402
  if (!pathCheck.valid) continue;
17327
17403
  const original = originalContents.get(filePath);
17328
- const fullPath = path27.join(vaultPath2, filePath);
17404
+ const fullPath = path28.join(vaultPath2, filePath);
17329
17405
  if (original === null) {
17330
17406
  try {
17331
- await fs26.unlink(fullPath);
17407
+ await fs27.unlink(fullPath);
17332
17408
  } catch {
17333
17409
  }
17334
17410
  } else if (original !== void 0) {
17335
17411
  try {
17336
- await fs26.writeFile(fullPath, original);
17412
+ await fs27.writeFile(fullPath, original);
17337
17413
  } catch {
17338
17414
  }
17339
17415
  }
@@ -17379,33 +17455,33 @@ async function previewPolicy(policy, vaultPath2, variables) {
17379
17455
  }
17380
17456
 
17381
17457
  // src/core/write/policy/storage.ts
17382
- import fs27 from "fs/promises";
17383
- import path28 from "path";
17458
+ import fs28 from "fs/promises";
17459
+ import path29 from "path";
17384
17460
  function getPoliciesDir(vaultPath2) {
17385
- return path28.join(vaultPath2, ".claude", "policies");
17461
+ return path29.join(vaultPath2, ".claude", "policies");
17386
17462
  }
17387
17463
  async function ensurePoliciesDir(vaultPath2) {
17388
17464
  const dir = getPoliciesDir(vaultPath2);
17389
- await fs27.mkdir(dir, { recursive: true });
17465
+ await fs28.mkdir(dir, { recursive: true });
17390
17466
  }
17391
17467
  async function listPolicies(vaultPath2) {
17392
17468
  const dir = getPoliciesDir(vaultPath2);
17393
17469
  const policies = [];
17394
17470
  try {
17395
- const files = await fs27.readdir(dir);
17471
+ const files = await fs28.readdir(dir);
17396
17472
  for (const file of files) {
17397
17473
  if (!file.endsWith(".yaml") && !file.endsWith(".yml")) {
17398
17474
  continue;
17399
17475
  }
17400
- const filePath = path28.join(dir, file);
17401
- const stat4 = await fs27.stat(filePath);
17402
- const content = await fs27.readFile(filePath, "utf-8");
17476
+ const filePath = path29.join(dir, file);
17477
+ const stat5 = await fs28.stat(filePath);
17478
+ const content = await fs28.readFile(filePath, "utf-8");
17403
17479
  const metadata = extractPolicyMetadata(content);
17404
17480
  policies.push({
17405
17481
  name: metadata.name || file.replace(/\.ya?ml$/, ""),
17406
17482
  description: metadata.description || "No description",
17407
17483
  path: file,
17408
- lastModified: stat4.mtime,
17484
+ lastModified: stat5.mtime,
17409
17485
  version: metadata.version || "1.0",
17410
17486
  requiredVariables: metadata.variables || []
17411
17487
  });
@@ -17422,10 +17498,10 @@ async function writePolicyRaw(vaultPath2, policyName, content, overwrite = false
17422
17498
  const dir = getPoliciesDir(vaultPath2);
17423
17499
  await ensurePoliciesDir(vaultPath2);
17424
17500
  const filename = `${policyName}.yaml`;
17425
- const filePath = path28.join(dir, filename);
17501
+ const filePath = path29.join(dir, filename);
17426
17502
  if (!overwrite) {
17427
17503
  try {
17428
- await fs27.access(filePath);
17504
+ await fs28.access(filePath);
17429
17505
  return {
17430
17506
  success: false,
17431
17507
  path: filename,
@@ -17442,7 +17518,7 @@ async function writePolicyRaw(vaultPath2, policyName, content, overwrite = false
17442
17518
  message: `Invalid policy: ${validation.errors.map((e) => e.message).join("; ")}`
17443
17519
  };
17444
17520
  }
17445
- await fs27.writeFile(filePath, content, "utf-8");
17521
+ await fs28.writeFile(filePath, content, "utf-8");
17446
17522
  return {
17447
17523
  success: true,
17448
17524
  path: filename,
@@ -17967,8 +18043,8 @@ function registerPolicyTools(server2, getVaultPath, getSearchFn) {
17967
18043
  import { z as z21 } from "zod";
17968
18044
 
17969
18045
  // src/core/write/tagRename.ts
17970
- import * as fs28 from "fs/promises";
17971
- import * as path29 from "path";
18046
+ import * as fs29 from "fs/promises";
18047
+ import * as path30 from "path";
17972
18048
  import matter8 from "gray-matter";
17973
18049
  import { getProtectedZones } from "@velvetmonkey/vault-core";
17974
18050
  function getNotesInFolder3(index, folder) {
@@ -18074,10 +18150,10 @@ async function renameTag(index, vaultPath2, oldTag, newTag, options) {
18074
18150
  const previews = [];
18075
18151
  let totalChanges = 0;
18076
18152
  for (const note of affectedNotes) {
18077
- const fullPath = path29.join(vaultPath2, note.path);
18153
+ const fullPath = path30.join(vaultPath2, note.path);
18078
18154
  let fileContent;
18079
18155
  try {
18080
- fileContent = await fs28.readFile(fullPath, "utf-8");
18156
+ fileContent = await fs29.readFile(fullPath, "utf-8");
18081
18157
  } catch {
18082
18158
  continue;
18083
18159
  }
@@ -18150,7 +18226,7 @@ async function renameTag(index, vaultPath2, oldTag, newTag, options) {
18150
18226
  previews.push(preview);
18151
18227
  if (!dryRun) {
18152
18228
  const newContent = matter8.stringify(updatedContent, fm);
18153
- await fs28.writeFile(fullPath, newContent, "utf-8");
18229
+ await fs29.writeFile(fullPath, newContent, "utf-8");
18154
18230
  }
18155
18231
  }
18156
18232
  }
@@ -19085,7 +19161,7 @@ function selectByMmr(candidates, limit, lambda = 0.7) {
19085
19161
  // src/core/read/snippets.ts
19086
19162
  init_embeddings();
19087
19163
  init_stemmer();
19088
- import * as fs29 from "fs";
19164
+ import * as fs30 from "fs";
19089
19165
  function stripFrontmatter(content) {
19090
19166
  const match = content.match(/^---[\s\S]*?---\n([\s\S]*)$/);
19091
19167
  return match ? match[1] : content;
@@ -19131,7 +19207,7 @@ async function extractBestSnippets(filePath, queryEmbedding, queryTokens, option
19131
19207
  const maxChunkChars = options?.maxChunkChars ?? 500;
19132
19208
  let content;
19133
19209
  try {
19134
- content = fs29.readFileSync(filePath, "utf-8");
19210
+ content = fs30.readFileSync(filePath, "utf-8");
19135
19211
  } catch {
19136
19212
  return [];
19137
19213
  }
@@ -19450,6 +19526,14 @@ function registerRecallTools(server2, getStateDb2, getVaultPath, getIndex) {
19450
19526
  const notes = results.filter((r) => r.type === "note");
19451
19527
  const memories = results.filter((r) => r.type === "memory");
19452
19528
  const index = getIndex?.() ?? null;
19529
+ const enrichedNotes = notes.map((n) => ({
19530
+ path: n.id,
19531
+ snippet: n.content,
19532
+ ...enrichNoteCompact(n.id, stateDb2, index)
19533
+ }));
19534
+ const hopResults = index ? multiHopBackfill(enrichedNotes, index, stateDb2, {
19535
+ maxBackfill: Math.max(5, (args.max_results ?? 10) - notes.length)
19536
+ }) : [];
19453
19537
  return {
19454
19538
  content: [{
19455
19539
  type: "text",
@@ -19459,20 +19543,12 @@ function registerRecallTools(server2, getStateDb2, getVaultPath, getIndex) {
19459
19543
  entities: entities.map((e) => ({
19460
19544
  name: e.id,
19461
19545
  description: e.content,
19462
- score: Math.round(e.score * 10) / 10,
19463
- breakdown: e.breakdown,
19464
19546
  ...enrichEntityCompact(e.id, stateDb2, index)
19465
19547
  })),
19466
- notes: notes.map((n) => ({
19467
- path: n.id,
19468
- snippet: n.content,
19469
- score: Math.round(n.score * 10) / 10,
19470
- ...enrichNoteCompact(n.id, stateDb2, index)
19471
- })),
19548
+ notes: [...enrichedNotes, ...hopResults],
19472
19549
  memories: memories.map((m) => ({
19473
19550
  key: m.id,
19474
- value: m.content,
19475
- score: Math.round(m.score * 10) / 10
19551
+ value: m.content
19476
19552
  }))
19477
19553
  }, null, 2)
19478
19554
  }]
@@ -19566,8 +19642,8 @@ function getNoteAccessFrequency(stateDb2, daysBack = 30) {
19566
19642
  }
19567
19643
  }
19568
19644
  }
19569
- return Array.from(noteMap.entries()).map(([path33, stats]) => ({
19570
- path: path33,
19645
+ return Array.from(noteMap.entries()).map(([path34, stats]) => ({
19646
+ path: path34,
19571
19647
  access_count: stats.access_count,
19572
19648
  last_accessed: stats.last_accessed,
19573
19649
  tools_used: Array.from(stats.tools)
@@ -19906,8 +19982,8 @@ function registerConfigTools(server2, getConfig2, setConfig, getStateDb2) {
19906
19982
  init_wikilinks();
19907
19983
  init_wikilinkFeedback();
19908
19984
  import { z as z28 } from "zod";
19909
- import * as fs30 from "fs/promises";
19910
- import * as path30 from "path";
19985
+ import * as fs31 from "fs/promises";
19986
+ import * as path31 from "path";
19911
19987
  import { scanVaultEntities as scanVaultEntities3, SCHEMA_VERSION as SCHEMA_VERSION2 } from "@velvetmonkey/vault-core";
19912
19988
  init_embeddings();
19913
19989
  function hasSkipWikilinks(content) {
@@ -19920,16 +19996,16 @@ function hasSkipWikilinks(content) {
19920
19996
  async function collectMarkdownFiles(dirPath, basePath, excludeFolders) {
19921
19997
  const results = [];
19922
19998
  try {
19923
- const entries = await fs30.readdir(dirPath, { withFileTypes: true });
19999
+ const entries = await fs31.readdir(dirPath, { withFileTypes: true });
19924
20000
  for (const entry of entries) {
19925
20001
  if (entry.name.startsWith(".")) continue;
19926
- const fullPath = path30.join(dirPath, entry.name);
20002
+ const fullPath = path31.join(dirPath, entry.name);
19927
20003
  if (entry.isDirectory()) {
19928
20004
  if (excludeFolders.some((f) => entry.name.toLowerCase() === f.toLowerCase())) continue;
19929
20005
  const sub = await collectMarkdownFiles(fullPath, basePath, excludeFolders);
19930
20006
  results.push(...sub);
19931
20007
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
19932
- results.push(path30.relative(basePath, fullPath));
20008
+ results.push(path31.relative(basePath, fullPath));
19933
20009
  }
19934
20010
  }
19935
20011
  } catch {
@@ -19959,7 +20035,7 @@ var EXCLUDE_FOLDERS = [
19959
20035
  ];
19960
20036
  function buildStatusReport(stateDb2, vaultPath2) {
19961
20037
  const recommendations = [];
19962
- const dbPath = path30.join(vaultPath2, ".flywheel", "state.db");
20038
+ const dbPath = path31.join(vaultPath2, ".flywheel", "state.db");
19963
20039
  const statedbExists = stateDb2 !== null;
19964
20040
  if (!statedbExists) {
19965
20041
  recommendations.push("StateDb not initialized \u2014 server needs restart");
@@ -20085,10 +20161,10 @@ async function executeRun(stateDb2, vaultPath2) {
20085
20161
  const allFiles = await collectMarkdownFiles(vaultPath2, vaultPath2, EXCLUDE_FOLDERS);
20086
20162
  let eligible = 0;
20087
20163
  for (const relativePath of allFiles) {
20088
- const fullPath = path30.join(vaultPath2, relativePath);
20164
+ const fullPath = path31.join(vaultPath2, relativePath);
20089
20165
  let content;
20090
20166
  try {
20091
- content = await fs30.readFile(fullPath, "utf-8");
20167
+ content = await fs31.readFile(fullPath, "utf-8");
20092
20168
  } catch {
20093
20169
  continue;
20094
20170
  }
@@ -20143,10 +20219,10 @@ async function executeEnrich(stateDb2, vaultPath2, dryRun, batchSize, offset) {
20143
20219
  const eligible = [];
20144
20220
  let notesSkipped = 0;
20145
20221
  for (const relativePath of allFiles) {
20146
- const fullPath = path30.join(vaultPath2, relativePath);
20222
+ const fullPath = path31.join(vaultPath2, relativePath);
20147
20223
  let content;
20148
20224
  try {
20149
- content = await fs30.readFile(fullPath, "utf-8");
20225
+ content = await fs31.readFile(fullPath, "utf-8");
20150
20226
  } catch {
20151
20227
  continue;
20152
20228
  }
@@ -20173,8 +20249,8 @@ async function executeEnrich(stateDb2, vaultPath2, dryRun, batchSize, offset) {
20173
20249
  match_count: result.linksAdded
20174
20250
  });
20175
20251
  if (!dryRun) {
20176
- const fullPath = path30.join(vaultPath2, relativePath);
20177
- await fs30.writeFile(fullPath, result.content, "utf-8");
20252
+ const fullPath = path31.join(vaultPath2, relativePath);
20253
+ await fs31.writeFile(fullPath, result.content, "utf-8");
20178
20254
  notesModified++;
20179
20255
  if (stateDb2) {
20180
20256
  trackWikilinkApplications(stateDb2, relativePath, entities);
@@ -20564,8 +20640,8 @@ import { z as z31 } from "zod";
20564
20640
 
20565
20641
  // src/core/read/similarity.ts
20566
20642
  init_embeddings();
20567
- import * as fs31 from "fs";
20568
- import * as path31 from "path";
20643
+ import * as fs32 from "fs";
20644
+ import * as path32 from "path";
20569
20645
  var STOP_WORDS = /* @__PURE__ */ new Set([
20570
20646
  "the",
20571
20647
  "be",
@@ -20702,10 +20778,10 @@ function extractKeyTerms(content, maxTerms = 15) {
20702
20778
  }
20703
20779
  function findSimilarNotes(db4, vaultPath2, index, sourcePath, options = {}) {
20704
20780
  const limit = options.limit ?? 10;
20705
- const absPath = path31.join(vaultPath2, sourcePath);
20781
+ const absPath = path32.join(vaultPath2, sourcePath);
20706
20782
  let content;
20707
20783
  try {
20708
- content = fs31.readFileSync(absPath, "utf-8");
20784
+ content = fs32.readFileSync(absPath, "utf-8");
20709
20785
  } catch {
20710
20786
  return [];
20711
20787
  }
@@ -20844,7 +20920,7 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb2) {
20844
20920
  diversity: z31.number().min(0).max(1).optional().describe("Relevance vs diversity tradeoff (0=max diversity, 1=pure relevance, default: 0.7)")
20845
20921
  }
20846
20922
  },
20847
- async ({ path: path33, limit, diversity }) => {
20923
+ async ({ path: path34, limit, diversity }) => {
20848
20924
  const index = getIndex();
20849
20925
  const vaultPath2 = getVaultPath();
20850
20926
  const stateDb2 = getStateDb2();
@@ -20853,10 +20929,10 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb2) {
20853
20929
  content: [{ type: "text", text: JSON.stringify({ error: "StateDb not available" }) }]
20854
20930
  };
20855
20931
  }
20856
- if (!index.notes.has(path33)) {
20932
+ if (!index.notes.has(path34)) {
20857
20933
  return {
20858
20934
  content: [{ type: "text", text: JSON.stringify({
20859
- error: `Note not found: ${path33}`,
20935
+ error: `Note not found: ${path34}`,
20860
20936
  hint: "Use the full relative path including .md extension"
20861
20937
  }, null, 2) }]
20862
20938
  };
@@ -20868,12 +20944,12 @@ function registerSimilarityTools(server2, getIndex, getVaultPath, getStateDb2) {
20868
20944
  };
20869
20945
  const useHybrid = hasEmbeddingsIndex();
20870
20946
  const method = useHybrid ? "hybrid" : "bm25";
20871
- const results = useHybrid ? await findHybridSimilarNotes(stateDb2.db, vaultPath2, index, path33, opts) : findSimilarNotes(stateDb2.db, vaultPath2, index, path33, opts);
20947
+ const results = useHybrid ? await findHybridSimilarNotes(stateDb2.db, vaultPath2, index, path34, opts) : findSimilarNotes(stateDb2.db, vaultPath2, index, path34, opts);
20872
20948
  return {
20873
20949
  content: [{
20874
20950
  type: "text",
20875
20951
  text: JSON.stringify({
20876
- source: path33,
20952
+ source: path34,
20877
20953
  method,
20878
20954
  count: results.length,
20879
20955
  similar: results
@@ -21748,7 +21824,7 @@ init_cooccurrence();
21748
21824
  init_retrievalCooccurrence();
21749
21825
  init_corrections();
21750
21826
  init_edgeWeights();
21751
- import * as fs32 from "node:fs/promises";
21827
+ import * as fs33 from "node:fs/promises";
21752
21828
  import { createHash as createHash3 } from "node:crypto";
21753
21829
 
21754
21830
  // src/resources/vault.ts
@@ -22294,7 +22370,7 @@ function applyToolGating(targetServer, categories, getDb4, registry, getVaultPat
22294
22370
  let totalBytes = 0;
22295
22371
  for (const p of notePaths) {
22296
22372
  try {
22297
- totalBytes += statSync5(path32.join(vp, p)).size;
22373
+ totalBytes += statSync5(path33.join(vp, p)).size;
22298
22374
  } catch {
22299
22375
  }
22300
22376
  }
@@ -22793,22 +22869,22 @@ async function buildStartupCatchupBatch(vaultPath2, sinceMs) {
22793
22869
  async function scanDir(dir) {
22794
22870
  let entries;
22795
22871
  try {
22796
- entries = await fs32.readdir(dir, { withFileTypes: true });
22872
+ entries = await fs33.readdir(dir, { withFileTypes: true });
22797
22873
  } catch {
22798
22874
  return;
22799
22875
  }
22800
22876
  for (const entry of entries) {
22801
- const fullPath = path32.join(dir, entry.name);
22877
+ const fullPath = path33.join(dir, entry.name);
22802
22878
  if (entry.isDirectory()) {
22803
22879
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
22804
22880
  await scanDir(fullPath);
22805
22881
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
22806
22882
  try {
22807
- const stat4 = await fs32.stat(fullPath);
22808
- if (stat4.mtimeMs > sinceMs) {
22883
+ const stat5 = await fs33.stat(fullPath);
22884
+ if (stat5.mtimeMs > sinceMs) {
22809
22885
  events.push({
22810
22886
  type: "upsert",
22811
- path: path32.relative(vaultPath2, fullPath),
22887
+ path: path33.relative(vaultPath2, fullPath),
22812
22888
  originalEvents: []
22813
22889
  });
22814
22890
  }
@@ -22998,8 +23074,8 @@ async function runPostIndexWork(index) {
22998
23074
  }
22999
23075
  } catch {
23000
23076
  try {
23001
- const dir = path32.dirname(rawPath);
23002
- const base = path32.basename(rawPath);
23077
+ const dir = path33.dirname(rawPath);
23078
+ const base = path33.basename(rawPath);
23003
23079
  const resolvedDir = realpathSync(dir).replace(/\\/g, "/");
23004
23080
  for (const prefix of vaultPrefixes) {
23005
23081
  if (resolvedDir.startsWith(prefix + "/") || resolvedDir === prefix) {
@@ -23031,7 +23107,7 @@ async function runPostIndexWork(index) {
23031
23107
  continue;
23032
23108
  }
23033
23109
  try {
23034
- const content = await fs32.readFile(path32.join(vaultPath, event.path), "utf-8");
23110
+ const content = await fs33.readFile(path33.join(vaultPath, event.path), "utf-8");
23035
23111
  const hash = createHash3("sha256").update(content).digest("hex").slice(0, 16);
23036
23112
  if (lastContentHashes.get(event.path) === hash) {
23037
23113
  serverLog("watcher", `Hash unchanged, skipping: ${event.path}`);
@@ -23112,7 +23188,7 @@ async function runPostIndexWork(index) {
23112
23188
  ...batch,
23113
23189
  events: filteredEvents.map((e) => ({
23114
23190
  ...e,
23115
- path: path32.join(vaultPath, e.path)
23191
+ path: path33.join(vaultPath, e.path)
23116
23192
  }))
23117
23193
  };
23118
23194
  const batchResult = await processBatch(vaultIndex, vaultPath, absoluteBatch);
@@ -23263,7 +23339,7 @@ async function runPostIndexWork(index) {
23263
23339
  removeEmbedding(event.path);
23264
23340
  embRemoved++;
23265
23341
  } else if (event.path.endsWith(".md")) {
23266
- const absPath = path32.join(vaultPath, event.path);
23342
+ const absPath = path33.join(vaultPath, event.path);
23267
23343
  await updateEmbedding(event.path, absPath);
23268
23344
  embUpdated++;
23269
23345
  }
@@ -23483,7 +23559,7 @@ async function runPostIndexWork(index) {
23483
23559
  for (const event of filteredEvents) {
23484
23560
  if (event.type === "delete" || !event.path.endsWith(".md")) continue;
23485
23561
  try {
23486
- const content = await fs32.readFile(path32.join(vaultPath, event.path), "utf-8");
23562
+ const content = await fs33.readFile(path33.join(vaultPath, event.path), "utf-8");
23487
23563
  const zones = getProtectedZones2(content);
23488
23564
  const linked = new Set(
23489
23565
  (forwardLinkResults.find((r) => r.file === event.path)?.resolved ?? []).map((n) => n.toLowerCase())
@@ -23524,7 +23600,7 @@ async function runPostIndexWork(index) {
23524
23600
  for (const event of filteredEvents) {
23525
23601
  if (event.type === "delete" || !event.path.endsWith(".md")) continue;
23526
23602
  try {
23527
- const content = await fs32.readFile(path32.join(vaultPath, event.path), "utf-8");
23603
+ const content = await fs33.readFile(path33.join(vaultPath, event.path), "utf-8");
23528
23604
  const removed = processImplicitFeedback(stateDb, event.path, content);
23529
23605
  for (const entity of removed) feedbackResults.push({ entity, file: event.path });
23530
23606
  } catch {
@@ -23604,7 +23680,7 @@ async function runPostIndexWork(index) {
23604
23680
  for (const event of filteredEvents) {
23605
23681
  if (event.type === "delete" || !event.path.endsWith(".md")) continue;
23606
23682
  try {
23607
- const content = await fs32.readFile(path32.join(vaultPath, event.path), "utf-8");
23683
+ const content = await fs33.readFile(path33.join(vaultPath, event.path), "utf-8");
23608
23684
  const zones = getProtectedZones2(content);
23609
23685
  const linkedSet = new Set(
23610
23686
  (forwardLinkResults.find((r) => r.file === event.path)?.resolved ?? []).concat(forwardLinkResults.find((r) => r.file === event.path)?.dead ?? []).map((n) => n.toLowerCase())
@@ -23637,7 +23713,7 @@ async function runPostIndexWork(index) {
23637
23713
  for (const event of filteredEvents) {
23638
23714
  if (event.type === "delete" || !event.path.endsWith(".md")) continue;
23639
23715
  try {
23640
- const content = await fs32.readFile(path32.join(vaultPath, event.path), "utf-8");
23716
+ const content = await fs33.readFile(path33.join(vaultPath, event.path), "utf-8");
23641
23717
  const result = await suggestRelatedLinks(content, {
23642
23718
  maxSuggestions: 5,
23643
23719
  strictness: "balanced",
@@ -23661,6 +23737,33 @@ async function runPostIndexWork(index) {
23661
23737
  if (suggestionResults.length > 0) {
23662
23738
  serverLog("watcher", `Suggestion scoring: ${suggestionResults.length} files scored`);
23663
23739
  }
23740
+ if (flywheelConfig?.proactive_linking !== false && suggestionResults.length > 0) {
23741
+ tracker.start("proactive_linking", { files: suggestionResults.length });
23742
+ try {
23743
+ const proactiveResults = [];
23744
+ for (const { file, top } of suggestionResults) {
23745
+ if (getNoteContext(file) === "daily") continue;
23746
+ try {
23747
+ const result = await applyProactiveSuggestions(file, vaultPath, top, {
23748
+ minScore: flywheelConfig?.proactive_min_score ?? 20,
23749
+ maxPerFile: flywheelConfig?.proactive_max_per_file ?? 3
23750
+ });
23751
+ if (result.applied.length > 0) {
23752
+ proactiveResults.push({ file, applied: result.applied });
23753
+ }
23754
+ } catch {
23755
+ }
23756
+ }
23757
+ const totalApplied = proactiveResults.reduce((s, r) => s + r.applied.length, 0);
23758
+ tracker.end({ files_modified: proactiveResults.length, total_applied: totalApplied, results: proactiveResults });
23759
+ if (totalApplied > 0) {
23760
+ serverLog("watcher", `Proactive linking: ${totalApplied} links in ${proactiveResults.length} files`);
23761
+ }
23762
+ } catch (e) {
23763
+ tracker.end({ error: String(e) });
23764
+ serverLog("watcher", `Proactive linking failed: ${e}`, "error");
23765
+ }
23766
+ }
23664
23767
  tracker.start("tag_scan", { files: filteredEvents.length });
23665
23768
  try {
23666
23769
  const tagDiffs = [];