@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.
- package/dist/index.js +432 -329
- 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
|
|
899
|
-
const posteriorMean = computePosteriorMean(
|
|
900
|
-
const totalObs = PRIOR_ALPHA +
|
|
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(
|
|
905
|
+
upsert.run(stat5.entity, 1 - posteriorMean);
|
|
906
906
|
updated++;
|
|
907
907
|
} else {
|
|
908
|
-
remove.run(
|
|
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
|
|
978
|
-
if (
|
|
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:
|
|
981
|
-
rawCount:
|
|
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
|
|
988
|
-
if (
|
|
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
|
|
992
|
-
if (
|
|
993
|
-
accuracy =
|
|
994
|
-
sampleCount =
|
|
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 =
|
|
997
|
-
sampleCount =
|
|
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(
|
|
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
|
|
1010
|
-
const posteriorMean = computePosteriorMean(
|
|
1011
|
-
const totalObs = PRIOR_ALPHA +
|
|
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(
|
|
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
|
|
1558
|
-
const ageMs = Date.now() -
|
|
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
|
|
1581
|
-
const ageMs = Date.now() -
|
|
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
|
|
4482
|
-
import
|
|
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 =
|
|
4811
|
-
const resolvedNote =
|
|
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 =
|
|
4816
|
-
let filename =
|
|
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 :
|
|
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 =
|
|
4853
|
-
const resolvedNote =
|
|
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 =
|
|
4941
|
+
const fullPath = path21.join(vaultPath2, notePath);
|
|
4868
4942
|
try {
|
|
4869
|
-
await
|
|
4870
|
-
const realPath = await
|
|
4871
|
-
const realVaultPath = await
|
|
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 =
|
|
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 =
|
|
4960
|
+
const parentDir = path21.dirname(fullPath);
|
|
4887
4961
|
try {
|
|
4888
|
-
await
|
|
4889
|
-
const realParentPath = await
|
|
4890
|
-
const realVaultPath = await
|
|
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 =
|
|
4916
|
-
const [rawContent,
|
|
4917
|
-
|
|
4918
|
-
|
|
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:
|
|
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 =
|
|
5044
|
+
const fullPath = path21.join(vaultPath2, notePath);
|
|
4971
5045
|
if (expectedHash) {
|
|
4972
|
-
const currentRaw = await
|
|
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
|
|
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,
|
|
5371
|
-
const parts =
|
|
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
|
|
5829
|
-
import
|
|
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 =
|
|
5956
|
+
const fullPath = path27.join(vaultPath2, notePath);
|
|
5883
5957
|
try {
|
|
5884
|
-
await
|
|
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 =
|
|
5971
|
+
const fullPath = path27.join(vaultPath2, notePath);
|
|
5898
5972
|
try {
|
|
5899
|
-
await
|
|
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 =
|
|
6002
|
+
const fullPath = path27.join(vaultPath2, notePath);
|
|
5929
6003
|
try {
|
|
5930
|
-
await
|
|
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 =
|
|
6033
|
+
const fullPath = path27.join(vaultPath2, notePath);
|
|
5960
6034
|
try {
|
|
5961
|
-
await
|
|
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
|
|
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(
|
|
6343
|
-
return
|
|
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,
|
|
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:
|
|
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
|
|
7125
|
+
const path34 = normalizePath(rawPath);
|
|
7052
7126
|
const now = Date.now();
|
|
7053
7127
|
const event = {
|
|
7054
7128
|
type,
|
|
7055
|
-
path:
|
|
7129
|
+
path: path34,
|
|
7056
7130
|
timestamp: now
|
|
7057
7131
|
};
|
|
7058
|
-
let pending = this.pending.get(
|
|
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(
|
|
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 ${
|
|
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(
|
|
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(
|
|
7096
|
-
const pending = this.pending.get(
|
|
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 ${
|
|
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:
|
|
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(
|
|
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 [
|
|
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:
|
|
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
|
|
7330
|
-
const stats = await
|
|
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", (
|
|
7523
|
-
console.error(`[flywheel] RAW EVENT: add ${
|
|
7524
|
-
if (shouldWatch(
|
|
7525
|
-
console.error(`[flywheel] ACCEPTED: add ${
|
|
7526
|
-
eventQueue.push("add",
|
|
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 ${
|
|
7602
|
+
console.error(`[flywheel] FILTERED: add ${path34}`);
|
|
7529
7603
|
}
|
|
7530
7604
|
});
|
|
7531
|
-
watcher.on("change", (
|
|
7532
|
-
console.error(`[flywheel] RAW EVENT: change ${
|
|
7533
|
-
if (shouldWatch(
|
|
7534
|
-
console.error(`[flywheel] ACCEPTED: change ${
|
|
7535
|
-
eventQueue.push("change",
|
|
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 ${
|
|
7611
|
+
console.error(`[flywheel] FILTERED: change ${path34}`);
|
|
7538
7612
|
}
|
|
7539
7613
|
});
|
|
7540
|
-
watcher.on("unlink", (
|
|
7541
|
-
console.error(`[flywheel] RAW EVENT: unlink ${
|
|
7542
|
-
if (shouldWatch(
|
|
7543
|
-
console.error(`[flywheel] ACCEPTED: unlink ${
|
|
7544
|
-
eventQueue.push("unlink",
|
|
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 ${
|
|
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
|
|
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 =
|
|
7867
|
+
const stats = fs8.statSync(file.absolutePath);
|
|
7794
7868
|
if (stats.size > MAX_INDEX_FILE_SIZE) {
|
|
7795
7869
|
continue;
|
|
7796
7870
|
}
|
|
7797
|
-
const raw =
|
|
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
|
|
7998
|
+
import * as path13 from "path";
|
|
7925
7999
|
|
|
7926
8000
|
// src/tools/read/tasks.ts
|
|
7927
|
-
import * as
|
|
7928
|
-
import * as
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
8289
|
-
import * as
|
|
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 =
|
|
8741
|
-
const content = await
|
|
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
|
|
9215
|
-
if (
|
|
9216
|
-
const posteriorMean = computePosteriorMean(
|
|
9217
|
-
const totalObs = PRIOR_ALPHA +
|
|
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(
|
|
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,
|
|
9341
|
+
for (const [name, path34] of entities) {
|
|
9268
9342
|
if (name.startsWith(targetLower) || targetLower.startsWith(name)) {
|
|
9269
|
-
return
|
|
9343
|
+
return path34;
|
|
9270
9344
|
}
|
|
9271
9345
|
}
|
|
9272
|
-
for (const [name,
|
|
9346
|
+
for (const [name, path34] of entities) {
|
|
9273
9347
|
if (name.includes(targetLower) || targetLower.includes(name)) {
|
|
9274
|
-
return
|
|
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
|
|
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
|
-
|
|
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(
|
|
10358
|
-
const filename =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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(
|
|
11069
|
-
const note = index.notes.get(
|
|
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 ??
|
|
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
|
|
11453
|
-
import * as
|
|
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 =
|
|
11767
|
-
const content = await
|
|
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
|
|
12027
|
-
import * as
|
|
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 =
|
|
12157
|
+
const absolutePath = path16.join(vaultPath2, notePath);
|
|
12082
12158
|
let content;
|
|
12083
12159
|
try {
|
|
12084
|
-
content = await
|
|
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 =
|
|
12180
|
+
const absolutePath = path16.join(vaultPath2, notePath);
|
|
12105
12181
|
let content;
|
|
12106
12182
|
try {
|
|
12107
|
-
content = await
|
|
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 =
|
|
12222
|
+
const absolutePath = path16.join(vaultPath2, note.path);
|
|
12147
12223
|
let content;
|
|
12148
12224
|
try {
|
|
12149
|
-
content = await
|
|
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:
|
|
12257
|
+
async ({ path: path34, include_content }) => {
|
|
12182
12258
|
const index = getIndex();
|
|
12183
12259
|
const vaultPath2 = getVaultPath();
|
|
12184
|
-
const result = await getNoteStructure(index,
|
|
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:
|
|
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,
|
|
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(
|
|
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 =
|
|
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:
|
|
12313
|
+
async ({ path: path34, heading, include_subheadings }) => {
|
|
12238
12314
|
const index = getIndex();
|
|
12239
12315
|
const vaultPath2 = getVaultPath();
|
|
12240
|
-
const result = await getSectionContent(index,
|
|
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:
|
|
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:
|
|
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 (
|
|
12305
|
-
const result2 = await getTasksFromNote(index,
|
|
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:
|
|
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:
|
|
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
|
|
12474
|
-
import * as
|
|
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 =
|
|
12563
|
+
const fullPath = path17.join(vaultPath2, notePath);
|
|
12488
12564
|
try {
|
|
12489
|
-
return await
|
|
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 =
|
|
12571
|
+
const fullPath = path17.join(vaultPath2, notePath);
|
|
12496
12572
|
try {
|
|
12497
|
-
await
|
|
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
|
|
12676
|
-
import
|
|
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 =
|
|
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
|
|
14020
|
-
import * as
|
|
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 =
|
|
14101
|
+
const fullPath = path19.join(vaultPath2, notePath);
|
|
14026
14102
|
try {
|
|
14027
|
-
return await
|
|
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
|
|
14204
|
-
import * as
|
|
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 =
|
|
14283
|
+
const fullPath = path20.join(vaultPath2, notePath);
|
|
14208
14284
|
try {
|
|
14209
|
-
return await
|
|
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 =
|
|
14291
|
+
const fullPath = path20.join(vaultPath2, notePath);
|
|
14216
14292
|
try {
|
|
14217
|
-
const stats = await
|
|
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
|
|
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 =
|
|
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
|
|
14487
|
-
import
|
|
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
|
|
14706
|
-
import
|
|
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 =
|
|
14755
|
-
const files = await
|
|
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 =
|
|
14842
|
+
const fullPath = path22.join(vaultPath2, notePath);
|
|
14767
14843
|
try {
|
|
14768
|
-
await
|
|
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 =
|
|
15020
|
+
const fullPath = path22.join(vaultPath2, notePath);
|
|
14945
15021
|
let fileExists = false;
|
|
14946
15022
|
try {
|
|
14947
|
-
await
|
|
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
|
|
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 =
|
|
15064
|
+
const fullPath = path22.join(vaultPath2, notePath);
|
|
14989
15065
|
try {
|
|
14990
|
-
await
|
|
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
|
|
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 =
|
|
15013
|
-
await
|
|
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 =
|
|
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 =
|
|
15037
|
-
templateContent = await
|
|
15112
|
+
const absTemplatePath = path23.join(vaultPath2, templatePath);
|
|
15113
|
+
templateContent = await fs21.readFile(absTemplatePath, "utf-8");
|
|
15038
15114
|
} catch {
|
|
15039
|
-
const title =
|
|
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 =
|
|
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,
|
|
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 =
|
|
15174
|
+
const fullPath = path23.join(vaultPath2, notePath);
|
|
15099
15175
|
try {
|
|
15100
|
-
await
|
|
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 =
|
|
15185
|
+
const fullPath = path23.join(vaultPath2, notePath);
|
|
15110
15186
|
try {
|
|
15111
|
-
await
|
|
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
|
|
15597
|
-
import
|
|
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 =
|
|
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 =
|
|
15629
|
-
await
|
|
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 =
|
|
15709
|
+
const templatePath = path24.join(vaultPath2, template);
|
|
15634
15710
|
try {
|
|
15635
|
-
const raw = await
|
|
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 =
|
|
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 =
|
|
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 =
|
|
15800
|
-
await
|
|
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
|
|
15821
|
-
import
|
|
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
|
|
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
|
|
15923
|
+
const entries = await fs23.readdir(dir, { withFileTypes: true });
|
|
15848
15924
|
for (const entry of entries) {
|
|
15849
|
-
const fullPath =
|
|
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 =
|
|
15861
|
-
const content = await
|
|
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 =
|
|
15948
|
-
const newFullPath =
|
|
16023
|
+
const oldFullPath = path25.join(vaultPath2, oldPath);
|
|
16024
|
+
const newFullPath = path25.join(vaultPath2, newPath);
|
|
15949
16025
|
try {
|
|
15950
|
-
await
|
|
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
|
|
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
|
|
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 =
|
|
16030
|
-
await
|
|
16031
|
-
await
|
|
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 =
|
|
16106
|
-
const dir =
|
|
16107
|
-
const newPath = dir === "." ? `${sanitizedTitle}.md` :
|
|
16108
|
-
const newFullPath =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
16639
|
-
import
|
|
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
|
|
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 =
|
|
16689
|
-
const policyPath =
|
|
16764
|
+
const policiesDir = path26.join(vaultPath2, ".claude", "policies");
|
|
16765
|
+
const policyPath = path26.join(policiesDir, `${policyName}.yaml`);
|
|
16690
16766
|
try {
|
|
16691
|
-
await
|
|
16767
|
+
await fs25.access(policyPath);
|
|
16692
16768
|
return loadPolicyFile(policyPath);
|
|
16693
16769
|
} catch {
|
|
16694
|
-
const ymlPath =
|
|
16770
|
+
const ymlPath = path26.join(policiesDir, `${policyName}.yml`);
|
|
16695
16771
|
try {
|
|
16696
|
-
await
|
|
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
|
|
16837
|
-
import
|
|
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 =
|
|
17121
|
+
const fullPath = path28.join(vaultPath2, notePath);
|
|
17046
17122
|
try {
|
|
17047
|
-
await
|
|
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 =
|
|
17404
|
+
const fullPath = path28.join(vaultPath2, filePath);
|
|
17329
17405
|
if (original === null) {
|
|
17330
17406
|
try {
|
|
17331
|
-
await
|
|
17407
|
+
await fs27.unlink(fullPath);
|
|
17332
17408
|
} catch {
|
|
17333
17409
|
}
|
|
17334
17410
|
} else if (original !== void 0) {
|
|
17335
17411
|
try {
|
|
17336
|
-
await
|
|
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
|
|
17383
|
-
import
|
|
17458
|
+
import fs28 from "fs/promises";
|
|
17459
|
+
import path29 from "path";
|
|
17384
17460
|
function getPoliciesDir(vaultPath2) {
|
|
17385
|
-
return
|
|
17461
|
+
return path29.join(vaultPath2, ".claude", "policies");
|
|
17386
17462
|
}
|
|
17387
17463
|
async function ensurePoliciesDir(vaultPath2) {
|
|
17388
17464
|
const dir = getPoliciesDir(vaultPath2);
|
|
17389
|
-
await
|
|
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
|
|
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 =
|
|
17401
|
-
const
|
|
17402
|
-
const content = await
|
|
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:
|
|
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 =
|
|
17501
|
+
const filePath = path29.join(dir, filename);
|
|
17426
17502
|
if (!overwrite) {
|
|
17427
17503
|
try {
|
|
17428
|
-
await
|
|
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
|
|
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
|
|
17971
|
-
import * as
|
|
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 =
|
|
18153
|
+
const fullPath = path30.join(vaultPath2, note.path);
|
|
18078
18154
|
let fileContent;
|
|
18079
18155
|
try {
|
|
18080
|
-
fileContent = await
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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(([
|
|
19570
|
-
path:
|
|
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
|
|
19910
|
-
import * as
|
|
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
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
20164
|
+
const fullPath = path31.join(vaultPath2, relativePath);
|
|
20089
20165
|
let content;
|
|
20090
20166
|
try {
|
|
20091
|
-
content = await
|
|
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 =
|
|
20222
|
+
const fullPath = path31.join(vaultPath2, relativePath);
|
|
20147
20223
|
let content;
|
|
20148
20224
|
try {
|
|
20149
|
-
content = await
|
|
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 =
|
|
20177
|
-
await
|
|
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
|
|
20568
|
-
import * as
|
|
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 =
|
|
20781
|
+
const absPath = path32.join(vaultPath2, sourcePath);
|
|
20706
20782
|
let content;
|
|
20707
20783
|
try {
|
|
20708
|
-
content =
|
|
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:
|
|
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(
|
|
20932
|
+
if (!index.notes.has(path34)) {
|
|
20857
20933
|
return {
|
|
20858
20934
|
content: [{ type: "text", text: JSON.stringify({
|
|
20859
|
-
error: `Note not found: ${
|
|
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,
|
|
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:
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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
|
|
22808
|
-
if (
|
|
22883
|
+
const stat5 = await fs33.stat(fullPath);
|
|
22884
|
+
if (stat5.mtimeMs > sinceMs) {
|
|
22809
22885
|
events.push({
|
|
22810
22886
|
type: "upsert",
|
|
22811
|
-
path:
|
|
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 =
|
|
23002
|
-
const base =
|
|
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
|
|
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:
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 = [];
|