@danielmarbach/mnemonic-mcp 0.27.0 → 0.27.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 +19 -0
- package/build/index.js +54 -12
- package/build/index.js.map +1 -1
- package/build/structured-content.d.ts +13 -0
- package/build/structured-content.d.ts.map +1 -1
- package/build/structured-content.js +5 -0
- package/build/structured-content.js.map +1 -1
- package/package.json +1 -1
- package/skills/mnemonic-rpi-workflow/SKILL.md +44 -6
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,25 @@ The format is loosely based on Keep a Changelog and uses semver-style version he
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.27.2] - 2026-05-01
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- `remember` and `update(content)` now return structured error responses with actionable retry guidance when markdown lint fails, instead of propagating raw errors that LLMs sometimes ignore.
|
|
14
|
+
- `remember` and `update` `content` parameter descriptions include proactive lint guidance (auto-fixable vs. unfixable issues, common MD040 fenced-code-language gotcha).
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- `semanticPatch` schema example and guidance now use `insertAfter` instead of the rejected `appendChild`/`replaceChildren` on `heading` selectors.
|
|
19
|
+
- `semanticPatch` workflow hint corrected: recommends `insertAfter` for adding content under headings instead of `appendChild`, which the code rejects.
|
|
20
|
+
- `semanticPatch` parameter now auto-parses string-wrapped JSON arrays, recovering gracefully when LLMs serialize the array as a string instead of a proper JSON array.
|
|
21
|
+
|
|
22
|
+
## [0.27.1] - 2026-04-28
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- `mnemonic-rpi-workflow` skill: Review phase now retrieves research and plan context before evaluating implementation, with comparison guardrails against original requirements and deliverables. Plan phase includes a self-check after drafting to catch placeholders and coverage gaps. Review evidence must be gathered fresh rather than reused from implementation. Added review handoff variant for subagent-driven review with full artifact chain.
|
|
27
|
+
|
|
9
28
|
## [0.27.0] - 2026-04-28
|
|
10
29
|
|
|
11
30
|
### Added
|
package/build/index.js
CHANGED
|
@@ -1461,7 +1461,9 @@ server.registerTool("remember", {
|
|
|
1461
1461
|
},
|
|
1462
1462
|
inputSchema: z.object({
|
|
1463
1463
|
title: z.string().describe("Specific, retrieval-friendly title. Prefer the concrete topic or decision, not a vague label."),
|
|
1464
|
-
content: z.string().describe("Markdown note body. Put the key fact, decision, or outcome in the opening lines, then supporting detail. Embeddings weight early content more heavily."
|
|
1464
|
+
content: z.string().describe("Markdown note body. Put the key fact, decision, or outcome in the opening lines, then supporting detail. Embeddings weight early content more heavily. " +
|
|
1465
|
+
"Content must pass markdown lint. Auto-fixable issues are fixed automatically. Common unfixable issues: fenced code blocks need a language tag (e.g. use ```text not bare ```), and broken links are rejected. " +
|
|
1466
|
+
"If lint fails, fix the specific issues listed in the error and retry the same call."),
|
|
1465
1467
|
tags: z.array(z.string()).optional().default([]).describe("Optional tags for later filtering. Use a small number of stable, meaningful tags."),
|
|
1466
1468
|
lifecycle: z
|
|
1467
1469
|
.enum(NOTE_LIFECYCLES)
|
|
@@ -1504,7 +1506,21 @@ server.registerTool("remember", {
|
|
|
1504
1506
|
}, async ({ title, content, tags, lifecycle, role, summary, alwaysLoad, cwd, scope, allowProtectedBranch = false }) => {
|
|
1505
1507
|
await ensureBranchSynced(cwd);
|
|
1506
1508
|
const project = await resolveProject(cwd);
|
|
1507
|
-
|
|
1509
|
+
let cleanedContent;
|
|
1510
|
+
try {
|
|
1511
|
+
cleanedContent = await cleanMarkdown(content);
|
|
1512
|
+
}
|
|
1513
|
+
catch (err) {
|
|
1514
|
+
if (err instanceof MarkdownLintError) {
|
|
1515
|
+
const message = `Markdown lint issues prevented this note from being stored. Fix the specific lint errors listed below in your content and retry the remember call — the note was NOT stored.\n\n${err.message}`;
|
|
1516
|
+
return {
|
|
1517
|
+
content: [{ type: "text", text: message }],
|
|
1518
|
+
structuredContent: { action: "lint_error", tool: "remember", issues: err.issues },
|
|
1519
|
+
isError: true,
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
throw err;
|
|
1523
|
+
}
|
|
1508
1524
|
const policy = project ? await configStore.getProjectPolicy(project.id) : undefined;
|
|
1509
1525
|
const policyScope = policy?.defaultScope;
|
|
1510
1526
|
const projectVaultExists = cwd ? Boolean(await vaultManager.getProjectVaultIfExists(cwd)) : true;
|
|
@@ -2263,7 +2279,17 @@ server.registerTool("update", {
|
|
|
2263
2279
|
inputSchema: z.object({
|
|
2264
2280
|
id: z.string().describe("Exact memory id. Use an id returned by `recall`, `list`, `recent_memories`, or `where_is`."),
|
|
2265
2281
|
semanticPatch: z
|
|
2266
|
-
.
|
|
2282
|
+
.preprocess((val) => {
|
|
2283
|
+
if (typeof val === "string") {
|
|
2284
|
+
try {
|
|
2285
|
+
return JSON.parse(val);
|
|
2286
|
+
}
|
|
2287
|
+
catch {
|
|
2288
|
+
return val;
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
return val;
|
|
2292
|
+
}, z.array(z.object({
|
|
2267
2293
|
selector: z.object({
|
|
2268
2294
|
heading: z.string().optional(),
|
|
2269
2295
|
headingStartsWith: z.string().optional(),
|
|
@@ -2282,20 +2308,20 @@ server.registerTool("update", {
|
|
|
2282
2308
|
z.object({ op: z.literal("insertBefore"), value: z.string() }),
|
|
2283
2309
|
z.object({ op: z.literal("remove") }),
|
|
2284
2310
|
]),
|
|
2285
|
-
}))
|
|
2311
|
+
})))
|
|
2286
2312
|
.optional()
|
|
2287
2313
|
.describe("Targeted edits to note sections. Array of {selector, operation} objects. Mutually exclusive with content. " +
|
|
2288
2314
|
"If this fails, fix the issue in your patch values and retry — do NOT fall back to full content rewrite.\n\n" +
|
|
2289
2315
|
"selector: exactly one of { heading: \"exact heading text\" } | { headingStartsWith: \"prefix\" } | { lastChild: true } | { nthChild: 0-based-index }\n" +
|
|
2290
2316
|
"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" +
|
|
2291
|
-
"Example —
|
|
2317
|
+
"Example — add a paragraph under ## Findings, replace body under ## Recommendation, remove ## Old Section:\n" +
|
|
2292
2318
|
"[\n" +
|
|
2293
|
-
" { \"selector\": { \"heading\": \"Findings\" }, \"operation\": { \"op\": \"
|
|
2294
|
-
" { \"selector\": { \"heading\": \"Recommendation\" }, \"operation\": { \"op\": \"
|
|
2319
|
+
" { \"selector\": { \"heading\": \"Findings\" }, \"operation\": { \"op\": \"insertAfter\", \"value\": \"A new paragraph.\" } },\n" +
|
|
2320
|
+
" { \"selector\": { \"heading\": \"Recommendation\" }, \"operation\": { \"op\": \"replace\", \"value\": \"## Recommendation\\n\\nUpdated recommendation.\" } },\n" +
|
|
2295
2321
|
" { \"selector\": { \"heading\": \"Old Section\" }, \"operation\": { \"op\": \"remove\" } }\n" +
|
|
2296
2322
|
"]\n\n" +
|
|
2297
|
-
"`replaceChildren`
|
|
2298
|
-
content: z.string().optional().describe("Full note body replacement. Use only for complete rewrites or when the note is small. Mutually exclusive with semanticPatch."),
|
|
2323
|
+
"IMPORTANT: `appendChild`, `prependChild`, and `replaceChildren` do NOT work with `heading` selectors (headings only contain inline text, not block content). To add or replace content under a heading, use `insertAfter`. To replace a heading entirely, use `replace`."),
|
|
2324
|
+
content: z.string().optional().describe("Full note body replacement. Use only for complete rewrites or when the note is small. Mutually exclusive with semanticPatch. Content must pass markdown lint. Auto-fixable issues are fixed automatically. Common unfixable issues: fenced code blocks need a language tag (e.g. use ```text not bare ```), and broken links are rejected. If lint fails, fix the specific issues and retry — do NOT fall back to semanticPatch for this."),
|
|
2299
2325
|
title: z.string().optional().describe("Specific, retrieval-friendly title. Prefer the concrete topic or decision, not a vague label."),
|
|
2300
2326
|
tags: z.array(z.string()).optional().describe("Optional tags for later filtering. Use a small number of stable, meaningful tags."),
|
|
2301
2327
|
lifecycle: z
|
|
@@ -2373,7 +2399,23 @@ server.registerTool("update", {
|
|
|
2373
2399
|
return { content: [{ type: "text", text: `Semantic patch failed: ${message}` }], isError: true };
|
|
2374
2400
|
}
|
|
2375
2401
|
}
|
|
2376
|
-
|
|
2402
|
+
let cleanedContent;
|
|
2403
|
+
if (content !== undefined) {
|
|
2404
|
+
try {
|
|
2405
|
+
cleanedContent = await cleanMarkdown(content);
|
|
2406
|
+
}
|
|
2407
|
+
catch (err) {
|
|
2408
|
+
if (err instanceof MarkdownLintError) {
|
|
2409
|
+
const message = `Markdown lint issues prevented the update. Fix the specific lint errors in your content and retry — do NOT fall back to semanticPatch for this.\n\n${err.message}`;
|
|
2410
|
+
return {
|
|
2411
|
+
content: [{ type: "text", text: message }],
|
|
2412
|
+
structuredContent: { action: "lint_error", tool: "update", issues: err.issues },
|
|
2413
|
+
isError: true,
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
throw err;
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2377
2419
|
const resolvedTitle = title ?? note.title;
|
|
2378
2420
|
const resolvedContent = patchedContent ?? cleanedContent ?? note.content;
|
|
2379
2421
|
const resolvedTags = tags ?? note.tags;
|
|
@@ -5365,8 +5407,8 @@ server.registerPrompt("mnemonic-workflow-hint", {
|
|
|
5365
5407
|
"- `operation` has an `op` key plus `value` (except `remove` which has no value).\n" +
|
|
5366
5408
|
"- The parameter must be a JSON array, NOT a string.\n" +
|
|
5367
5409
|
"- Use `get` first to read exact heading text, then use those headings (without `##` prefix) as selector values.\n" +
|
|
5368
|
-
"- Common mistake: writing `{ \"op\": \"appendChild\", \"value\": \"...\" }` at the top level instead of nesting inside `operation`. Correct shape: `{ \"selector\": { \"heading\": \"Findings\" }, \"operation\": { \"op\": \"
|
|
5369
|
-
"- `replaceChildren`
|
|
5410
|
+
"- Common mistake: writing `{ \"op\": \"appendChild\", \"value\": \"...\" }` at the top level instead of nesting inside `operation`. Correct shape: `{ \"selector\": { \"heading\": \"Findings\" }, \"operation\": { \"op\": \"insertAfter\", \"value\": \"text\" } }`\n" +
|
|
5411
|
+
"- `appendChild`, `prependChild`, and `replaceChildren` do NOT work with `heading` selectors. To add content under a heading, use `insertAfter`. To replace a heading, use `replace`.",
|
|
5370
5412
|
},
|
|
5371
5413
|
},
|
|
5372
5414
|
],
|