@velvetmonkey/flywheel-memory 2.6.0 → 2.6.2

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 +59 -5
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -5799,6 +5799,29 @@ function insertInSection(content, section, newContent, position, options) {
5799
5799
  }
5800
5800
  return lines.join("\n");
5801
5801
  }
5802
+ function indentContinuation(text) {
5803
+ const lines = text.split("\n");
5804
+ if (lines.length <= 1) return text;
5805
+ let inCodeBlock = false;
5806
+ return lines.map((line, i) => {
5807
+ if (/^\s*(```|~~~)/.test(line)) inCodeBlock = !inCodeBlock;
5808
+ if (i === 0) return line;
5809
+ if (line === "") return inCodeBlock ? " " : " <!-- -->";
5810
+ return ` ${line}`;
5811
+ }).join("\n");
5812
+ }
5813
+ function sanitizeForObsidian(text) {
5814
+ const lines = text.split("\n");
5815
+ let inFence = false;
5816
+ return lines.map((line) => {
5817
+ if (/^\s*(```|~~~)/.test(line)) {
5818
+ inFence = !inFence;
5819
+ return line;
5820
+ }
5821
+ if (inFence) return line;
5822
+ return line.replace(/<(?=[A-Za-z0-9])/g, "&lt;").replace(/^(\s*)>/gm, "$1&gt;").replace(/%%/g, "&#37;%").replace(/==(.*?)==/g, "&#61;=$1&#61;=").replace(/(?<=\s)#(?=[A-Za-z0-9])/g, "&#35;");
5823
+ }).join("\n");
5824
+ }
5802
5825
  var EMPTY_PLACEHOLDER_PATTERNS;
5803
5826
  var init_markdown_structure = __esm({
5804
5827
  "src/core/write/markdown-structure.ts"() {
@@ -14841,6 +14864,7 @@ function registerGraphTools(server2, getIndex, getVaultPath, getStateDb4) {
14841
14864
  return { content: [{ type: "text", text: JSON.stringify({ error: "StateDb not initialized" }) }] };
14842
14865
  }
14843
14866
  const limit = Math.min(requestedLimit ?? 20, MAX_LIMIT);
14867
+ const index = getIndex();
14844
14868
  const targets = getInboundTargetsForNote(stateDb2, notePath);
14845
14869
  const inPlaceholders = targets.map(() => "?").join(",");
14846
14870
  const rows = stateDb2.db.prepare(`
@@ -14862,7 +14886,8 @@ function registerGraphTools(server2, getIndex, getVaultPath, getStateDb4) {
14862
14886
  connections: rows.map((r) => ({
14863
14887
  node: r.node,
14864
14888
  weight: r.weight,
14865
- direction: r.direction
14889
+ direction: r.direction,
14890
+ resolved_path: r.direction === "outgoing" ? resolveTarget(index, r.node) ?? void 0 : void 0
14866
14891
  }))
14867
14892
  }, null, 2)
14868
14893
  }]
@@ -20144,6 +20169,7 @@ async function executeDeleteNote(options) {
20144
20169
  }
20145
20170
 
20146
20171
  // src/tools/write/mutations.ts
20172
+ init_markdown_structure();
20147
20173
  async function createNoteFromTemplate(vaultPath2, notePath, config2) {
20148
20174
  const validation = await validatePathSecure(vaultPath2, notePath);
20149
20175
  if (!validation.valid) {
@@ -20244,13 +20270,17 @@ function registerMutationTools(server2, getVaultPath, getConfig2 = () => ({})) {
20244
20270
  commit: z12.boolean().default(false).describe("If true, commit this change to git (creates undo point)"),
20245
20271
  skipWikilinks: z12.boolean().default(false).describe("If true, skip auto-wikilink application (wikilinks are applied by default)"),
20246
20272
  suggestOutgoingLinks: z12.boolean().default(false).describe('Suggest related outgoing wikilinks based on content (e.g., "\u2192 [[AI]], [[Philosophy]]"). Off by default \u2014 set true for daily notes, journals, or capture-heavy contexts.'),
20273
+ children: z12.array(z12.object({
20274
+ label: z12.string().describe('Bold label, e.g. "**Result:**"'),
20275
+ content: z12.string().describe("Pre-neutralized text; sanitized and indented by this tool")
20276
+ })).optional().describe("Labeled sub-bullets appended under the parent content line"),
20247
20277
  maxSuggestions: z12.number().min(1).max(10).default(5).describe("Maximum number of suggested wikilinks (1-10, default: 5)"),
20248
20278
  linkedEntities: z12.array(z12.string()).optional().describe("Entity names already linked in the content. When skipWikilinks=true, these are tracked for feedback without re-processing the content."),
20249
20279
  dry_run: z12.boolean().optional().default(false).describe("Preview changes without writing to disk"),
20250
20280
  agent_id: z12.string().optional().describe('Agent identifier for multi-agent scoping (e.g., "claude-opus", "planning-agent")'),
20251
20281
  session_id: z12.string().optional().describe('Session identifier for conversation scoping (e.g., "sess-abc123")')
20252
20282
  },
20253
- async ({ path: notePath, section, content, create_if_missing, position, format, commit, skipWikilinks, suggestOutgoingLinks, maxSuggestions, linkedEntities, dry_run, agent_id, session_id }) => {
20283
+ async ({ path: notePath, section, content, create_if_missing, position, format, commit, skipWikilinks, suggestOutgoingLinks, children, maxSuggestions, linkedEntities, dry_run, agent_id, session_id }) => {
20254
20284
  const vaultPath2 = getVaultPath();
20255
20285
  const preserveListNesting = true;
20256
20286
  const bumpHeadings = true;
@@ -20314,15 +20344,39 @@ function registerMutationTools(server2, getVaultPath, getConfig2 = () => ({})) {
20314
20344
  trackWikilinkApplications(stateDb2, notePath, linkedEntities);
20315
20345
  }
20316
20346
  }
20347
+ let finalContent = processedContent;
20348
+ let finalFormat = format;
20349
+ let childTextsForLinks = [];
20350
+ if (children && children.length > 0) {
20351
+ const childBlocks = children.map(({ label, content: childContent }) => {
20352
+ const sanitized = sanitizeForObsidian(childContent);
20353
+ const processed = indentContinuation(sanitized);
20354
+ const lines = processed.split("\n");
20355
+ if (/^\s*(```|~~~)/.test(lines[0])) {
20356
+ return [` - ${label}`, ...lines.map((l) => ` ${l}`)].join("\n");
20357
+ }
20358
+ return [`- ${label} ${lines[0]}`, ...lines.slice(1)].map((l) => ` ${l}`).join("\n");
20359
+ });
20360
+ finalContent = `- ${processedContent}
20361
+ ${childBlocks.join("\n")}`;
20362
+ finalFormat = "plain";
20363
+ childTextsForLinks = children.map((c) => c.content);
20364
+ }
20317
20365
  let suggestInfo;
20318
20366
  if (suggestOutgoingLinks && !skipWikilinks) {
20319
- const result = await suggestRelatedLinks(processedContent, { maxSuggestions, notePath });
20367
+ const suggestionText = childTextsForLinks.length > 0 ? childTextsForLinks.join(" ") : processedContent;
20368
+ const result = await suggestRelatedLinks(suggestionText, { maxSuggestions, notePath });
20320
20369
  if (result.suffix) {
20321
- processedContent = processedContent + " " + result.suffix;
20370
+ if (childTextsForLinks.length > 0) {
20371
+ finalContent = finalContent + " " + result.suffix;
20372
+ } else {
20373
+ processedContent = processedContent + " " + result.suffix;
20374
+ finalContent = processedContent;
20375
+ }
20322
20376
  suggestInfo = `Suggested: ${result.suggestions.join(", ")}`;
20323
20377
  }
20324
20378
  }
20325
- const formattedContent = formatContent(processedContent, format);
20379
+ const formattedContent = formatContent(finalContent, finalFormat);
20326
20380
  const updatedContent = insertInSection(
20327
20381
  ctx.content,
20328
20382
  ctx.sectionBoundary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velvetmonkey/flywheel-memory",
3
- "version": "2.6.0",
3
+ "version": "2.6.2",
4
4
  "description": "MCP tools that search, write, and auto-link your Obsidian vault — and learn from your edits.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -56,7 +56,7 @@
56
56
  "dependencies": {
57
57
  "@huggingface/transformers": "^3.8.1",
58
58
  "@modelcontextprotocol/sdk": "^1.26.0",
59
- "@velvetmonkey/vault-core": "^2.6.0",
59
+ "@velvetmonkey/vault-core": "^2.6.1",
60
60
  "better-sqlite3": "^12.0.0",
61
61
  "chokidar": "^4.0.0",
62
62
  "gray-matter": "^4.0.3",