@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.
Files changed (2) hide show
  1. package/dist/index.js +42 -10
  2. 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 entityCount = indexBuilt ? index.entities.size : 0;
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 events = getRecentIndexEvents(stateDb2, 1);
7562
- if (events.length > 0 && events[0].steps && events[0].steps.length > 0) {
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: entityCount,
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 entityCount = index.entities.size;
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: entityCount,
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 entityCount = stateDb ? getAllEntitiesFromDb3(stateDb).length : 0;
16107
- tracker.end({ entity_count: entityCount });
16108
- serverLog("watcher", `Entity scan: ${entityCount} entities`);
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.30",
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.30",
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
+ }