@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 +11 -0
- package/build/git.d.ts +4 -3
- package/build/git.d.ts.map +1 -1
- package/build/git.js +6 -2
- package/build/git.js.map +1 -1
- package/build/index.js +237 -36
- package/build/index.js.map +1 -1
- package/build/storage.d.ts.map +1 -1
- package/build/storage.js +7 -12
- package/build/storage.js.map +1 -1
- package/build/structured-content.d.ts +166 -6
- package/build/structured-content.d.ts.map +1 -1
- package/build/structured-content.js +20 -1
- package/build/structured-content.js.map +1 -1
- package/build/vault.d.ts.map +1 -1
- package/build/vault.js +10 -9
- package/build/vault.js.map +1 -1
- package/package.json +1 -1
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 {
|
package/build/git.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
79
|
-
|
|
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":"AAAA,OAAO,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAElD,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,SAA4B,EAAE,KAAc;QACtD,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtE,KAAK,CAAC,OAAO,SAAS,YAAY,MAAM,EAAE,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
956
|
-
|
|
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
|
|
1240
|
-
const
|
|
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
|
|
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
|
-
|
|
1349
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1660
|
-
const
|
|
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
|
|
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
|
-
|
|
1758
|
-
await
|
|
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
|
-
|
|
2545
|
-
await
|
|
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
|
-
|
|
2629
|
-
await
|
|
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 =
|
|
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 =
|
|
2922
|
+
const embeddingB = embeddings.get(entryB.note.id);
|
|
2766
2923
|
if (!embeddingB)
|
|
2767
2924
|
continue;
|
|
2768
|
-
const similarity = cosineSimilarity(embeddingA
|
|
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 =
|
|
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 =
|
|
3068
|
+
const embeddingB = embeddings.get(entryB.note.id);
|
|
2911
3069
|
if (!embeddingB)
|
|
2912
3070
|
continue;
|
|
2913
|
-
const similarity = cosineSimilarity(embeddingA
|
|
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 =
|
|
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
|
-
|
|
3380
|
-
await
|
|
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
|
}
|