@danielmarbach/mnemonic-mcp 0.25.0 → 0.25.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.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,21 @@ The format is loosely based on Keep a Changelog and uses semver-style version he
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.25.2] - 2026-04-24
10
+
11
+ ### Changed
12
+
13
+ - `semanticPatch` parameter description now includes the full selector/operation type union and a 3-patch working example, so LLMs can construct correct JSON shapes without guessing at the nesting structure.
14
+ - `mnemonic-workflow-hint` prompt now includes a `semanticPatch format` section with explicit nesting examples and a callout for the common `{op, value}` flattening mistake.
15
+ - Selector-not-found errors now include a format reminder showing the correct selector shape using the first available heading.
16
+
17
+ ## [0.25.1] - 2026-04-24
18
+
19
+ ### Changed
20
+
21
+ - `semanticPatch` no longer rejects content with markdown lint issues — the patch succeeds and warnings are surfaced in the response instead, so the LLM can continue without retrying from scratch. Previously any lint issue forced a hard failure.
22
+ - Structural errors (bad selector, invalid operation) and content lint errors are now distinguished from patch lint warnings with separate guidance, so the LLM gets the right fix direction for each failure mode.
23
+
9
24
  ## [0.25.0] - 2026-04-24
10
25
 
11
26
  ### Added
package/build/index.js CHANGED
@@ -17,7 +17,7 @@ import { suggestAutoRelationships } from "./auto-relate.js";
17
17
  import { computeRecallMetadataBoost, computeHybridScore, selectRecallResults, selectWorkflowResults, applyLexicalReranking, applyCanonicalExplanationPromotion, } from "./recall.js";
18
18
  import { shouldTriggerLexicalRescue, rankDocumentsByTfIdf, LEXICAL_RESCUE_CANDIDATE_LIMIT, LEXICAL_RESCUE_THRESHOLD, LEXICAL_RESCUE_RESULT_LIMIT, } from "./lexical.js";
19
19
  import { getRelationshipPreview } from "./relationships.js";
20
- import { cleanMarkdown } from "./markdown.js";
20
+ import { MarkdownLintError, cleanMarkdown } from "./markdown.js";
21
21
  import { applySemanticPatches } from "./semantic-patch.js";
22
22
  import { MnemonicConfigStore, readVaultSchemaVersion } from "./config.js";
23
23
  import { CONSOLIDATION_MODES, PROTECTED_BRANCH_BEHAVIORS, PROJECT_POLICY_SCOPES, WRITE_SCOPES, isProtectedBranch, resolveProtectedBranchBehavior, resolveProtectedBranchPatterns, resolveConsolidationMode, resolveWriteScope, } from "./project-memory-policy.js";
@@ -2158,8 +2158,16 @@ server.registerTool("update", {
2158
2158
  ]),
2159
2159
  }))
2160
2160
  .optional()
2161
- .describe("Use for targeted edits when you know the structure. More token-efficient than passing full content. " +
2162
- "Mutually exclusive with content."),
2161
+ .describe("Targeted edits to note sections. Array of {selector, operation} objects. Mutually exclusive with content. " +
2162
+ "If this fails, fix the issue in your patch values and retry — do NOT fall back to full content rewrite.\n\n" +
2163
+ "selector: exactly one of { heading: \"exact heading text\" } | { headingStartsWith: \"prefix\" } | { lastChild: true } | { nthChild: 0-based-index }\n" +
2164
+ "operation: { op: \"appendChild\", value: \"content\" } | { op: \"prependChild\", value: \"content\" } | { op: \"replace\", value: \"new content\" } | { op: \"replaceChildren\", value: \"new children\" } | { op: \"insertAfter\", value: \"content\" } | { op: \"insertBefore\", value: \"content\" } | { op: \"remove\" }\n\n" +
2165
+ "Example — append a paragraph under ## Findings, replace ## Recommendation body, remove ## Old Section:\n" +
2166
+ "[\n" +
2167
+ " { \"selector\": { \"heading\": \"Findings\" }, \"operation\": { \"op\": \"appendChild\", \"value\": \"A new paragraph.\" } },\n" +
2168
+ " { \"selector\": { \"heading\": \"Recommendation\" }, \"operation\": { \"op\": \"replaceChildren\", \"value\": \"Updated recommendation.\" } },\n" +
2169
+ " { \"selector\": { \"heading\": \"Old Section\" }, \"operation\": { \"op\": \"remove\" } }\n" +
2170
+ "]"),
2163
2171
  content: z.string().optional().describe("Full note body replacement. Use only for complete rewrites or when the note is small. Mutually exclusive with semanticPatch."),
2164
2172
  title: z.string().optional().describe("Specific, retrieval-friendly title. Prefer the concrete topic or decision, not a vague label."),
2165
2173
  tags: z.array(z.string()).optional().describe("Optional tags for later filtering. Use a small number of stable, meaningful tags."),
@@ -2222,11 +2230,18 @@ server.registerTool("update", {
2222
2230
  }
2223
2231
  const now = new Date().toISOString();
2224
2232
  let patchedContent;
2233
+ let lintWarnings;
2225
2234
  if (semanticPatch && semanticPatch.length > 0) {
2226
2235
  try {
2227
- patchedContent = await applySemanticPatches(note.content, semanticPatch);
2236
+ const result = await applySemanticPatches(note.content, semanticPatch);
2237
+ patchedContent = result.content;
2238
+ lintWarnings = result.lintWarnings;
2228
2239
  }
2229
2240
  catch (err) {
2241
+ if (err instanceof MarkdownLintError) {
2242
+ const message = `Semantic patch produced content with markdown lint issues. Fix the lint issues in your patch values and retry — do NOT fall back to full content rewrite.\n\n${err.message}`;
2243
+ return { content: [{ type: "text", text: message }], isError: true };
2244
+ }
2230
2245
  const message = err instanceof Error ? err.message : String(err);
2231
2246
  return { content: [{ type: "text", text: `Semantic patch failed: ${message}` }], isError: true };
2232
2247
  }
@@ -2333,11 +2348,15 @@ server.registerTool("update", {
2333
2348
  project: noteProjectRef(updated),
2334
2349
  lifecycle: updated.lifecycle,
2335
2350
  role: updated.role,
2351
+ lintWarnings: lintWarnings && lintWarnings.length > 0 ? lintWarnings : undefined,
2336
2352
  persistence,
2337
2353
  };
2338
2354
  invalidateActiveProjectCache();
2339
2355
  const fieldText = changes.length > 0 ? `\nfields modified: ${changes.join(", ")}` : "";
2340
- return { content: [{ type: "text", text: `Updated memory '${id}'${fieldText}\n${formatPersistenceSummary(persistence)}` }], structuredContent };
2356
+ const warningsText = lintWarnings && lintWarnings.length > 0
2357
+ ? `\nmarkdown lint warnings (not auto-fixable):\n- ${lintWarnings.join("\n- ")}`
2358
+ : "";
2359
+ return { content: [{ type: "text", text: `Updated memory '${id}'${fieldText}${warningsText}\n${formatPersistenceSummary(persistence)}` }], structuredContent };
2341
2360
  });
2342
2361
  // ── forget ────────────────────────────────────────────────────────────────────
2343
2362
  server.registerTool("forget", {
@@ -5056,7 +5075,15 @@ server.registerPrompt("mnemonic-workflow-hint", {
5056
5075
  "- Existing bug note found by `recall` -> inspect with `get` -> refine with `update`.\n" +
5057
5076
  "- No matching note found by `recall` -> optional `discover_tags` with note context -> create with `remember`.\n" +
5058
5077
  "- Two notes overlap heavily -> inspect -> clean up with `consolidate`.\n" +
5059
- "- Resume work: `project_memory_summary` -> `recall` (lifecycle: temporary) -> continue from temporary notes.",
5078
+ "- Resume work: `project_memory_summary` -> `recall` (lifecycle: temporary) -> continue from temporary notes.\n\n" +
5079
+ "### semanticPatch format\n\n" +
5080
+ "When using `update` with `semanticPatch`:\n" +
5081
+ "- Each patch is an object with two keys: `selector` and `operation` (not flat `{op, value}` at top level).\n" +
5082
+ "- `selector` has exactly one key: `heading`, `headingStartsWith`, `nthChild`, or `lastChild`.\n" +
5083
+ "- `operation` has an `op` key plus `value` (except `remove` which has no value).\n" +
5084
+ "- The parameter must be a JSON array, NOT a string.\n" +
5085
+ "- Use `get` first to read exact heading text, then use those headings (without `##` prefix) as selector values.\n" +
5086
+ "- Common mistake: writing `{ \"op\": \"appendChild\", \"value\": \"...\" }` at the top level instead of nesting inside `operation`. Correct shape: `{ \"selector\": { \"heading\": \"Findings\" }, \"operation\": { \"op\": \"appendChild\", \"value\": \"text\" } }`",
5060
5087
  },
5061
5088
  },
5062
5089
  ],