@danielmarbach/mnemonic-mcp 0.9.0 → 0.10.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 CHANGED
@@ -4,6 +4,17 @@ 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.10.0] - 2026-03-14
8
+
9
+ ### Changed
10
+
11
+ - Performance in hot read paths: `recall` now reuses note reads within a single request instead of re-reading the same note during scoring and formatting.
12
+ - Consolidation analysis (`detect-duplicates`, `suggest-merges`) now preloads embeddings once per note and reuses vectors during pairwise similarity checks.
13
+ - `Storage.listNotes` and `Storage.listEmbeddings` now load files in parallel to reduce serialized disk I/O while preserving existing output behavior.
14
+ - `VaultManager.searchOrder` now avoids duplicate git-root resolution in a single call, reducing unnecessary git subprocess work in read-heavy flows.
15
+ - Mutating tools now return deterministic retry metadata when a git commit fails after the mutation is already written, including attempted commit message/body/files and retry safety hints.
16
+ - Commit persistence reporting now includes explicit commit-failure details (`git.commit=failed` with `commitError`) so recovery can be automated without reconstructing commit intent.
17
+
7
18
  ## [0.9.0] - 2026-03-14
8
19
 
9
20
  ### Added
package/build/git.d.ts CHANGED
@@ -11,12 +11,13 @@ export interface SyncResult {
11
11
  pushedCommits: number;
12
12
  }
13
13
  export interface CommitResult {
14
- status: "committed" | "skipped";
15
- reason?: "git-disabled" | "no-changes";
14
+ status: "committed" | "skipped" | "failed";
15
+ reason?: "git-disabled" | "no-changes" | "error";
16
+ error?: string;
16
17
  }
17
18
  export interface PushResult {
18
19
  status: "pushed" | "skipped" | "failed";
19
- reason?: "git-disabled" | "no-remote" | "auto-push-disabled";
20
+ reason?: "git-disabled" | "no-remote" | "auto-push-disabled" | "commit-failed";
20
21
  error?: string;
21
22
  }
22
23
  export declare class GitOps {
@@ -1 +1 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAEA,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,SAAS,EAAE,QAAQ,GAAG,MAAM,EAAE,KAAK,EAAE,OAAO;CAKzD;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,uEAAuE;IACvE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,2CAA2C;IAC3C,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,+CAA+C;IAC/C,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,MAAM,CAAC,EAAE,cAAc,GAAG,YAAY,CAAC;CACxC;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACxC,MAAM,CAAC,EAAE,cAAc,GAAG,WAAW,GAAG,oBAAoB,CAAC;IAC7D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,OAAO,CAAU;gBAEb,OAAO,EAAE,MAAM,EAAE,WAAW,GAAE,MAAgB;IAMpD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B;;;;;;;;;;;;;;;;;;OAkBG;IACG,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKzE,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAwB9F;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IA6BjC,uEAAuE;IACjE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,cAAc,IAAI,OAAO,CAAC,UAAU,CAAC;YAiB7B,WAAW;YASX,oBAAoB;IASlC;;;OAGG;YACW,cAAc;CAuC7B"}
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAEA,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,SAAS,EAAE,QAAQ,GAAG,MAAM,EAAE,KAAK,EAAE,OAAO;CAKzD;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,uEAAuE;IACvE,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,2CAA2C;IAC3C,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,+CAA+C;IAC/C,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC3C,MAAM,CAAC,EAAE,cAAc,GAAG,YAAY,GAAG,OAAO,CAAC;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;IACxC,MAAM,CAAC,EAAE,cAAc,GAAG,WAAW,GAAG,oBAAoB,GAAG,eAAe,CAAC;IAC/E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,MAAM;IACjB,OAAO,CAAC,GAAG,CAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,OAAO,CAAU;gBAEb,OAAO,EAAE,MAAM,EAAE,WAAW,GAAE,MAAgB;IAMpD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B;;;;;;;;;;;;;;;;;;OAkBG;IACG,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAQzE,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAyB9F;;;;OAIG;IACG,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC;IA6BjC,uEAAuE;IACjE,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,cAAc,IAAI,OAAO,CAAC,UAAU,CAAC;YAiB7B,WAAW;YASX,oBAAoB;IASlC;;;OAGG;YACW,cAAc;CAuC7B"}
package/build/git.js CHANGED
@@ -53,6 +53,9 @@ export class GitOps {
53
53
  */
54
54
  async commit(message, files, body) {
55
55
  const result = await this.commitWithStatus(message, files, body);
56
+ if (result.status === "failed") {
57
+ throw new GitOperationError("commit", result.error ?? "unknown commit failure");
58
+ }
56
59
  return result.status === "committed";
57
60
  }
58
61
  async commitWithStatus(message, files, body) {
@@ -75,8 +78,9 @@ export class GitOps {
75
78
  return { status: "committed" };
76
79
  }
77
80
  catch (err) {
78
- console.error(`[git] Commit failed: ${err}`);
79
- throw new GitOperationError("commit", err);
81
+ const message = err instanceof Error ? err.message : String(err);
82
+ console.error(`[git] Commit failed: ${message}`);
83
+ return { status: "failed", reason: "error", error: message };
80
84
  }
81
85
  }
82
86
  /**
package/build/git.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
package/build/index.js CHANGED
@@ -592,12 +592,34 @@ function buildPersistenceStatus(args) {
592
592
  commitMessage: args.commitMessage,
593
593
  commitBody: args.commitBody,
594
594
  commitReason: args.commit.reason,
595
+ commitError: args.commit.error,
595
596
  pushReason: args.push.reason,
596
597
  pushError: args.push.error,
597
598
  },
599
+ retry: args.retry,
598
600
  durability: resolveDurability(args.commit, args.push),
599
601
  };
600
602
  }
603
+ function buildMutationRetryContract(args) {
604
+ if (args.commit.status !== "failed") {
605
+ return undefined;
606
+ }
607
+ return {
608
+ attemptedCommit: {
609
+ message: args.commitMessage,
610
+ body: args.commitBody,
611
+ files: args.files,
612
+ cwd: args.cwd,
613
+ vault: storageLabel(args.vault),
614
+ error: args.commit.error ?? "Unknown git commit failure",
615
+ },
616
+ mutationApplied: args.mutationApplied,
617
+ retrySafe: args.mutationApplied,
618
+ rationale: args.mutationApplied
619
+ ? "Mutation is already persisted on disk; commit can be retried deterministically."
620
+ : "Mutation was not applied; retry may require re-running the operation.",
621
+ };
622
+ }
601
623
  function formatPersistenceSummary(persistence) {
602
624
  const parts = [
603
625
  `Persistence: embedding ${persistence.embedding.status}`,
@@ -716,7 +738,7 @@ async function formatProjectPolicyLine(projectId) {
716
738
  }
717
739
  return `Policy: default write scope ${policy.defaultScope} (updated ${policy.updatedAt})`;
718
740
  }
719
- async function moveNoteBetweenVaults(found, targetVault, noteToWrite) {
741
+ async function moveNoteBetweenVaults(found, targetVault, noteToWrite, cwd) {
720
742
  const { note, vault: sourceVault } = found;
721
743
  const finalNote = noteToWrite ?? note;
722
744
  const embedding = await sourceVault.storage.readEmbedding(note.id);
@@ -733,7 +755,9 @@ async function moveNoteBetweenVaults(found, targetVault, noteToWrite) {
733
755
  noteTitle: finalNote.title,
734
756
  projectName: finalNote.projectName,
735
757
  });
736
- const targetCommit = await targetVault.git.commitWithStatus(`move: ${finalNote.title}`, [vaultManager.noteRelPath(targetVault, finalNote.id)], targetCommitBody);
758
+ const targetCommitMessage = `move: ${finalNote.title}`;
759
+ const targetCommitFiles = [vaultManager.noteRelPath(targetVault, finalNote.id)];
760
+ const targetCommit = await targetVault.git.commitWithStatus(targetCommitMessage, targetCommitFiles, targetCommitBody);
737
761
  const sourceCommitBody = formatCommitBody({
738
762
  summary: `Moved to ${targetVaultLabel}`,
739
763
  noteId: finalNote.id,
@@ -741,7 +765,18 @@ async function moveNoteBetweenVaults(found, targetVault, noteToWrite) {
741
765
  projectName: finalNote.projectName,
742
766
  });
743
767
  await sourceVault.git.commitWithStatus(`move: ${finalNote.title}`, [vaultManager.noteRelPath(sourceVault, finalNote.id)], sourceCommitBody);
744
- const targetPush = await pushAfterMutation(targetVault);
768
+ const targetPush = targetCommit.status === "committed"
769
+ ? await pushAfterMutation(targetVault)
770
+ : { status: "skipped", reason: "commit-failed" };
771
+ const retry = buildMutationRetryContract({
772
+ commit: targetCommit,
773
+ commitMessage: targetCommitMessage,
774
+ commitBody: targetCommitBody,
775
+ files: targetCommitFiles,
776
+ cwd,
777
+ vault: targetVault,
778
+ mutationApplied: true,
779
+ });
745
780
  if (sourceVault !== targetVault) {
746
781
  await pushAfterMutation(sourceVault);
747
782
  }
@@ -753,8 +788,9 @@ async function moveNoteBetweenVaults(found, targetVault, noteToWrite) {
753
788
  embedding: embedding ? { status: "written" } : { status: "skipped", reason: "no-source-embedding" },
754
789
  commit: targetCommit,
755
790
  push: targetPush,
756
- commitMessage: `move: ${finalNote.title}`,
791
+ commitMessage: targetCommitMessage,
757
792
  commitBody: targetCommitBody,
793
+ retry,
758
794
  }),
759
795
  };
760
796
  }
@@ -952,8 +988,21 @@ server.registerTool("set_project_identity", {
952
988
  `Resolved identity: ${candidateIdentity.project.id}\n` +
953
989
  `Remote: ${remoteName}`,
954
990
  });
955
- await vaultManager.main.git.commit(`identity: ${defaultProject.name} use remote ${remoteName}`, ["config.json"], commitBody);
956
- await pushAfterMutation(vaultManager.main);
991
+ const commitMessage = `identity: ${defaultProject.name} use remote ${remoteName}`;
992
+ const commitFiles = ["config.json"];
993
+ const commitStatus = await vaultManager.main.git.commitWithStatus(commitMessage, commitFiles, commitBody);
994
+ const pushStatus = commitStatus.status === "committed"
995
+ ? await pushAfterMutation(vaultManager.main)
996
+ : { status: "skipped", reason: "commit-failed" };
997
+ const retry = buildMutationRetryContract({
998
+ commit: commitStatus,
999
+ commitMessage,
1000
+ commitBody,
1001
+ files: commitFiles,
1002
+ cwd,
1003
+ vault: vaultManager.main,
1004
+ mutationApplied: true,
1005
+ });
957
1006
  const structuredContent = {
958
1007
  action: "project_identity_set",
959
1008
  project: {
@@ -971,12 +1020,14 @@ server.registerTool("set_project_identity", {
971
1020
  remoteName,
972
1021
  updatedAt: now,
973
1022
  },
1023
+ retry,
974
1024
  };
975
1025
  return {
976
1026
  content: [{
977
1027
  type: "text",
978
1028
  text: `Project identity override set for ${defaultProject.name}: ` +
979
- `default=\`${defaultProject.id}\`, effective=\`${candidateIdentity.project.id}\`, remote=${remoteName}`,
1029
+ `default=\`${defaultProject.id}\`, effective=\`${candidateIdentity.project.id}\`, remote=${remoteName}` +
1030
+ `${commitStatus.status === "failed" ? `\nCommit failed; retry data included. Push status: ${pushStatus.status}.` : ""}`,
980
1031
  }],
981
1032
  structuredContent,
982
1033
  };
@@ -1236,16 +1287,30 @@ server.registerTool("remember", {
1236
1287
  scope: writeScope,
1237
1288
  tags: tags,
1238
1289
  });
1239
- const commitStatus = await vault.git.commitWithStatus(`remember: ${title}`, [vaultManager.noteRelPath(vault, id)], commitBody);
1240
- const pushStatus = await pushAfterMutation(vault);
1290
+ const commitMessage = `remember: ${title}`;
1291
+ const commitFiles = [vaultManager.noteRelPath(vault, id)];
1292
+ const commitStatus = await vault.git.commitWithStatus(commitMessage, commitFiles, commitBody);
1293
+ const pushStatus = commitStatus.status === "committed"
1294
+ ? await pushAfterMutation(vault)
1295
+ : { status: "skipped", reason: "commit-failed" };
1296
+ const retry = buildMutationRetryContract({
1297
+ commit: commitStatus,
1298
+ commitMessage,
1299
+ commitBody,
1300
+ files: commitFiles,
1301
+ cwd,
1302
+ vault,
1303
+ mutationApplied: true,
1304
+ });
1241
1305
  const persistence = buildPersistenceStatus({
1242
1306
  storage: vault.storage,
1243
1307
  id,
1244
1308
  embedding: embeddingStatus,
1245
1309
  commit: commitStatus,
1246
1310
  push: pushStatus,
1247
- commitMessage: `remember: ${title}`,
1311
+ commitMessage,
1248
1312
  commitBody,
1313
+ retry,
1249
1314
  });
1250
1315
  const vaultLabel = vault.isProject ? " [project vault]" : " [main vault]";
1251
1316
  const textContent = `Remembered as \`${id}\` [${projectScope}, stored=${writeScope}]${vaultLabel}\n${formatPersistenceSummary(persistence)}`;
@@ -1345,8 +1410,21 @@ server.registerTool("set_project_memory_policy", {
1345
1410
  ? `\nProtected branch patterns: ${effectiveProtectedBranchPatterns.join(", ")}`
1346
1411
  : ""}`,
1347
1412
  });
1348
- await vaultManager.main.git.commit(`policy: ${project.name} default scope ${effectiveDefaultScope}`, ["config.json"], commitBody);
1349
- await pushAfterMutation(vaultManager.main);
1413
+ const commitMessage = `policy: ${project.name} default scope ${effectiveDefaultScope}`;
1414
+ const commitFiles = ["config.json"];
1415
+ const commitStatus = await vaultManager.main.git.commitWithStatus(commitMessage, commitFiles, commitBody);
1416
+ const pushStatus = commitStatus.status === "committed"
1417
+ ? await pushAfterMutation(vaultManager.main)
1418
+ : { status: "skipped", reason: "commit-failed" };
1419
+ const retry = buildMutationRetryContract({
1420
+ commit: commitStatus,
1421
+ commitMessage,
1422
+ commitBody,
1423
+ files: commitFiles,
1424
+ cwd,
1425
+ vault: vaultManager.main,
1426
+ mutationApplied: true,
1427
+ });
1350
1428
  const structuredContent = {
1351
1429
  action: "policy_set",
1352
1430
  project: { id: project.id, name: project.name },
@@ -1354,13 +1432,15 @@ server.registerTool("set_project_memory_policy", {
1354
1432
  consolidationMode: effectiveConsolidationMode,
1355
1433
  protectedBranchBehavior: effectiveProtectedBranchBehavior,
1356
1434
  protectedBranchPatterns: effectiveProtectedBranchPatterns,
1357
- timestamp: now,
1435
+ updatedAt: now,
1436
+ retry,
1358
1437
  };
1359
1438
  return {
1360
1439
  content: [{
1361
1440
  type: "text",
1362
1441
  text: `Project memory policy set for ${project.name}: defaultScope=${effectiveDefaultScope}` +
1363
- `${modeStr}${branchBehaviorStr}${branchPatternsStr}`,
1442
+ `${modeStr}${branchBehaviorStr}${branchPatternsStr}` +
1443
+ `${commitStatus.status === "failed" ? `\nCommit failed; retry data included. Push status: ${pushStatus.status}.` : ""}`,
1364
1444
  }],
1365
1445
  structuredContent,
1366
1446
  };
@@ -1473,6 +1553,20 @@ server.registerTool("recall", {
1473
1553
  const project = await resolveProject(cwd);
1474
1554
  const queryVec = await embed(query);
1475
1555
  const vaults = await vaultManager.searchOrder(cwd);
1556
+ const noteCache = new Map();
1557
+ const noteCacheKey = (vault, id) => `${vault.storage.vaultPath}::${id}`;
1558
+ const readCachedNote = async (vault, id) => {
1559
+ const key = noteCacheKey(vault, id);
1560
+ const cached = noteCache.get(key);
1561
+ if (cached) {
1562
+ return cached;
1563
+ }
1564
+ const note = await vault.storage.readNote(id);
1565
+ if (note) {
1566
+ noteCache.set(key, note);
1567
+ }
1568
+ return note;
1569
+ };
1476
1570
  for (const vault of vaults) {
1477
1571
  await embedMissingNotes(vault.storage).catch(() => { });
1478
1572
  }
@@ -1483,7 +1577,7 @@ server.registerTool("recall", {
1483
1577
  const rawScore = cosineSimilarity(queryVec, rec.embedding);
1484
1578
  if (rawScore < minSimilarity)
1485
1579
  continue;
1486
- const note = await vault.storage.readNote(rec.id);
1580
+ const note = await readCachedNote(vault, rec.id);
1487
1581
  if (!note)
1488
1582
  continue;
1489
1583
  if (tags && tags.length > 0) {
@@ -1512,7 +1606,7 @@ server.registerTool("recall", {
1512
1606
  }
1513
1607
  const sections = [];
1514
1608
  for (const { id, score, vault } of top) {
1515
- const note = await vault.storage.readNote(id);
1609
+ const note = await readCachedNote(vault, id);
1516
1610
  if (note)
1517
1611
  sections.push(formatNote(note, score));
1518
1612
  }
@@ -1523,7 +1617,7 @@ server.registerTool("recall", {
1523
1617
  // Build structured results array
1524
1618
  const structuredResults = [];
1525
1619
  for (const { id, score, vault, boosted } of top) {
1526
- const note = await vault.storage.readNote(id);
1620
+ const note = await readCachedNote(vault, id);
1527
1621
  if (note) {
1528
1622
  structuredResults.push({
1529
1623
  id,
@@ -1656,16 +1750,30 @@ server.registerTool("update", {
1656
1750
  projectName: updated.projectName,
1657
1751
  tags: updated.tags,
1658
1752
  });
1659
- const commitStatus = await vault.git.commitWithStatus(`update: ${updated.title}`, [vaultManager.noteRelPath(vault, id)], commitBody);
1660
- const pushStatus = await pushAfterMutation(vault);
1753
+ const commitMessage = `update: ${updated.title}`;
1754
+ const commitFiles = [vaultManager.noteRelPath(vault, id)];
1755
+ const commitStatus = await vault.git.commitWithStatus(commitMessage, commitFiles, commitBody);
1756
+ const pushStatus = commitStatus.status === "committed"
1757
+ ? await pushAfterMutation(vault)
1758
+ : { status: "skipped", reason: "commit-failed" };
1759
+ const retry = buildMutationRetryContract({
1760
+ commit: commitStatus,
1761
+ commitMessage,
1762
+ commitBody,
1763
+ files: commitFiles,
1764
+ cwd,
1765
+ vault,
1766
+ mutationApplied: true,
1767
+ });
1661
1768
  const persistence = buildPersistenceStatus({
1662
1769
  storage: vault.storage,
1663
1770
  id,
1664
1771
  embedding: embeddingStatus,
1665
1772
  commit: commitStatus,
1666
1773
  push: pushStatus,
1667
- commitMessage: `update: ${updated.title}`,
1774
+ commitMessage,
1668
1775
  commitBody,
1776
+ retry,
1669
1777
  });
1670
1778
  const structuredContent = {
1671
1779
  action: "updated",
@@ -1745,6 +1853,7 @@ server.registerTool("forget", {
1745
1853
  const vaultChanges = await removeRelationshipsToNoteIds([id]);
1746
1854
  // Always include the deleted note's path (git add on a deleted file stages the removal)
1747
1855
  addVaultChange(vaultChanges, noteVault, vaultManager.noteRelPath(noteVault, id));
1856
+ let retry;
1748
1857
  for (const [v, files] of vaultChanges) {
1749
1858
  const isPrimaryVault = v === noteVault;
1750
1859
  const summary = isPrimaryVault ? `Deleted note and cleaned up ${files.length - 1} reference(s)` : "Cleaned up dangling reference";
@@ -1754,8 +1863,22 @@ server.registerTool("forget", {
1754
1863
  noteTitle: note.title,
1755
1864
  projectName: note.projectName,
1756
1865
  });
1757
- await v.git.commit(`forget: ${note.title}`, files, commitBody);
1758
- await pushAfterMutation(v);
1866
+ const commitMessage = `forget: ${note.title}`;
1867
+ const commitStatus = await v.git.commitWithStatus(commitMessage, files, commitBody);
1868
+ if (!retry) {
1869
+ retry = buildMutationRetryContract({
1870
+ commit: commitStatus,
1871
+ commitMessage,
1872
+ commitBody,
1873
+ files,
1874
+ cwd,
1875
+ vault: v,
1876
+ mutationApplied: true,
1877
+ });
1878
+ }
1879
+ if (commitStatus.status === "committed") {
1880
+ await pushAfterMutation(v);
1881
+ }
1759
1882
  }
1760
1883
  const structuredContent = {
1761
1884
  action: "forgotten",
@@ -1765,6 +1888,7 @@ server.registerTool("forget", {
1765
1888
  projectName: note.projectName,
1766
1889
  relationshipsCleaned: vaultChanges.size > 0 ? Array.from(vaultChanges.values()).reduce((sum, files) => sum + files.length - 1, 0) : 0,
1767
1890
  vaultsModified: Array.from(vaultChanges.keys()).map(v => storageLabel(v)),
1891
+ retry,
1768
1892
  };
1769
1893
  return { content: [{ type: "text", text: `Forgotten '${id}' (${note.title})` }], structuredContent };
1770
1894
  });
@@ -2432,7 +2556,7 @@ server.registerTool("move_memory", {
2432
2556
  updatedAt: new Date().toISOString(),
2433
2557
  };
2434
2558
  }
2435
- const moveResult = await moveNoteBetweenVaults(found, targetVault, noteToWrite);
2559
+ const moveResult = await moveNoteBetweenVaults(found, targetVault, noteToWrite, cwd);
2436
2560
  const movedNote = moveResult.note;
2437
2561
  const associationValue = movedNote.projectName && movedNote.project
2438
2562
  ? `${movedNote.projectName} (${movedNote.project})`
@@ -2527,6 +2651,7 @@ server.registerTool("relate", {
2527
2651
  return { content: [{ type: "text", text: `Relationship already exists between '${fromId}' and '${toId}'` }], isError: true };
2528
2652
  }
2529
2653
  const modifiedNoteIds = [];
2654
+ let retry;
2530
2655
  for (const [vault, files] of vaultChanges) {
2531
2656
  const isFromVault = vault === fromVault;
2532
2657
  const thisNote = isFromVault ? fromNote : toNote;
@@ -2541,8 +2666,22 @@ server.registerTool("relate", {
2541
2666
  type,
2542
2667
  },
2543
2668
  });
2544
- await vault.git.commit(`relate: ${fromNote.title} ↔ ${toNote.title}`, files, commitBody);
2545
- await pushAfterMutation(vault);
2669
+ const commitMessage = `relate: ${fromNote.title} ↔ ${toNote.title}`;
2670
+ const commitStatus = await vault.git.commitWithStatus(commitMessage, files, commitBody);
2671
+ if (!retry) {
2672
+ retry = buildMutationRetryContract({
2673
+ commit: commitStatus,
2674
+ commitMessage,
2675
+ commitBody,
2676
+ files,
2677
+ cwd,
2678
+ vault,
2679
+ mutationApplied: true,
2680
+ });
2681
+ }
2682
+ if (commitStatus.status === "committed") {
2683
+ await pushAfterMutation(vault);
2684
+ }
2546
2685
  modifiedNoteIds.push(...files.map(f => path.basename(f, '.md')));
2547
2686
  }
2548
2687
  const dirStr = bidirectional ? "↔" : "→";
@@ -2553,6 +2692,7 @@ server.registerTool("relate", {
2553
2692
  type,
2554
2693
  bidirectional,
2555
2694
  notesModified: modifiedNoteIds,
2695
+ retry,
2556
2696
  };
2557
2697
  return {
2558
2698
  content: [{ type: "text", text: `Linked \`${fromId}\` ${dirStr} \`${toId}\` (${type})` }],
@@ -2616,6 +2756,7 @@ server.registerTool("unrelate", {
2616
2756
  if (vaultChanges.size === 0) {
2617
2757
  return { content: [{ type: "text", text: `No relationship found between '${fromId}' and '${toId}'` }], isError: true };
2618
2758
  }
2759
+ let retry;
2619
2760
  for (const [vault, files] of vaultChanges) {
2620
2761
  const found = foundFrom?.vault === vault ? foundFrom : foundTo;
2621
2762
  const commitBody = found
@@ -2625,8 +2766,22 @@ server.registerTool("unrelate", {
2625
2766
  projectName: found.note.projectName,
2626
2767
  })
2627
2768
  : undefined;
2628
- await vault.git.commit(`unrelate: ${fromId} ↔ ${toId}`, files, commitBody);
2629
- await pushAfterMutation(vault);
2769
+ const commitMessage = `unrelate: ${fromId} ↔ ${toId}`;
2770
+ const commitStatus = await vault.git.commitWithStatus(commitMessage, files, commitBody);
2771
+ if (!retry) {
2772
+ retry = buildMutationRetryContract({
2773
+ commit: commitStatus,
2774
+ commitMessage,
2775
+ commitBody,
2776
+ files,
2777
+ cwd,
2778
+ vault,
2779
+ mutationApplied: true,
2780
+ });
2781
+ }
2782
+ if (commitStatus.status === "committed") {
2783
+ await pushAfterMutation(vault);
2784
+ }
2630
2785
  }
2631
2786
  const modifiedNoteIds = [];
2632
2787
  for (const [vault, files] of vaultChanges) {
@@ -2639,6 +2794,7 @@ server.registerTool("unrelate", {
2639
2794
  type: "related-to", // not tracked for unrelate
2640
2795
  bidirectional,
2641
2796
  notesModified: modifiedNoteIds,
2797
+ retry,
2642
2798
  };
2643
2799
  return { content: [{ type: "text", text: `Removed relationship between \`${fromId}\` and \`${toId}\`` }], structuredContent };
2644
2800
  });
@@ -2751,21 +2907,22 @@ async function detectDuplicates(entries, threshold, project) {
2751
2907
  const checked = new Set();
2752
2908
  let foundCount = 0;
2753
2909
  const duplicates = [];
2910
+ const embeddings = await loadEmbeddingsByNoteId(entries);
2754
2911
  for (let i = 0; i < entries.length; i++) {
2755
2912
  const entryA = entries[i];
2756
2913
  if (checked.has(entryA.note.id))
2757
2914
  continue;
2758
- const embeddingA = await entryA.vault.storage.readEmbedding(entryA.note.id);
2915
+ const embeddingA = embeddings.get(entryA.note.id);
2759
2916
  if (!embeddingA)
2760
2917
  continue;
2761
2918
  for (let j = i + 1; j < entries.length; j++) {
2762
2919
  const entryB = entries[j];
2763
2920
  if (checked.has(entryB.note.id))
2764
2921
  continue;
2765
- const embeddingB = await entryB.vault.storage.readEmbedding(entryB.note.id);
2922
+ const embeddingB = embeddings.get(entryB.note.id);
2766
2923
  if (!embeddingB)
2767
2924
  continue;
2768
- const similarity = cosineSimilarity(embeddingA.embedding, embeddingB.embedding);
2925
+ const similarity = cosineSimilarity(embeddingA, embeddingB);
2769
2926
  if (similarity >= threshold) {
2770
2927
  foundCount++;
2771
2928
  lines.push(`${foundCount}. ${entryA.note.title} (${entryA.note.id})`);
@@ -2895,11 +3052,12 @@ async function suggestMerges(entries, threshold, defaultConsolidationMode, proje
2895
3052
  const checked = new Set();
2896
3053
  let suggestionCount = 0;
2897
3054
  const suggestions = [];
3055
+ const embeddings = await loadEmbeddingsByNoteId(entries);
2898
3056
  for (let i = 0; i < entries.length; i++) {
2899
3057
  const entryA = entries[i];
2900
3058
  if (checked.has(entryA.note.id))
2901
3059
  continue;
2902
- const embeddingA = await entryA.vault.storage.readEmbedding(entryA.note.id);
3060
+ const embeddingA = embeddings.get(entryA.note.id);
2903
3061
  if (!embeddingA)
2904
3062
  continue;
2905
3063
  const similar = [];
@@ -2907,10 +3065,10 @@ async function suggestMerges(entries, threshold, defaultConsolidationMode, proje
2907
3065
  const entryB = entries[j];
2908
3066
  if (checked.has(entryB.note.id))
2909
3067
  continue;
2910
- const embeddingB = await entryB.vault.storage.readEmbedding(entryB.note.id);
3068
+ const embeddingB = embeddings.get(entryB.note.id);
2911
3069
  if (!embeddingB)
2912
3070
  continue;
2913
- const similarity = cosineSimilarity(embeddingA.embedding, embeddingB.embedding);
3071
+ const similarity = cosineSimilarity(embeddingA, embeddingB);
2914
3072
  if (similarity >= threshold) {
2915
3073
  similar.push({ entry: entryB, similarity });
2916
3074
  }
@@ -2972,6 +3130,16 @@ async function suggestMerges(entries, threshold, defaultConsolidationMode, proje
2972
3130
  };
2973
3131
  return { content: [{ type: "text", text: lines.join("\n") }], structuredContent };
2974
3132
  }
3133
+ async function loadEmbeddingsByNoteId(entries) {
3134
+ const embeddings = new Map();
3135
+ await Promise.all(entries.map(async (entry) => {
3136
+ const record = await entry.vault.storage.readEmbedding(entry.note.id);
3137
+ if (record) {
3138
+ embeddings.set(entry.note.id, record.embedding);
3139
+ }
3140
+ }));
3141
+ return embeddings;
3142
+ }
2975
3143
  async function executeMerge(entries, mergePlan, defaultConsolidationMode, project, cwd, explicitMode, policy, allowProtectedBranch = false) {
2976
3144
  const sourceIds = normalizeMergePlanSourceIds(mergePlan.sourceIds);
2977
3145
  const targetTitle = mergePlan.targetTitle.trim();
@@ -3162,6 +3330,7 @@ async function executeMerge(entries, mergePlan, defaultConsolidationMode, projec
3162
3330
  let targetPushStatus = { status: "skipped", reason: "no-remote" };
3163
3331
  let targetCommitBody;
3164
3332
  let targetCommitMessage;
3333
+ let targetCommitFiles;
3165
3334
  for (const [vault, files] of vaultChanges) {
3166
3335
  const isTargetVault = vault === targetVault;
3167
3336
  // Determine action and summary based on mode
@@ -3199,14 +3368,28 @@ async function executeMerge(entries, mergePlan, defaultConsolidationMode, projec
3199
3368
  });
3200
3369
  const commitMessage = `${action}: ${targetTitle}`;
3201
3370
  const commitStatus = await vault.git.commitWithStatus(commitMessage, files, commitBody);
3202
- const pushStatus = await pushAfterMutation(vault);
3371
+ const pushStatus = commitStatus.status === "committed"
3372
+ ? await pushAfterMutation(vault)
3373
+ : { status: "skipped", reason: "commit-failed" };
3203
3374
  if (isTargetVault) {
3204
3375
  targetCommitStatus = commitStatus;
3205
3376
  targetPushStatus = pushStatus;
3206
3377
  targetCommitBody = commitBody;
3207
3378
  targetCommitMessage = commitMessage;
3379
+ targetCommitFiles = [...files];
3208
3380
  }
3209
3381
  }
3382
+ const retry = targetCommitMessage && targetCommitFiles
3383
+ ? buildMutationRetryContract({
3384
+ commit: targetCommitStatus,
3385
+ commitMessage: targetCommitMessage,
3386
+ commitBody: targetCommitBody,
3387
+ files: targetCommitFiles,
3388
+ cwd,
3389
+ vault: targetVault,
3390
+ mutationApplied: true,
3391
+ })
3392
+ : undefined;
3210
3393
  const persistence = buildPersistenceStatus({
3211
3394
  storage: targetVault.storage,
3212
3395
  id: targetId,
@@ -3215,6 +3398,7 @@ async function executeMerge(entries, mergePlan, defaultConsolidationMode, projec
3215
3398
  push: targetPushStatus,
3216
3399
  commitMessage: targetCommitMessage,
3217
3400
  commitBody: targetCommitBody,
3401
+ retry,
3218
3402
  });
3219
3403
  const lines = [];
3220
3404
  lines.push(`Consolidated ${sourceIds.length} notes into '${targetId}'`);
@@ -3245,6 +3429,7 @@ async function executeMerge(entries, mergePlan, defaultConsolidationMode, projec
3245
3429
  notesProcessed: entries.length,
3246
3430
  notesModified: vaultChanges.size,
3247
3431
  persistence,
3432
+ retry,
3248
3433
  };
3249
3434
  return { content: [{ type: "text", text: lines.join("\n") }], structuredContent };
3250
3435
  }
@@ -3370,14 +3555,29 @@ async function pruneSuperseded(entries, consolidationMode, project, cwd, policy,
3370
3555
  }
3371
3556
  }
3372
3557
  // Commit changes per vault
3558
+ let retry;
3373
3559
  for (const [vault, files] of vaultChanges) {
3374
3560
  const prunedIds = files.map((f) => f.replace(/\.mnemonic\/notes\/(.+)\.md$/, "$1").replace(/notes\/(.+)\.md$/, "$1"));
3375
3561
  const commitBody = formatCommitBody({
3376
3562
  noteIds: prunedIds,
3377
3563
  description: `Pruned ${prunedIds.length} superseded note(s)\nNotes: ${prunedIds.join(", ")}`,
3378
3564
  });
3379
- await vault.git.commit(`prune: removed ${files.length} superseded note(s)`, files, commitBody);
3380
- await pushAfterMutation(vault);
3565
+ const commitMessage = `prune: removed ${files.length} superseded note(s)`;
3566
+ const commitStatus = await vault.git.commitWithStatus(commitMessage, files, commitBody);
3567
+ if (!retry) {
3568
+ retry = buildMutationRetryContract({
3569
+ commit: commitStatus,
3570
+ commitMessage,
3571
+ commitBody,
3572
+ files,
3573
+ cwd,
3574
+ vault,
3575
+ mutationApplied: true,
3576
+ });
3577
+ }
3578
+ if (commitStatus.status === "committed") {
3579
+ await pushAfterMutation(vault);
3580
+ }
3381
3581
  }
3382
3582
  lines.push("");
3383
3583
  lines.push(`Pruned ${supersededIds.size} note(s).`);
@@ -3388,6 +3588,7 @@ async function pruneSuperseded(entries, consolidationMode, project, cwd, policy,
3388
3588
  projectName: project?.name,
3389
3589
  notesProcessed: entries.length,
3390
3590
  notesModified: vaultChanges.size,
3591
+ retry,
3391
3592
  };
3392
3593
  return { content: [{ type: "text", text: lines.join("\n") }], structuredContent };
3393
3594
  }