@velvetmonkey/flywheel-memory 2.0.30 → 2.0.32
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 +79 -8
- package/package.json +84 -84
package/dist/index.js
CHANGED
|
@@ -3216,6 +3216,7 @@ import {
|
|
|
3216
3216
|
getEntityAliases,
|
|
3217
3217
|
applyWikilinks,
|
|
3218
3218
|
resolveAliasWikilinks,
|
|
3219
|
+
detectImplicitEntities,
|
|
3219
3220
|
getEntityIndexFromDb,
|
|
3220
3221
|
getStateDbMetadata,
|
|
3221
3222
|
getEntityByName,
|
|
@@ -5060,6 +5061,42 @@ function processWikilinks(content, notePath) {
|
|
|
5060
5061
|
firstOccurrenceOnly: true,
|
|
5061
5062
|
caseInsensitive: true
|
|
5062
5063
|
});
|
|
5064
|
+
const implicitMatches = detectImplicitEntities(result.content, {
|
|
5065
|
+
detectImplicit: true,
|
|
5066
|
+
implicitPatterns: ["proper-nouns", "single-caps", "quoted-terms", "camel-case", "acronyms"],
|
|
5067
|
+
minEntityLength: 3
|
|
5068
|
+
});
|
|
5069
|
+
const alreadyLinked = new Set(
|
|
5070
|
+
[...resolved.linkedEntities, ...result.linkedEntities].map((e) => e.toLowerCase())
|
|
5071
|
+
);
|
|
5072
|
+
for (const entity of sortedEntities) {
|
|
5073
|
+
const name = getEntityName2(entity);
|
|
5074
|
+
alreadyLinked.add(name.toLowerCase());
|
|
5075
|
+
const aliases = getEntityAliases(entity);
|
|
5076
|
+
for (const alias of aliases) {
|
|
5077
|
+
alreadyLinked.add(alias.toLowerCase());
|
|
5078
|
+
}
|
|
5079
|
+
}
|
|
5080
|
+
const currentNoteName = notePath ? notePath.replace(/\.md$/, "").split("/").pop()?.toLowerCase() : null;
|
|
5081
|
+
const newImplicits = implicitMatches.filter((m) => {
|
|
5082
|
+
const normalized = m.text.toLowerCase();
|
|
5083
|
+
if (alreadyLinked.has(normalized)) return false;
|
|
5084
|
+
if (currentNoteName && normalized === currentNoteName) return false;
|
|
5085
|
+
return true;
|
|
5086
|
+
});
|
|
5087
|
+
if (newImplicits.length > 0) {
|
|
5088
|
+
let processedContent = result.content;
|
|
5089
|
+
for (let i = newImplicits.length - 1; i >= 0; i--) {
|
|
5090
|
+
const m = newImplicits[i];
|
|
5091
|
+
processedContent = processedContent.slice(0, m.start) + `[[${m.text}]]` + processedContent.slice(m.end);
|
|
5092
|
+
}
|
|
5093
|
+
return {
|
|
5094
|
+
content: processedContent,
|
|
5095
|
+
linksAdded: resolved.linksAdded + result.linksAdded + newImplicits.length,
|
|
5096
|
+
linkedEntities: [...resolved.linkedEntities, ...result.linkedEntities],
|
|
5097
|
+
implicitEntities: newImplicits.map((m) => m.text)
|
|
5098
|
+
};
|
|
5099
|
+
}
|
|
5063
5100
|
return {
|
|
5064
5101
|
content: result.content,
|
|
5065
5102
|
linksAdded: resolved.linksAdded + result.linksAdded,
|
|
@@ -5076,9 +5113,11 @@ function maybeApplyWikilinks(content, skipWikilinks, notePath) {
|
|
|
5076
5113
|
if (moduleStateDb4 && notePath) {
|
|
5077
5114
|
trackWikilinkApplications(moduleStateDb4, notePath, result.linkedEntities);
|
|
5078
5115
|
}
|
|
5116
|
+
const implicitCount = result.implicitEntities?.length ?? 0;
|
|
5117
|
+
const implicitInfo = implicitCount > 0 ? ` + ${implicitCount} implicit: ${result.implicitEntities.join(", ")}` : "";
|
|
5079
5118
|
return {
|
|
5080
5119
|
content: result.content,
|
|
5081
|
-
wikilinkInfo: `Applied ${result.linksAdded} wikilink(s): ${result.linkedEntities.join(", ")}`
|
|
5120
|
+
wikilinkInfo: `Applied ${result.linksAdded} wikilink(s): ${result.linkedEntities.join(", ")}${implicitInfo}`
|
|
5082
5121
|
};
|
|
5083
5122
|
}
|
|
5084
5123
|
return { content: result.content };
|
|
@@ -7361,6 +7400,37 @@ function rowToEvent(row) {
|
|
|
7361
7400
|
steps: row.steps ? JSON.parse(row.steps) : null
|
|
7362
7401
|
};
|
|
7363
7402
|
}
|
|
7403
|
+
function getRecentPipelineEvent(stateDb2) {
|
|
7404
|
+
const row = stateDb2.db.prepare(
|
|
7405
|
+
"SELECT * FROM index_events WHERE steps IS NOT NULL ORDER BY timestamp DESC LIMIT 1"
|
|
7406
|
+
).get();
|
|
7407
|
+
return row ? rowToEvent(row) : null;
|
|
7408
|
+
}
|
|
7409
|
+
function computeEntityDiff(before, after) {
|
|
7410
|
+
const beforeMap = new Map(before.map((e) => [e.nameLower, e]));
|
|
7411
|
+
const afterMap = new Map(after.map((e) => [e.nameLower, e]));
|
|
7412
|
+
const added = [];
|
|
7413
|
+
const removed = [];
|
|
7414
|
+
const alias_changes = [];
|
|
7415
|
+
for (const [key, entity] of afterMap) {
|
|
7416
|
+
if (!beforeMap.has(key)) {
|
|
7417
|
+
added.push(entity.name);
|
|
7418
|
+
} else {
|
|
7419
|
+
const prev = beforeMap.get(key);
|
|
7420
|
+
const prevAliases = JSON.stringify(prev.aliases.sort());
|
|
7421
|
+
const currAliases = JSON.stringify(entity.aliases.sort());
|
|
7422
|
+
if (prevAliases !== currAliases) {
|
|
7423
|
+
alias_changes.push({ entity: entity.name, before: prev.aliases, after: entity.aliases });
|
|
7424
|
+
}
|
|
7425
|
+
}
|
|
7426
|
+
}
|
|
7427
|
+
for (const [key, entity] of beforeMap) {
|
|
7428
|
+
if (!afterMap.has(key)) {
|
|
7429
|
+
removed.push(entity.name);
|
|
7430
|
+
}
|
|
7431
|
+
}
|
|
7432
|
+
return { added, removed, alias_changes };
|
|
7433
|
+
}
|
|
7364
7434
|
function getRecentIndexEvents(stateDb2, limit = 20) {
|
|
7365
7435
|
const rows = stateDb2.db.prepare(
|
|
7366
7436
|
"SELECT * FROM index_events ORDER BY timestamp DESC LIMIT ?"
|
|
@@ -7558,9 +7628,8 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
|
|
|
7558
7628
|
let lastPipeline;
|
|
7559
7629
|
if (stateDb2) {
|
|
7560
7630
|
try {
|
|
7561
|
-
const
|
|
7562
|
-
if (
|
|
7563
|
-
const evt = events[0];
|
|
7631
|
+
const evt = getRecentPipelineEvent(stateDb2);
|
|
7632
|
+
if (evt && evt.steps && evt.steps.length > 0) {
|
|
7564
7633
|
lastPipeline = {
|
|
7565
7634
|
timestamp: evt.timestamp,
|
|
7566
7635
|
trigger: evt.trigger,
|
|
@@ -16101,12 +16170,14 @@ async function runPostIndexWork(index) {
|
|
|
16101
16170
|
setIndexState("ready");
|
|
16102
16171
|
tracker.end({ note_count: vaultIndex.notes.size, entity_count: vaultIndex.entities.size, tag_count: vaultIndex.tags.size });
|
|
16103
16172
|
serverLog("watcher", `Index rebuilt: ${vaultIndex.notes.size} notes, ${vaultIndex.entities.size} entities`);
|
|
16173
|
+
const entitiesBefore = stateDb ? getAllEntitiesFromDb3(stateDb) : [];
|
|
16104
16174
|
tracker.start("entity_scan", { note_count: vaultIndex.notes.size });
|
|
16105
16175
|
await updateEntitiesInStateDb();
|
|
16106
|
-
const
|
|
16107
|
-
|
|
16108
|
-
|
|
16109
|
-
|
|
16176
|
+
const entitiesAfter = stateDb ? getAllEntitiesFromDb3(stateDb) : [];
|
|
16177
|
+
const entityDiff = computeEntityDiff(entitiesBefore, entitiesAfter);
|
|
16178
|
+
tracker.end({ entity_count: entitiesAfter.length, ...entityDiff });
|
|
16179
|
+
serverLog("watcher", `Entity scan: ${entitiesAfter.length} entities`);
|
|
16180
|
+
tracker.start("hub_scores", { entity_count: entitiesAfter.length });
|
|
16110
16181
|
const hubUpdated = await exportHubScores(vaultIndex, stateDb);
|
|
16111
16182
|
tracker.end({ updated: hubUpdated ?? 0 });
|
|
16112
16183
|
serverLog("watcher", `Hub scores: ${hubUpdated ?? 0} updated`);
|
package/package.json
CHANGED
|
@@ -1,84 +1,84 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
-
"version": "2.0.
|
|
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
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"flywheel-memory": "dist/index.js"
|
|
9
|
-
},
|
|
10
|
-
"repository": {
|
|
11
|
-
"type": "git",
|
|
12
|
-
"url": "git+https://github.com/velvetmonkey/flywheel-memory.git",
|
|
13
|
-
"directory": "packages/mcp-server"
|
|
14
|
-
},
|
|
15
|
-
"bugs": {
|
|
16
|
-
"url": "https://github.com/velvetmonkey/flywheel-memory/issues"
|
|
17
|
-
},
|
|
18
|
-
"homepage": "https://github.com/velvetmonkey/flywheel-memory#readme",
|
|
19
|
-
"author": "velvetmonkey",
|
|
20
|
-
"keywords": [
|
|
21
|
-
"mcp",
|
|
22
|
-
"mcp-server",
|
|
23
|
-
"obsidian",
|
|
24
|
-
"pkm",
|
|
25
|
-
"markdown",
|
|
26
|
-
"knowledge-graph",
|
|
27
|
-
"wikilinks",
|
|
28
|
-
"backlinks",
|
|
29
|
-
"vault",
|
|
30
|
-
"claude",
|
|
31
|
-
"claude-code",
|
|
32
|
-
"local-first",
|
|
33
|
-
"daily-notes",
|
|
34
|
-
"zettelkasten"
|
|
35
|
-
],
|
|
36
|
-
"scripts": {
|
|
37
|
-
"build": "npx esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --packages=external && chmod +x dist/index.js",
|
|
38
|
-
"dev": "npx esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --packages=external --watch",
|
|
39
|
-
"test": "vitest run",
|
|
40
|
-
"test:watch": "vitest",
|
|
41
|
-
"test:read": "vitest run test/read/",
|
|
42
|
-
"test:write": "vitest run test/write/",
|
|
43
|
-
"test:security": "vitest run test/write/security/",
|
|
44
|
-
"test:stress": "vitest run test/write/stress/ test/write/battle-hardening/",
|
|
45
|
-
"test:coverage": "vitest run --coverage",
|
|
46
|
-
"test:ci": "vitest run --reporter=github-actions",
|
|
47
|
-
"lint": "tsc --noEmit",
|
|
48
|
-
"clean": "rm -rf dist",
|
|
49
|
-
"prepublishOnly": "npm run build"
|
|
50
|
-
},
|
|
51
|
-
"dependencies": {
|
|
52
|
-
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
53
|
-
"@velvetmonkey/vault-core": "^2.0.
|
|
54
|
-
"better-sqlite3": "^11.0.0",
|
|
55
|
-
"chokidar": "^4.0.0",
|
|
56
|
-
"gray-matter": "^4.0.3",
|
|
57
|
-
"simple-git": "^3.22.0",
|
|
58
|
-
"zod": "^3.22.4",
|
|
59
|
-
"@huggingface/transformers": "^3.8.1"
|
|
60
|
-
},
|
|
61
|
-
"devDependencies": {
|
|
62
|
-
"@types/better-sqlite3": "^7.6.0",
|
|
63
|
-
"@types/node": "^20.10.0",
|
|
64
|
-
"@vitest/coverage-v8": "^2.0.0",
|
|
65
|
-
"esbuild": "^0.24.0",
|
|
66
|
-
"fast-check": "^3.15.0",
|
|
67
|
-
"mcp-testing-kit": "^0.2.0",
|
|
68
|
-
"tsx": "^4.19.0",
|
|
69
|
-
"typescript": "^5.3.2",
|
|
70
|
-
"vitest": "^2.0.0"
|
|
71
|
-
},
|
|
72
|
-
"engines": {
|
|
73
|
-
"node": ">=18.0.0"
|
|
74
|
-
},
|
|
75
|
-
"license": "AGPL-3.0-only",
|
|
76
|
-
"files": [
|
|
77
|
-
"dist",
|
|
78
|
-
"README.md",
|
|
79
|
-
"LICENSE"
|
|
80
|
-
],
|
|
81
|
-
"publishConfig": {
|
|
82
|
-
"access": "public"
|
|
83
|
-
}
|
|
84
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@velvetmonkey/flywheel-memory",
|
|
3
|
+
"version": "2.0.32",
|
|
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
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"flywheel-memory": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/velvetmonkey/flywheel-memory.git",
|
|
13
|
+
"directory": "packages/mcp-server"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/velvetmonkey/flywheel-memory/issues"
|
|
17
|
+
},
|
|
18
|
+
"homepage": "https://github.com/velvetmonkey/flywheel-memory#readme",
|
|
19
|
+
"author": "velvetmonkey",
|
|
20
|
+
"keywords": [
|
|
21
|
+
"mcp",
|
|
22
|
+
"mcp-server",
|
|
23
|
+
"obsidian",
|
|
24
|
+
"pkm",
|
|
25
|
+
"markdown",
|
|
26
|
+
"knowledge-graph",
|
|
27
|
+
"wikilinks",
|
|
28
|
+
"backlinks",
|
|
29
|
+
"vault",
|
|
30
|
+
"claude",
|
|
31
|
+
"claude-code",
|
|
32
|
+
"local-first",
|
|
33
|
+
"daily-notes",
|
|
34
|
+
"zettelkasten"
|
|
35
|
+
],
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "npx esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --packages=external && chmod +x dist/index.js",
|
|
38
|
+
"dev": "npx esbuild src/index.ts --bundle --platform=node --format=esm --outfile=dist/index.js --packages=external --watch",
|
|
39
|
+
"test": "vitest run",
|
|
40
|
+
"test:watch": "vitest",
|
|
41
|
+
"test:read": "vitest run test/read/",
|
|
42
|
+
"test:write": "vitest run test/write/",
|
|
43
|
+
"test:security": "vitest run test/write/security/",
|
|
44
|
+
"test:stress": "vitest run test/write/stress/ test/write/battle-hardening/",
|
|
45
|
+
"test:coverage": "vitest run --coverage",
|
|
46
|
+
"test:ci": "vitest run --reporter=github-actions",
|
|
47
|
+
"lint": "tsc --noEmit",
|
|
48
|
+
"clean": "rm -rf dist",
|
|
49
|
+
"prepublishOnly": "npm run build"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@modelcontextprotocol/sdk": "^1.25.1",
|
|
53
|
+
"@velvetmonkey/vault-core": "^2.0.32",
|
|
54
|
+
"better-sqlite3": "^11.0.0",
|
|
55
|
+
"chokidar": "^4.0.0",
|
|
56
|
+
"gray-matter": "^4.0.3",
|
|
57
|
+
"simple-git": "^3.22.0",
|
|
58
|
+
"zod": "^3.22.4",
|
|
59
|
+
"@huggingface/transformers": "^3.8.1"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/better-sqlite3": "^7.6.0",
|
|
63
|
+
"@types/node": "^20.10.0",
|
|
64
|
+
"@vitest/coverage-v8": "^2.0.0",
|
|
65
|
+
"esbuild": "^0.24.0",
|
|
66
|
+
"fast-check": "^3.15.0",
|
|
67
|
+
"mcp-testing-kit": "^0.2.0",
|
|
68
|
+
"tsx": "^4.19.0",
|
|
69
|
+
"typescript": "^5.3.2",
|
|
70
|
+
"vitest": "^2.0.0"
|
|
71
|
+
},
|
|
72
|
+
"engines": {
|
|
73
|
+
"node": ">=18.0.0"
|
|
74
|
+
},
|
|
75
|
+
"license": "AGPL-3.0-only",
|
|
76
|
+
"files": [
|
|
77
|
+
"dist",
|
|
78
|
+
"README.md",
|
|
79
|
+
"LICENSE"
|
|
80
|
+
],
|
|
81
|
+
"publishConfig": {
|
|
82
|
+
"access": "public"
|
|
83
|
+
}
|
|
84
|
+
}
|