@velvetmonkey/flywheel-memory 2.0.37 → 2.0.39
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 +65 -5
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -6986,7 +6986,7 @@ function refreshIfStale(vaultPath2, index, excludeTags) {
|
|
|
6986
6986
|
}
|
|
6987
6987
|
|
|
6988
6988
|
// src/index.ts
|
|
6989
|
-
import { openStateDb, scanVaultEntities as scanVaultEntities3, getSessionId, getAllEntitiesFromDb as getAllEntitiesFromDb3 } from "@velvetmonkey/vault-core";
|
|
6989
|
+
import { openStateDb, scanVaultEntities as scanVaultEntities3, getSessionId, getAllEntitiesFromDb as getAllEntitiesFromDb3, findEntityMatches as findEntityMatches2, getProtectedZones as getProtectedZones2, rangeOverlapsProtectedZone } from "@velvetmonkey/vault-core";
|
|
6990
6990
|
|
|
6991
6991
|
// src/tools/read/graph.ts
|
|
6992
6992
|
import * as fs9 from "fs";
|
|
@@ -17369,6 +17369,7 @@ async function runPostIndexWork(index) {
|
|
|
17369
17369
|
const entitiesAfter = stateDb ? getAllEntitiesFromDb3(stateDb) : [];
|
|
17370
17370
|
const entityDiff = computeEntityDiff(entitiesBefore, entitiesAfter);
|
|
17371
17371
|
const categoryChanges = [];
|
|
17372
|
+
const descriptionChanges = [];
|
|
17372
17373
|
if (stateDb) {
|
|
17373
17374
|
const beforeMap = new Map(entitiesBefore.map((e) => [e.name, e]));
|
|
17374
17375
|
const insertChange = stateDb.db.prepare(
|
|
@@ -17380,9 +17381,17 @@ async function runPostIndexWork(index) {
|
|
|
17380
17381
|
insertChange.run(after.name, "category", before.category, after.category);
|
|
17381
17382
|
categoryChanges.push({ entity: after.name, from: before.category, to: after.category });
|
|
17382
17383
|
}
|
|
17384
|
+
if (before) {
|
|
17385
|
+
const oldDesc = before.description ?? null;
|
|
17386
|
+
const newDesc = after.description ?? null;
|
|
17387
|
+
if (oldDesc !== newDesc) {
|
|
17388
|
+
insertChange.run(after.name, "description", oldDesc, newDesc);
|
|
17389
|
+
descriptionChanges.push({ entity: after.name, from: oldDesc, to: newDesc });
|
|
17390
|
+
}
|
|
17391
|
+
}
|
|
17383
17392
|
}
|
|
17384
17393
|
}
|
|
17385
|
-
tracker.end({ entity_count: entitiesAfter.length, ...entityDiff, category_changes: categoryChanges });
|
|
17394
|
+
tracker.end({ entity_count: entitiesAfter.length, ...entityDiff, category_changes: categoryChanges, description_changes: descriptionChanges });
|
|
17386
17395
|
serverLog("watcher", `Entity scan: ${entitiesAfter.length} entities`);
|
|
17387
17396
|
tracker.start("hub_scores", { entity_count: entitiesAfter.length });
|
|
17388
17397
|
const hubUpdated = await exportHubScores(vaultIndex, stateDb);
|
|
@@ -17521,6 +17530,7 @@ async function runPostIndexWork(index) {
|
|
|
17521
17530
|
}
|
|
17522
17531
|
}
|
|
17523
17532
|
const linkDiffs = [];
|
|
17533
|
+
const survivedLinks = [];
|
|
17524
17534
|
if (stateDb) {
|
|
17525
17535
|
const upsertHistory = stateDb.db.prepare(`
|
|
17526
17536
|
INSERT INTO note_link_history (note_path, target) VALUES (?, ?)
|
|
@@ -17533,6 +17543,9 @@ async function runPostIndexWork(index) {
|
|
|
17533
17543
|
const markPositive = stateDb.db.prepare(`
|
|
17534
17544
|
UPDATE note_link_history SET last_positive_at = datetime('now') WHERE note_path = ? AND target = ?
|
|
17535
17545
|
`);
|
|
17546
|
+
const getEdgeCount = stateDb.db.prepare(
|
|
17547
|
+
"SELECT edits_survived FROM note_link_history WHERE note_path=? AND target=?"
|
|
17548
|
+
);
|
|
17536
17549
|
for (const entry of forwardLinkResults) {
|
|
17537
17550
|
const currentSet = /* @__PURE__ */ new Set([
|
|
17538
17551
|
...entry.resolved.map((n) => n.toLowerCase()),
|
|
@@ -17551,6 +17564,10 @@ async function runPostIndexWork(index) {
|
|
|
17551
17564
|
for (const link of currentSet) {
|
|
17552
17565
|
if (!previousSet.has(link)) continue;
|
|
17553
17566
|
upsertHistory.run(entry.file, link);
|
|
17567
|
+
const countRow = getEdgeCount.get(entry.file, link);
|
|
17568
|
+
if (countRow) {
|
|
17569
|
+
survivedLinks.push({ entity: link, file: entry.file, count: countRow.edits_survived });
|
|
17570
|
+
}
|
|
17554
17571
|
const hit = checkThreshold.get(entry.file, link);
|
|
17555
17572
|
if (hit) {
|
|
17556
17573
|
const entity = entitiesAfter.find(
|
|
@@ -17572,12 +17589,23 @@ async function runPostIndexWork(index) {
|
|
|
17572
17589
|
}
|
|
17573
17590
|
}
|
|
17574
17591
|
}
|
|
17592
|
+
const processedFiles = new Set(forwardLinkResults.map((r) => r.file));
|
|
17593
|
+
for (const event of filteredEvents) {
|
|
17594
|
+
if (event.type === "delete" || !event.path.endsWith(".md")) continue;
|
|
17595
|
+
if (processedFiles.has(event.path)) continue;
|
|
17596
|
+
const previousSet = getStoredNoteLinks(stateDb, event.path);
|
|
17597
|
+
if (previousSet.size > 0) {
|
|
17598
|
+
linkDiffs.push({ file: event.path, added: [], removed: [...previousSet] });
|
|
17599
|
+
updateStoredNoteLinks(stateDb, event.path, /* @__PURE__ */ new Set());
|
|
17600
|
+
}
|
|
17601
|
+
}
|
|
17575
17602
|
}
|
|
17576
17603
|
tracker.end({
|
|
17577
17604
|
total_resolved: totalResolved,
|
|
17578
17605
|
total_dead: totalDead,
|
|
17579
17606
|
links: forwardLinkResults,
|
|
17580
|
-
link_diffs: linkDiffs
|
|
17607
|
+
link_diffs: linkDiffs,
|
|
17608
|
+
survived: survivedLinks
|
|
17581
17609
|
});
|
|
17582
17610
|
serverLog("watcher", `Forward links: ${totalResolved} resolved, ${totalDead} dead`);
|
|
17583
17611
|
tracker.start("wikilink_check", { files: filteredEvents.length });
|
|
@@ -17607,8 +17635,40 @@ async function runPostIndexWork(index) {
|
|
|
17607
17635
|
trackedLinks.push({ file: diff.file, entities: diff.added });
|
|
17608
17636
|
}
|
|
17609
17637
|
}
|
|
17610
|
-
|
|
17611
|
-
|
|
17638
|
+
const mentionResults = [];
|
|
17639
|
+
for (const event of filteredEvents) {
|
|
17640
|
+
if (event.type === "delete" || !event.path.endsWith(".md")) continue;
|
|
17641
|
+
try {
|
|
17642
|
+
const content = await fs30.readFile(path31.join(vaultPath, event.path), "utf-8");
|
|
17643
|
+
const zones = getProtectedZones2(content);
|
|
17644
|
+
const linked = new Set(
|
|
17645
|
+
(forwardLinkResults.find((r) => r.file === event.path)?.resolved ?? []).map((n) => n.toLowerCase())
|
|
17646
|
+
);
|
|
17647
|
+
const mentions = [];
|
|
17648
|
+
for (const entity of entitiesAfter) {
|
|
17649
|
+
if (linked.has(entity.nameLower)) continue;
|
|
17650
|
+
const matches = findEntityMatches2(content, entity.name, true);
|
|
17651
|
+
const valid = matches.some((m) => !rangeOverlapsProtectedZone(m.start, m.end, zones));
|
|
17652
|
+
if (valid) {
|
|
17653
|
+
mentions.push(entity.name);
|
|
17654
|
+
continue;
|
|
17655
|
+
}
|
|
17656
|
+
for (const alias of entity.aliases ?? []) {
|
|
17657
|
+
const aliasMatches = findEntityMatches2(content, alias, true);
|
|
17658
|
+
if (aliasMatches.some((m) => !rangeOverlapsProtectedZone(m.start, m.end, zones))) {
|
|
17659
|
+
mentions.push(entity.name);
|
|
17660
|
+
break;
|
|
17661
|
+
}
|
|
17662
|
+
}
|
|
17663
|
+
}
|
|
17664
|
+
if (mentions.length > 0) {
|
|
17665
|
+
mentionResults.push({ file: event.path, entities: mentions });
|
|
17666
|
+
}
|
|
17667
|
+
} catch {
|
|
17668
|
+
}
|
|
17669
|
+
}
|
|
17670
|
+
tracker.end({ tracked: trackedLinks, mentions: mentionResults });
|
|
17671
|
+
serverLog("watcher", `Wikilink check: ${trackedLinks.reduce((s, t) => s + t.entities.length, 0)} tracked links in ${trackedLinks.length} files, ${mentionResults.reduce((s, m) => s + m.entities.length, 0)} unwikified mentions`);
|
|
17612
17672
|
tracker.start("implicit_feedback", { files: filteredEvents.length });
|
|
17613
17673
|
const feedbackResults = [];
|
|
17614
17674
|
if (stateDb) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.39",
|
|
4
4
|
"description": "MCP server that gives Claude full read/write access to your Obsidian vault. 42 tools for search, backlinks, graph queries, mutations, and hybrid semantic search.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
55
|
-
"@velvetmonkey/vault-core": "^2.0.
|
|
55
|
+
"@velvetmonkey/vault-core": "^2.0.39",
|
|
56
56
|
"better-sqlite3": "^11.0.0",
|
|
57
57
|
"chokidar": "^4.0.0",
|
|
58
58
|
"gray-matter": "^4.0.3",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"engines": {
|
|
75
75
|
"node": ">=18.0.0"
|
|
76
76
|
},
|
|
77
|
-
"license": "
|
|
77
|
+
"license": "Apache-2.0",
|
|
78
78
|
"files": [
|
|
79
79
|
"dist",
|
|
80
80
|
"README.md",
|