@velvetmonkey/flywheel-memory 2.0.30 → 2.0.31
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 +42 -10
- package/package.json +84 -84
package/dist/index.js
CHANGED
|
@@ -7361,6 +7361,37 @@ function rowToEvent(row) {
|
|
|
7361
7361
|
steps: row.steps ? JSON.parse(row.steps) : null
|
|
7362
7362
|
};
|
|
7363
7363
|
}
|
|
7364
|
+
function getRecentPipelineEvent(stateDb2) {
|
|
7365
|
+
const row = stateDb2.db.prepare(
|
|
7366
|
+
"SELECT * FROM index_events WHERE steps IS NOT NULL ORDER BY timestamp DESC LIMIT 1"
|
|
7367
|
+
).get();
|
|
7368
|
+
return row ? rowToEvent(row) : null;
|
|
7369
|
+
}
|
|
7370
|
+
function computeEntityDiff(before, after) {
|
|
7371
|
+
const beforeMap = new Map(before.map((e) => [e.nameLower, e]));
|
|
7372
|
+
const afterMap = new Map(after.map((e) => [e.nameLower, e]));
|
|
7373
|
+
const added = [];
|
|
7374
|
+
const removed = [];
|
|
7375
|
+
const alias_changes = [];
|
|
7376
|
+
for (const [key, entity] of afterMap) {
|
|
7377
|
+
if (!beforeMap.has(key)) {
|
|
7378
|
+
added.push(entity.name);
|
|
7379
|
+
} else {
|
|
7380
|
+
const prev = beforeMap.get(key);
|
|
7381
|
+
const prevAliases = JSON.stringify(prev.aliases.sort());
|
|
7382
|
+
const currAliases = JSON.stringify(entity.aliases.sort());
|
|
7383
|
+
if (prevAliases !== currAliases) {
|
|
7384
|
+
alias_changes.push({ entity: entity.name, before: prev.aliases, after: entity.aliases });
|
|
7385
|
+
}
|
|
7386
|
+
}
|
|
7387
|
+
}
|
|
7388
|
+
for (const [key, entity] of beforeMap) {
|
|
7389
|
+
if (!afterMap.has(key)) {
|
|
7390
|
+
removed.push(entity.name);
|
|
7391
|
+
}
|
|
7392
|
+
}
|
|
7393
|
+
return { added, removed, alias_changes };
|
|
7394
|
+
}
|
|
7364
7395
|
function getRecentIndexEvents(stateDb2, limit = 20) {
|
|
7365
7396
|
const rows = stateDb2.db.prepare(
|
|
7366
7397
|
"SELECT * FROM index_events ORDER BY timestamp DESC LIMIT ?"
|
|
@@ -7504,7 +7535,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
|
|
|
7504
7535
|
recommendations.push(`Index is ${Math.floor(indexAge / 60)} minutes old. Consider running refresh_index.`);
|
|
7505
7536
|
}
|
|
7506
7537
|
const noteCount = indexBuilt ? index.notes.size : 0;
|
|
7507
|
-
const
|
|
7538
|
+
const entityCount2 = indexBuilt ? index.entities.size : 0;
|
|
7508
7539
|
const tagCount = indexBuilt ? index.tags.size : 0;
|
|
7509
7540
|
let linkCount = 0;
|
|
7510
7541
|
if (indexBuilt) {
|
|
@@ -7558,9 +7589,8 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
|
|
|
7558
7589
|
let lastPipeline;
|
|
7559
7590
|
if (stateDb2) {
|
|
7560
7591
|
try {
|
|
7561
|
-
const
|
|
7562
|
-
if (
|
|
7563
|
-
const evt = events[0];
|
|
7592
|
+
const evt = getRecentPipelineEvent(stateDb2);
|
|
7593
|
+
if (evt && evt.steps && evt.steps.length > 0) {
|
|
7564
7594
|
lastPipeline = {
|
|
7565
7595
|
timestamp: evt.timestamp,
|
|
7566
7596
|
trigger: evt.trigger,
|
|
@@ -7585,7 +7615,7 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
|
|
|
7585
7615
|
index_age_seconds: indexAge,
|
|
7586
7616
|
index_stale: indexStale,
|
|
7587
7617
|
note_count: noteCount,
|
|
7588
|
-
entity_count:
|
|
7618
|
+
entity_count: entityCount2,
|
|
7589
7619
|
tag_count: tagCount,
|
|
7590
7620
|
link_count: linkCount,
|
|
7591
7621
|
periodic_notes: periodicNotes && periodicNotes.length > 0 ? periodicNotes : void 0,
|
|
@@ -14514,7 +14544,7 @@ function computeMetrics(index, stateDb2) {
|
|
|
14514
14544
|
}
|
|
14515
14545
|
}
|
|
14516
14546
|
const tagCount = index.tags.size;
|
|
14517
|
-
const
|
|
14547
|
+
const entityCount2 = index.entities.size;
|
|
14518
14548
|
const avgLinksPerNote = noteCount > 0 ? linkCount / noteCount : 0;
|
|
14519
14549
|
const possibleLinks = noteCount * (noteCount - 1);
|
|
14520
14550
|
const linkDensity = possibleLinks > 0 ? linkCount / possibleLinks : 0;
|
|
@@ -14536,7 +14566,7 @@ function computeMetrics(index, stateDb2) {
|
|
|
14536
14566
|
link_count: linkCount,
|
|
14537
14567
|
orphan_count: orphanCount,
|
|
14538
14568
|
tag_count: tagCount,
|
|
14539
|
-
entity_count:
|
|
14569
|
+
entity_count: entityCount2,
|
|
14540
14570
|
avg_links_per_note: Math.round(avgLinksPerNote * 100) / 100,
|
|
14541
14571
|
link_density: Math.round(linkDensity * 1e4) / 1e4,
|
|
14542
14572
|
connected_ratio: Math.round(connectedRatio * 1e3) / 1e3,
|
|
@@ -16101,11 +16131,13 @@ async function runPostIndexWork(index) {
|
|
|
16101
16131
|
setIndexState("ready");
|
|
16102
16132
|
tracker.end({ note_count: vaultIndex.notes.size, entity_count: vaultIndex.entities.size, tag_count: vaultIndex.tags.size });
|
|
16103
16133
|
serverLog("watcher", `Index rebuilt: ${vaultIndex.notes.size} notes, ${vaultIndex.entities.size} entities`);
|
|
16134
|
+
const entitiesBefore = stateDb ? getAllEntitiesFromDb3(stateDb) : [];
|
|
16104
16135
|
tracker.start("entity_scan", { note_count: vaultIndex.notes.size });
|
|
16105
16136
|
await updateEntitiesInStateDb();
|
|
16106
|
-
const
|
|
16107
|
-
|
|
16108
|
-
|
|
16137
|
+
const entitiesAfter = stateDb ? getAllEntitiesFromDb3(stateDb) : [];
|
|
16138
|
+
const entityDiff = computeEntityDiff(entitiesBefore, entitiesAfter);
|
|
16139
|
+
tracker.end({ entity_count: entitiesAfter.length, ...entityDiff });
|
|
16140
|
+
serverLog("watcher", `Entity scan: ${entitiesAfter.length} entities`);
|
|
16109
16141
|
tracker.start("hub_scores", { entity_count: entityCount });
|
|
16110
16142
|
const hubUpdated = await exportHubScores(vaultIndex, stateDb);
|
|
16111
16143
|
tracker.end({ updated: hubUpdated ?? 0 });
|
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.31",
|
|
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.31",
|
|
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
|
+
}
|