@velvetmonkey/flywheel-memory 2.0.85 → 2.0.86

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 +31 -30
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -7594,10 +7594,10 @@ function queryTasksFromCache(options) {
7594
7594
  if (!db3) {
7595
7595
  throw new Error("Task cache database not initialized.");
7596
7596
  }
7597
- const { status = "all", folder, tag, excludeTags = [], has_due_date, limit, offset = 0 } = options;
7597
+ const { status, folder, tag, excludeTags = [], has_due_date, limit, offset = 0 } = options;
7598
7598
  const conditions = [];
7599
7599
  const params = [];
7600
- if (status !== "all") {
7600
+ if (status) {
7601
7601
  conditions.push("status = ?");
7602
7602
  params.push(status);
7603
7603
  }
@@ -9774,11 +9774,11 @@ function sortNotes(notes, sortBy, order) {
9774
9774
  function registerQueryTools(server2, getIndex, getVaultPath, getStateDb) {
9775
9775
  server2.tool(
9776
9776
  "search",
9777
- 'Search the vault \u2014 always try this before reading files. Returns frontmatter, backlinks (with lines), outlinks (with lines + exists), headings, content snippet or preview, entity metadata, and timestamps for every hit.\n\nSearch the vault across metadata, content, and entities. Scope controls what to search: "metadata" for frontmatter/tags/folders, "content" for full-text search (FTS5), "entities" for people/projects/technologies, "all" (default) tries metadata then falls back to content search. When embeddings have been built (via init_semantic), content and all scopes automatically include embedding-based results via hybrid ranking.\n\nExample: search({ query: "quarterly review", scope: "content", limit: 5 })\nExample: search({ where: { type: "project", status: "active" }, scope: "metadata" })',
9777
+ 'Search the vault \u2014 always try this before reading files. Returns frontmatter, backlinks (with lines), outlinks (with lines + exists), headings, content snippet or preview, entity metadata, and timestamps for every hit.\n\nSearches across metadata (frontmatter/tags/folders), content (FTS5 full-text + hybrid semantic), and entities (people/projects/technologies). Uses filters to narrow by frontmatter fields, tags, folders, or dates. Hybrid semantic results are automatically included when embeddings have been built (via init_semantic).\n\nExample: search({ query: "quarterly review", limit: 5 })\nExample: search({ where: { type: "project", status: "active" } })',
9778
9778
  {
9779
- query: z4.string().optional().describe('Search query text. Required for scope "content", "entities", "all". For "metadata" scope, use filters instead.'),
9780
- scope: z4.enum(["metadata", "content", "entities", "all"]).default("all").describe("What to search: metadata (frontmatter/tags/folders), content (FTS5 full-text), entities (people/projects), all (metadata then content). Semantic results are automatically included when embeddings have been built (via init_semantic)."),
9781
- // Metadata filters (used with scope "metadata" or "all")
9779
+ query: z4.string().optional().describe("Search query text. Required unless using metadata filters (where, has_tag, folder, etc.)"),
9780
+ scope: z4.enum(["metadata", "content", "entities", "all"]).optional().describe('Narrow to a specific search type. Omit for best results (searches everything). Use "metadata" for frontmatter-only queries, "entities" for entity lookup.'),
9781
+ // Metadata filters
9782
9782
  where: z4.record(z4.unknown()).optional().describe('Frontmatter filters as key-value pairs. Example: { "type": "project", "status": "active" }'),
9783
9783
  has_tag: z4.string().optional().describe("Filter to notes with this tag"),
9784
9784
  has_any_tag: z4.array(z4.string()).optional().describe("Filter to notes with any of these tags"),
@@ -9799,7 +9799,8 @@ function registerQueryTools(server2, getIndex, getVaultPath, getStateDb) {
9799
9799
  // Context boost (edge weights)
9800
9800
  context_note: z4.string().optional().describe("Path of the note providing context. When set, results connected to this note via weighted edges get an RRF boost.")
9801
9801
  },
9802
- async ({ query, scope, where, has_tag, has_any_tag, has_all_tags, include_children, folder, title_contains, modified_after, modified_before, sort_by, order, prefix, limit: requestedLimit, context_note }) => {
9802
+ async ({ query, scope: rawScope, where, has_tag, has_any_tag, has_all_tags, include_children, folder, title_contains, modified_after, modified_before, sort_by, order, prefix, limit: requestedLimit, context_note }) => {
9803
+ const scope = rawScope || "all";
9803
9804
  const limit = Math.min(requestedLimit ?? 20, MAX_LIMIT);
9804
9805
  const index = getIndex();
9805
9806
  const vaultPath2 = getVaultPath();
@@ -10721,7 +10722,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig = ()
10721
10722
  description: "Read the structure of a specific note. Use after search identifies a note you need more detail on. Returns headings, frontmatter, tags, word count. Set include_content: true to get the full markdown.",
10722
10723
  inputSchema: {
10723
10724
  path: z6.string().describe("Path to the note"),
10724
- include_content: z6.boolean().default(false).describe("Include the text content under each top-level section")
10725
+ include_content: z6.boolean().default(true).describe("Include the text content under each top-level section. Set false to get structure only.")
10725
10726
  }
10726
10727
  },
10727
10728
  async ({ path: path33, include_content }) => {
@@ -10834,7 +10835,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig = ()
10834
10835
  description: 'Query tasks from the vault. Use path to scope to a single note. Use status to filter (default: "open"). Use has_due_date to find tasks with due dates.',
10835
10836
  inputSchema: {
10836
10837
  path: z6.string().optional().describe("Scope to tasks from this specific note path"),
10837
- status: z6.enum(["open", "completed", "cancelled", "all"]).default("open").describe("Filter by task status"),
10838
+ status: z6.enum(["open", "completed", "cancelled"]).default("open").describe("Filter by task status"),
10838
10839
  has_due_date: z6.boolean().optional().describe("If true, only return tasks with due dates (sorted by date)"),
10839
10840
  folder: z6.string().optional().describe("Limit to tasks in notes within this folder"),
10840
10841
  tag: z6.string().optional().describe("Filter to tasks with this tag"),
@@ -10855,7 +10856,7 @@ function registerPrimitiveTools(server2, getIndex, getVaultPath, getConfig = ()
10855
10856
  };
10856
10857
  }
10857
10858
  let filtered = result2;
10858
- if (status !== "all") {
10859
+ if (status) {
10859
10860
  filtered = result2.filter((t) => t.status === status);
10860
10861
  }
10861
10862
  const paged2 = filtered.slice(offset, offset + limit);
@@ -13508,19 +13509,19 @@ Example: vault_add_to_section({ path: "daily/2026-02-15.md", section: "Log", con
13508
13509
  format: z11.enum(["plain", "bullet", "task", "numbered", "timestamp-bullet"]).default("plain").describe("How to format the content"),
13509
13510
  commit: z11.boolean().default(false).describe("If true, commit this change to git (creates undo point)"),
13510
13511
  skipWikilinks: z11.boolean().default(false).describe("If true, skip auto-wikilink application (wikilinks are applied by default)"),
13511
- preserveListNesting: z11.boolean().default(true).describe("Detect and preserve the indentation level of surrounding list items. Set false to disable."),
13512
- bumpHeadings: z11.boolean().default(true).describe("Auto-bump heading levels in inserted content so they nest under the target section (e.g., ## in a ## section becomes ###). Set false to disable."),
13513
- suggestOutgoingLinks: z11.boolean().default(true).describe('Append suggested outgoing wikilinks based on content (e.g., "\u2192 [[AI]], [[Philosophy]]"). Set false to disable.'),
13514
- maxSuggestions: z11.number().min(1).max(10).default(5).describe("Maximum number of suggested wikilinks to append (1-10, default: 5)"),
13515
- validate: z11.boolean().default(true).describe("Check input for common issues (double timestamps, non-markdown bullets, etc.)"),
13516
- normalize: z11.boolean().default(true).describe("Auto-fix common issues before formatting (replace \u2022 with -, trim excessive whitespace, etc.)"),
13517
- guardrails: z11.enum(["warn", "strict", "off"]).default("warn").describe('Output validation mode: "warn" returns issues but proceeds, "strict" blocks on errors, "off" disables'),
13512
+ suggestOutgoingLinks: z11.boolean().default(true).describe("Suggest related outgoing wikilinks based on content. Set false to disable."),
13513
+ maxSuggestions: z11.number().min(1).max(10).default(5).describe("Maximum number of suggested wikilinks (1-10, default: 5)"),
13518
13514
  linkedEntities: z11.array(z11.string()).optional().describe("Entity names already linked in the content. When skipWikilinks=true, these are tracked for feedback without re-processing the content."),
13519
13515
  dry_run: z11.boolean().optional().default(false).describe("Preview changes without writing to disk"),
13520
13516
  agent_id: z11.string().optional().describe('Agent identifier for multi-agent scoping (e.g., "claude-opus", "planning-agent")'),
13521
13517
  session_id: z11.string().optional().describe('Session identifier for conversation scoping (e.g., "sess-abc123")')
13522
13518
  },
13523
- async ({ path: notePath, section, content, create_if_missing, position, format, commit, skipWikilinks, preserveListNesting, bumpHeadings, suggestOutgoingLinks, maxSuggestions, validate, normalize, guardrails, linkedEntities, dry_run, agent_id, session_id }) => {
13519
+ async ({ path: notePath, section, content, create_if_missing, position, format, commit, skipWikilinks, suggestOutgoingLinks, maxSuggestions, linkedEntities, dry_run, agent_id, session_id }) => {
13520
+ const preserveListNesting = true;
13521
+ const bumpHeadings = true;
13522
+ const validate = true;
13523
+ const normalize = true;
13524
+ const guardrails = "warn";
13524
13525
  let noteCreated = false;
13525
13526
  let templateUsed;
13526
13527
  if (create_if_missing) {
@@ -13652,16 +13653,16 @@ Example: vault_add_to_section({ path: "daily/2026-02-15.md", section: "Log", con
13652
13653
  useRegex: z11.boolean().default(false).describe("Treat search as regex"),
13653
13654
  commit: z11.boolean().default(false).describe("If true, commit this change to git (creates undo point)"),
13654
13655
  skipWikilinks: z11.boolean().default(false).describe("If true, skip auto-wikilink application on replacement text"),
13655
- suggestOutgoingLinks: z11.boolean().default(true).describe('Append suggested outgoing wikilinks based on content (e.g., "\u2192 [[AI]], [[Philosophy]]"). Set false to disable.'),
13656
- maxSuggestions: z11.number().min(1).max(10).default(5).describe("Maximum number of suggested wikilinks to append (1-10, default: 5)"),
13657
- validate: z11.boolean().default(true).describe("Check input for common issues (double timestamps, non-markdown bullets, etc.)"),
13658
- normalize: z11.boolean().default(true).describe("Auto-fix common issues before formatting (replace \u2022 with -, trim excessive whitespace, etc.)"),
13659
- guardrails: z11.enum(["warn", "strict", "off"]).default("warn").describe('Output validation mode: "warn" returns issues but proceeds, "strict" blocks on errors, "off" disables'),
13656
+ suggestOutgoingLinks: z11.boolean().default(true).describe("Suggest related outgoing wikilinks based on content. Set false to disable."),
13657
+ maxSuggestions: z11.number().min(1).max(10).default(5).describe("Maximum number of suggested wikilinks (1-10, default: 5)"),
13660
13658
  dry_run: z11.boolean().optional().default(false).describe("Preview changes without writing to disk"),
13661
13659
  agent_id: z11.string().optional().describe("Agent identifier for multi-agent scoping"),
13662
13660
  session_id: z11.string().optional().describe("Session identifier for conversation scoping")
13663
13661
  },
13664
- async ({ path: notePath, section, search, replacement, mode, useRegex, commit, skipWikilinks, suggestOutgoingLinks, maxSuggestions, validate, normalize, guardrails, dry_run, agent_id, session_id }) => {
13662
+ async ({ path: notePath, section, search, replacement, mode, useRegex, commit, skipWikilinks, suggestOutgoingLinks, maxSuggestions, dry_run, agent_id, session_id }) => {
13663
+ const validate = true;
13664
+ const normalize = true;
13665
+ const guardrails = "warn";
13665
13666
  return withVaultFile(
13666
13667
  {
13667
13668
  vaultPath: vaultPath2,
@@ -17570,7 +17571,7 @@ function getEdgeWeightBoost(entityName, edgeWeightMap) {
17570
17571
  async function performRecall(stateDb2, query, options = {}) {
17571
17572
  const {
17572
17573
  max_results = 20,
17573
- focus = "all",
17574
+ focus,
17574
17575
  entity,
17575
17576
  max_tokens,
17576
17577
  diversity = 0.7,
@@ -17580,7 +17581,7 @@ async function performRecall(stateDb2, query, options = {}) {
17580
17581
  const recencyIndex2 = loadRecencyFromStateDb();
17581
17582
  const edgeWeightMap = getEntityEdgeWeightMap(stateDb2);
17582
17583
  const feedbackBoosts = getAllFeedbackBoosts(stateDb2);
17583
- if (focus === "all" || focus === "entities") {
17584
+ if (!focus || focus === "entities") {
17584
17585
  try {
17585
17586
  const entityResults = searchEntitiesDb2(stateDb2, query, max_results);
17586
17587
  for (const e of entityResults) {
@@ -17609,7 +17610,7 @@ async function performRecall(stateDb2, query, options = {}) {
17609
17610
  } catch {
17610
17611
  }
17611
17612
  }
17612
- if (focus === "all" || focus === "notes") {
17613
+ if (!focus || focus === "notes") {
17613
17614
  try {
17614
17615
  const noteResults = searchFTS5("", query, max_results);
17615
17616
  for (const n of noteResults) {
@@ -17632,7 +17633,7 @@ async function performRecall(stateDb2, query, options = {}) {
17632
17633
  } catch {
17633
17634
  }
17634
17635
  }
17635
- if (focus === "all" || focus === "memories") {
17636
+ if (!focus || focus === "memories") {
17636
17637
  try {
17637
17638
  const memResults = searchMemories(stateDb2, {
17638
17639
  query,
@@ -17660,7 +17661,7 @@ async function performRecall(stateDb2, query, options = {}) {
17660
17661
  } catch {
17661
17662
  }
17662
17663
  }
17663
- if ((focus === "all" || focus === "entities") && query.length >= 20 && hasEntityEmbeddingsIndex()) {
17664
+ if ((!focus || focus === "entities") && query.length >= 20 && hasEntityEmbeddingsIndex()) {
17664
17665
  try {
17665
17666
  const embedding = await embedTextCached(query);
17666
17667
  const semanticMatches = findSemanticallySimilarEntities(embedding, max_results);
@@ -17769,7 +17770,7 @@ function registerRecallTools(server2, getStateDb, getVaultPath) {
17769
17770
  {
17770
17771
  query: z24.string().describe('What to recall (e.g., "Project X", "meetings about auth")'),
17771
17772
  max_results: z24.number().min(1).max(100).optional().describe("Max results (default: 20)"),
17772
- focus: z24.enum(["entities", "notes", "memories", "all"]).optional().describe("Limit search to specific type (default: all)"),
17773
+ focus: z24.enum(["entities", "notes", "memories"]).optional().describe("Limit to a specific result type. Omit for best results (searches everything)."),
17773
17774
  entity: z24.string().optional().describe("Filter memories by entity association"),
17774
17775
  max_tokens: z24.number().optional().describe("Token budget for response (truncates lower-ranked results)"),
17775
17776
  diversity: z24.number().min(0).max(1).optional().describe("Relevance vs diversity tradeoff (0=max diversity, 1=pure relevance, default: 0.7)")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velvetmonkey/flywheel-memory",
3
- "version": "2.0.85",
3
+ "version": "2.0.86",
4
4
  "description": "MCP server that gives Claude full read/write access to your Obsidian vault. Select from 51 tools for search, backlinks, graph queries, mutations, agent memory, and hybrid semantic search.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "dependencies": {
55
55
  "@modelcontextprotocol/sdk": "^1.25.1",
56
- "@velvetmonkey/vault-core": "^2.0.85",
56
+ "@velvetmonkey/vault-core": "^2.0.86",
57
57
  "better-sqlite3": "^11.0.0",
58
58
  "chokidar": "^4.0.0",
59
59
  "gray-matter": "^4.0.3",