@danielmarbach/mnemonic-mcp 0.23.0 → 0.24.0
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 +6 -0
- package/build/index.js +48 -4
- package/build/index.js.map +1 -1
- package/build/markdown-ast.d.ts +4 -0
- package/build/markdown-ast.d.ts.map +1 -0
- package/build/markdown-ast.js +10 -0
- package/build/markdown-ast.js.map +1 -0
- package/build/semantic-patch.d.ts +36 -0
- package/build/semantic-patch.d.ts.map +1 -0
- package/build/semantic-patch.js +141 -0
- package/build/semantic-patch.js.map +1 -0
- package/package.json +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to `mnemonic` will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is loosely based on Keep a Changelog and uses semver-style version headings.
|
|
6
6
|
|
|
7
|
+
## [0.24.0] - 2026-04-24
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `update` now supports `semanticPatch` for token-efficient targeted edits (insert, replace, append, remove content under specific headings) without round-tripping the full note body.
|
|
12
|
+
|
|
7
13
|
## [0.23.0] - 2026-04-17
|
|
8
14
|
|
|
9
15
|
### Added
|
package/build/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import { computeRecallMetadataBoost, computeHybridScore, selectRecallResults, ap
|
|
|
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
20
|
import { cleanMarkdown } from "./markdown.js";
|
|
21
|
+
import { applySemanticPatches } from "./semantic-patch.js";
|
|
21
22
|
import { MnemonicConfigStore, readVaultSchemaVersion } from "./config.js";
|
|
22
23
|
import { CONSOLIDATION_MODES, PROTECTED_BRANCH_BEHAVIORS, PROJECT_POLICY_SCOPES, WRITE_SCOPES, isProtectedBranch, resolveProtectedBranchBehavior, resolveProtectedBranchPatterns, resolveConsolidationMode, resolveWriteScope, } from "./project-memory-policy.js";
|
|
23
24
|
import { classifyTheme, classifyThemeWithGraduation, computeThemesWithGraduation, summarizePreview, titleCaseTheme, daysSinceUpdate, withinThemeScore, anchorScore, computeConnectionDiversity, workingStateScore, extractNextAction, } from "./project-introspection.js";
|
|
@@ -2103,7 +2104,8 @@ server.registerTool("update", {
|
|
|
2103
2104
|
"- The updated memory id, changed fields, and persistence status\n\n" +
|
|
2104
2105
|
"Side effects: rewrites the note, refreshes embeddings, git commits, and may push.\n\n" +
|
|
2105
2106
|
"Typical next step:\n" +
|
|
2106
|
-
"- Use `relate` or `consolidate` if the update changes how this note connects to others
|
|
2107
|
+
"- Use `relate` or `consolidate` if the update changes how this note connects to others.\n\n" +
|
|
2108
|
+
"Use `semanticPatch` for targeted edits (more token-efficient). Use `content` only for complete rewrites.",
|
|
2107
2109
|
annotations: {
|
|
2108
2110
|
readOnlyHint: false,
|
|
2109
2111
|
destructiveHint: false,
|
|
@@ -2112,7 +2114,31 @@ server.registerTool("update", {
|
|
|
2112
2114
|
},
|
|
2113
2115
|
inputSchema: z.object({
|
|
2114
2116
|
id: z.string().describe("Exact memory id. Use an id returned by `recall`, `list`, `recent_memories`, or `where_is`."),
|
|
2115
|
-
|
|
2117
|
+
semanticPatch: z
|
|
2118
|
+
.array(z.object({
|
|
2119
|
+
selector: z.object({
|
|
2120
|
+
heading: z.string().optional(),
|
|
2121
|
+
headingStartsWith: z.string().optional(),
|
|
2122
|
+
nthChild: z.number().int().optional(),
|
|
2123
|
+
lastChild: z.literal(true).optional(),
|
|
2124
|
+
}).refine((sel) => {
|
|
2125
|
+
const keys = [sel.heading, sel.headingStartsWith, sel.nthChild, sel.lastChild].filter((v) => v !== undefined);
|
|
2126
|
+
return keys.length === 1;
|
|
2127
|
+
}, { message: "Selector must have exactly one of: heading, headingStartsWith, nthChild, lastChild" }),
|
|
2128
|
+
operation: z.discriminatedUnion("op", [
|
|
2129
|
+
z.object({ op: z.literal("appendChild"), value: z.string() }),
|
|
2130
|
+
z.object({ op: z.literal("prependChild"), value: z.string() }),
|
|
2131
|
+
z.object({ op: z.literal("replace"), value: z.string() }),
|
|
2132
|
+
z.object({ op: z.literal("replaceChildren"), value: z.string() }),
|
|
2133
|
+
z.object({ op: z.literal("insertAfter"), value: z.string() }),
|
|
2134
|
+
z.object({ op: z.literal("insertBefore"), value: z.string() }),
|
|
2135
|
+
z.object({ op: z.literal("remove") }),
|
|
2136
|
+
]),
|
|
2137
|
+
}))
|
|
2138
|
+
.optional()
|
|
2139
|
+
.describe("Use for targeted edits when you know the structure. More token-efficient than passing full content. " +
|
|
2140
|
+
"Mutually exclusive with content."),
|
|
2141
|
+
content: z.string().optional().describe("Full note body replacement. Use only for complete rewrites or when the note is small. Mutually exclusive with semanticPatch."),
|
|
2116
2142
|
title: z.string().optional().describe("Specific, retrieval-friendly title. Prefer the concrete topic or decision, not a vague label."),
|
|
2117
2143
|
tags: z.array(z.string()).optional().describe("Optional tags for later filtering. Use a small number of stable, meaningful tags."),
|
|
2118
2144
|
lifecycle: z
|
|
@@ -2133,12 +2159,18 @@ server.registerTool("update", {
|
|
|
2133
2159
|
"When true, update can commit on a protected branch without changing project policy."),
|
|
2134
2160
|
}),
|
|
2135
2161
|
outputSchema: UpdateResultSchema,
|
|
2136
|
-
}, async ({ id, content, title, tags, lifecycle, summary, alwaysLoad, cwd, allowProtectedBranch = false }) => {
|
|
2162
|
+
}, async ({ id, content, semanticPatch, title, tags, lifecycle, summary, alwaysLoad, cwd, allowProtectedBranch = false }) => {
|
|
2137
2163
|
await ensureBranchSynced(cwd);
|
|
2138
2164
|
const found = await vaultManager.findNote(id, cwd);
|
|
2139
2165
|
if (!found) {
|
|
2140
2166
|
return { content: [{ type: "text", text: `No memory found with id '${id}'` }], isError: true };
|
|
2141
2167
|
}
|
|
2168
|
+
// Validate: content and semanticPatch are mutually exclusive
|
|
2169
|
+
const hasContent = content !== undefined;
|
|
2170
|
+
const hasSemanticPatch = semanticPatch !== undefined && semanticPatch.length > 0;
|
|
2171
|
+
if (hasContent && hasSemanticPatch) {
|
|
2172
|
+
return { content: [{ type: "text", text: "Exactly one of content or semanticPatch must be provided, not both." }], isError: true };
|
|
2173
|
+
}
|
|
2142
2174
|
const { note, vault } = found;
|
|
2143
2175
|
if (vault.isProject) {
|
|
2144
2176
|
const resolvedProject = await resolveProject(cwd);
|
|
@@ -2163,11 +2195,21 @@ server.registerTool("update", {
|
|
|
2163
2195
|
}
|
|
2164
2196
|
}
|
|
2165
2197
|
const now = new Date().toISOString();
|
|
2198
|
+
let patchedContent;
|
|
2199
|
+
if (semanticPatch && semanticPatch.length > 0) {
|
|
2200
|
+
try {
|
|
2201
|
+
patchedContent = await applySemanticPatches(note.content, semanticPatch);
|
|
2202
|
+
}
|
|
2203
|
+
catch (err) {
|
|
2204
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
2205
|
+
return { content: [{ type: "text", text: `Semantic patch failed: ${message}` }], isError: true };
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2166
2208
|
const cleanedContent = content === undefined ? undefined : await cleanMarkdown(content);
|
|
2167
2209
|
const updated = {
|
|
2168
2210
|
...note,
|
|
2169
2211
|
title: title ?? note.title,
|
|
2170
|
-
content: cleanedContent ?? note.content,
|
|
2212
|
+
content: patchedContent ?? cleanedContent ?? note.content,
|
|
2171
2213
|
tags: tags ?? note.tags,
|
|
2172
2214
|
lifecycle: lifecycle ?? note.lifecycle,
|
|
2173
2215
|
alwaysLoad: alwaysLoad !== undefined ? alwaysLoad : note.alwaysLoad,
|
|
@@ -2211,6 +2253,8 @@ server.registerTool("update", {
|
|
|
2211
2253
|
changes.push("title");
|
|
2212
2254
|
if (content !== undefined)
|
|
2213
2255
|
changes.push("content");
|
|
2256
|
+
if (semanticPatch !== undefined)
|
|
2257
|
+
changes.push("semanticPatch");
|
|
2214
2258
|
if (tags !== undefined)
|
|
2215
2259
|
changes.push("tags");
|
|
2216
2260
|
if (lifecycle !== undefined && lifecycle !== note.lifecycle)
|