@velvetmonkey/flywheel-memory 2.0.45 → 2.0.47

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 +21 -8
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -3493,7 +3493,7 @@ import {
3493
3493
  } from "@velvetmonkey/vault-core";
3494
3494
 
3495
3495
  // src/core/write/wikilinkFeedback.ts
3496
- var MIN_FEEDBACK_COUNT = 10;
3496
+ var MIN_FEEDBACK_COUNT = 5;
3497
3497
  var SUPPRESSION_THRESHOLD = 0.3;
3498
3498
  var FEEDBACK_BOOST_MIN_SAMPLES = 5;
3499
3499
  var FOLDER_SUPPRESSION_MIN_COUNT = 5;
@@ -3544,7 +3544,7 @@ function getEntityStats(stateDb2) {
3544
3544
  SUM(CASE WHEN correct = 1 THEN 1 ELSE 0 END) as correct_count,
3545
3545
  SUM(CASE WHEN correct = 0 THEN 1 ELSE 0 END) as incorrect_count
3546
3546
  FROM wikilink_feedback
3547
- GROUP BY entity
3547
+ GROUP BY entity COLLATE NOCASE
3548
3548
  ORDER BY total DESC
3549
3549
  `).all();
3550
3550
  return rows.map((r) => {
@@ -3566,7 +3566,7 @@ function updateSuppressionList(stateDb2) {
3566
3566
  COUNT(*) as total,
3567
3567
  SUM(CASE WHEN correct = 0 THEN 1 ELSE 0 END) as false_positives
3568
3568
  FROM wikilink_feedback
3569
- GROUP BY entity
3569
+ GROUP BY entity COLLATE NOCASE
3570
3570
  HAVING total >= ?
3571
3571
  `).all(MIN_FEEDBACK_COUNT);
3572
3572
  let updated = 0;
@@ -3596,7 +3596,7 @@ function updateSuppressionList(stateDb2) {
3596
3596
  }
3597
3597
  function isSuppressed(stateDb2, entity, folder) {
3598
3598
  const row = stateDb2.db.prepare(
3599
- "SELECT entity FROM wikilink_suppressions WHERE entity = ?"
3599
+ "SELECT entity FROM wikilink_suppressions WHERE entity = ? COLLATE NOCASE"
3600
3600
  ).get(entity);
3601
3601
  if (row) return true;
3602
3602
  if (folder !== void 0) {
@@ -3605,7 +3605,7 @@ function isSuppressed(stateDb2, entity, folder) {
3605
3605
  COUNT(*) as total,
3606
3606
  SUM(CASE WHEN correct = 0 THEN 1 ELSE 0 END) as false_positives
3607
3607
  FROM wikilink_feedback
3608
- WHERE entity = ? AND (
3608
+ WHERE entity = ? COLLATE NOCASE AND (
3609
3609
  CASE WHEN ? = '' THEN note_path NOT LIKE '%/%'
3610
3610
  ELSE note_path LIKE ? || '/%'
3611
3611
  END
@@ -3629,7 +3629,7 @@ function getSuppressedCount(stateDb2) {
3629
3629
  function getSuppressedEntities(stateDb2) {
3630
3630
  return stateDb2.db.prepare(`
3631
3631
  SELECT s.entity, s.false_positive_rate,
3632
- COALESCE((SELECT COUNT(*) FROM wikilink_feedback WHERE entity = s.entity), 0) as total
3632
+ COALESCE((SELECT COUNT(*) FROM wikilink_feedback WHERE entity = s.entity COLLATE NOCASE), 0) as total
3633
3633
  FROM wikilink_suppressions s
3634
3634
  ORDER BY s.false_positive_rate DESC
3635
3635
  `).all();
@@ -8574,7 +8574,7 @@ function getSweepResults() {
8574
8574
 
8575
8575
  // src/tools/read/health.ts
8576
8576
  var STALE_THRESHOLD_SECONDS = 300;
8577
- function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () => ({}), getStateDb = () => null) {
8577
+ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () => ({}), getStateDb = () => null, getWatcherStatus2 = () => null) {
8578
8578
  const IndexProgressSchema = z3.object({
8579
8579
  parsed: z3.coerce.number().describe("Number of files parsed so far"),
8580
8580
  total: z3.coerce.number().describe("Total number of files to parse")
@@ -8647,6 +8647,8 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
8647
8647
  embeddings_count: z3.coerce.number().describe("Number of notes with semantic embeddings"),
8648
8648
  tasks_ready: z3.boolean().describe("Whether the task cache is ready to serve queries"),
8649
8649
  tasks_building: z3.boolean().describe("Whether the task cache is currently rebuilding"),
8650
+ watcher_state: z3.enum(["starting", "ready", "rebuilding", "dirty", "error"]).optional().describe("Current file watcher state"),
8651
+ watcher_pending: z3.coerce.number().optional().describe("Number of pending file events in the watcher queue"),
8650
8652
  dead_link_count: z3.coerce.number().describe("Total number of broken/dead wikilinks across the vault"),
8651
8653
  top_dead_link_targets: z3.array(z3.object({
8652
8654
  target: z3.string().describe("The dead link target"),
@@ -8830,6 +8832,8 @@ function registerHealthTools(server2, getIndex, getVaultPath, getConfig = () =>
8830
8832
  embeddings_count: getEmbeddingsCount(),
8831
8833
  tasks_ready: isTaskCacheReady(),
8832
8834
  tasks_building: isTaskCacheBuilding(),
8835
+ watcher_state: getWatcherStatus2()?.state,
8836
+ watcher_pending: getWatcherStatus2()?.pendingEvents,
8833
8837
  dead_link_count: deadLinkCount,
8834
8838
  top_dead_link_targets: topDeadLinkTargets,
8835
8839
  sweep: getSweepResults() ?? void 0,
@@ -17319,6 +17323,10 @@ try {
17319
17323
  var vaultIndex;
17320
17324
  var flywheelConfig = {};
17321
17325
  var stateDb = null;
17326
+ var watcherStatus = null;
17327
+ function getWatcherStatus() {
17328
+ return watcherStatus;
17329
+ }
17322
17330
  var PRESETS = {
17323
17331
  // Presets
17324
17332
  minimal: ["search", "structure", "append", "frontmatter", "notes"],
@@ -17543,7 +17551,7 @@ if (_originalRegisterTool) {
17543
17551
  }
17544
17552
  var categoryList = Array.from(enabledCategories).sort().join(", ");
17545
17553
  serverLog("server", `Tool categories: ${categoryList}`);
17546
- registerHealthTools(server, () => vaultIndex, () => vaultPath, () => flywheelConfig, () => stateDb);
17554
+ registerHealthTools(server, () => vaultIndex, () => vaultPath, () => flywheelConfig, () => stateDb, getWatcherStatus);
17547
17555
  registerSystemTools(
17548
17556
  server,
17549
17557
  () => vaultIndex,
@@ -18324,6 +18332,7 @@ async function runPostIndexWork(index) {
18324
18332
  const mentions = [];
18325
18333
  for (const entity of entitiesAfter) {
18326
18334
  if (linked.has(entity.nameLower)) continue;
18335
+ if (stateDb && isSuppressed(stateDb, entity.name)) continue;
18327
18336
  const matches = findEntityMatches2(content, entity.name, true);
18328
18337
  const valid = matches.some((m) => !rangeOverlapsProtectedZone(m.start, m.end, zones));
18329
18338
  if (valid) {
@@ -18471,6 +18480,7 @@ async function runPostIndexWork(index) {
18471
18480
  config,
18472
18481
  onBatch: handleBatch,
18473
18482
  onStateChange: (status) => {
18483
+ watcherStatus = status;
18474
18484
  if (status.state === "dirty") {
18475
18485
  serverLog("watcher", "Index may be stale", "warn");
18476
18486
  }
@@ -18526,3 +18536,6 @@ if (process.argv.includes("--init-semantic")) {
18526
18536
  process.on("beforeExit", async () => {
18527
18537
  await flushLogs();
18528
18538
  });
18539
+ export {
18540
+ getWatcherStatus
18541
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velvetmonkey/flywheel-memory",
3
- "version": "2.0.45",
3
+ "version": "2.0.47",
4
4
  "description": "MCP server that gives Claude full read/write access to your Obsidian vault. Select from 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.44",
55
+ "@velvetmonkey/vault-core": "^2.0.47",
56
56
  "better-sqlite3": "^11.0.0",
57
57
  "chokidar": "^4.0.0",
58
58
  "gray-matter": "^4.0.3",