@danielmarbach/mnemonic-mcp 0.11.2 → 0.12.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 +13 -0
- package/README.md +1 -0
- package/build/git.d.ts +8 -1
- package/build/git.d.ts.map +1 -1
- package/build/git.js +41 -6
- package/build/git.js.map +1 -1
- package/build/index.js +112 -3
- package/build/index.js.map +1 -1
- package/build/structured-content.d.ts +113 -0
- package/build/structured-content.d.ts.map +1 -1
- package/build/structured-content.js +18 -0
- package/build/structured-content.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ 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.12.0] - 2026-03-15
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- `discover_tags` MCP tool: lists existing tags with usage counts, example note titles, lifecycle distribution, and `isTemporaryOnly` flags for consistent tag terminology across sessions.
|
|
12
|
+
|
|
13
|
+
## [0.11.3] - 2026-03-15
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `git.add()` index.lock errors now retry with exponential backoff before failing, matching the retry behavior for `git.commit()` failures.
|
|
18
|
+
- Retry contract now includes `operation` field indicating whether add or commit failed, so clients can retry appropriately.
|
|
19
|
+
|
|
7
20
|
## [0.11.2] - 2026-03-15
|
|
8
21
|
|
|
9
22
|
### Fixed
|
package/README.md
CHANGED
|
@@ -410,6 +410,7 @@ Imported notes are written to the main vault with `lifecycle: permanent` and `sc
|
|
|
410
410
|
|-----------------------------|--------------------------------------------------------------------------|
|
|
411
411
|
| `consolidate` | Merge multiple notes into one with relationship to sources |
|
|
412
412
|
| `detect_project` | Resolve `cwd` to stable project id via git remote URL |
|
|
413
|
+
| `discover_tags` | List existing tags with usage counts and examples for consistent terminology |
|
|
413
414
|
| `execute_migration` | Execute a named migration (supports dry-run) |
|
|
414
415
|
| `forget` | Delete note + embedding, git commit + push, cleanup relationships |
|
|
415
416
|
| `get` | Fetch one or more notes by exact id |
|
package/build/git.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare class GitOperationError extends Error {
|
|
2
|
-
constructor(operation: "commit" | "push", cause: unknown);
|
|
2
|
+
constructor(operation: "add" | "commit" | "push", cause: unknown);
|
|
3
3
|
}
|
|
4
4
|
export interface SyncResult {
|
|
5
5
|
hasRemote: boolean;
|
|
@@ -13,6 +13,8 @@ export interface SyncResult {
|
|
|
13
13
|
export interface CommitResult {
|
|
14
14
|
status: "committed" | "skipped" | "failed";
|
|
15
15
|
reason?: "git-disabled" | "no-changes" | "error";
|
|
16
|
+
/** Which operation failed, when status is "failed" */
|
|
17
|
+
operation?: "add" | "commit";
|
|
16
18
|
error?: string;
|
|
17
19
|
}
|
|
18
20
|
export interface PushResult {
|
|
@@ -52,6 +54,11 @@ export declare class GitOps {
|
|
|
52
54
|
*/
|
|
53
55
|
commit(message: string, files: string[], body?: string): Promise<boolean>;
|
|
54
56
|
commitWithStatus(message: string, files: string[], body?: string): Promise<CommitResult>;
|
|
57
|
+
/**
|
|
58
|
+
* Retry git.add() with exponential backoff for transient index.lock errors.
|
|
59
|
+
* Returns a CommitResult-like object for add failures.
|
|
60
|
+
*/
|
|
61
|
+
private addWithRetry;
|
|
55
62
|
/**
|
|
56
63
|
* Bidirectional sync: fetch → count unpushed local commits → pull (rebase)
|
|
57
64
|
* → push. Returns details about what changed so callers can trigger
|
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;
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAEA,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,SAAS,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,EAAE,KAAK,EAAE,OAAO;CAKjE;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,sDAAsD;IACtD,SAAS,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAC7B,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;IAmC9F;;;OAGG;YACW,YAAY;IA4B1B;;;;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
|
@@ -61,12 +61,19 @@ export class GitOps {
|
|
|
61
61
|
async commitWithStatus(message, files, body) {
|
|
62
62
|
if (!this.enabled)
|
|
63
63
|
return { status: "skipped", reason: "git-disabled" };
|
|
64
|
+
// Scope every add+commit to only the paths mnemonic manages.
|
|
65
|
+
// Never commit files outside the vault — e.g. src/ or test/ changes
|
|
66
|
+
// that happen to be staged in the same repo.
|
|
67
|
+
const scopedFiles = files.length > 0 ? files : [`${this.notesRelDir}/`];
|
|
68
|
+
// Retry git.add() with exponential backoff for transient index.lock errors.
|
|
69
|
+
// git.add() can fail with "Unable to create '.git/index.lock': File exists"
|
|
70
|
+
// when concurrent git operations race for the index. This is recoverable
|
|
71
|
+
// by retrying after a short delay.
|
|
72
|
+
const addResult = await this.addWithRetry(scopedFiles);
|
|
73
|
+
if (addResult.status === "failed") {
|
|
74
|
+
return addResult;
|
|
75
|
+
}
|
|
64
76
|
try {
|
|
65
|
-
// Scope every add+commit to only the paths mnemonic manages.
|
|
66
|
-
// Never commit files outside the vault — e.g. src/ or test/ changes
|
|
67
|
-
// that happen to be staged in the same repo.
|
|
68
|
-
const scopedFiles = files.length > 0 ? files : [`${this.notesRelDir}/`];
|
|
69
|
-
await this.git.add(scopedFiles);
|
|
70
77
|
const status = await this.git.status();
|
|
71
78
|
if (status.staged.length === 0)
|
|
72
79
|
return { status: "skipped", reason: "no-changes" };
|
|
@@ -80,8 +87,36 @@ export class GitOps {
|
|
|
80
87
|
catch (err) {
|
|
81
88
|
const errMessage = err instanceof Error ? err.message : String(err);
|
|
82
89
|
console.error(`[git] Commit failed: ${errMessage}`);
|
|
83
|
-
return { status: "failed", reason: "error", error: errMessage };
|
|
90
|
+
return { status: "failed", reason: "error", operation: "commit", error: errMessage };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Retry git.add() with exponential backoff for transient index.lock errors.
|
|
95
|
+
* Returns a CommitResult-like object for add failures.
|
|
96
|
+
*/
|
|
97
|
+
async addWithRetry(files) {
|
|
98
|
+
const maxRetries = 3;
|
|
99
|
+
const baseDelayMs = 50;
|
|
100
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
101
|
+
try {
|
|
102
|
+
await this.git.add(files);
|
|
103
|
+
return { status: "committed" };
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
const errMessage = err instanceof Error ? err.message : String(err);
|
|
107
|
+
const isLockError = errMessage.includes("index.lock") || errMessage.includes("File exists");
|
|
108
|
+
if (isLockError && attempt < maxRetries - 1) {
|
|
109
|
+
const delayMs = baseDelayMs * Math.pow(2, attempt);
|
|
110
|
+
console.error(`[git] add() lock contention, retrying in ${delayMs}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
111
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
console.error(`[git] add() failed: ${errMessage}`);
|
|
115
|
+
return { status: "failed", reason: "error", operation: "add", error: errMessage };
|
|
116
|
+
}
|
|
84
117
|
}
|
|
118
|
+
// Should be unreachable, but TypeScript needs a return
|
|
119
|
+
return { status: "failed", reason: "error", operation: "add", error: "max retries exceeded" };
|
|
85
120
|
}
|
|
86
121
|
/**
|
|
87
122
|
* Bidirectional sync: fetch → count unpushed local commits → pull (rebase)
|
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,
|
|
1
|
+
{"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
|
package/build/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import { checkBranchChange } from "./branch-tracker.js";
|
|
|
19
19
|
import { Migrator } from "./migration.js";
|
|
20
20
|
import { parseMemorySections } from "./import.js";
|
|
21
21
|
import { defaultClaudeHome, defaultVaultPath, resolveUserPath } from "./paths.js";
|
|
22
|
-
import { RememberResultSchema, RecallResultSchema, ListResultSchema, GetResultSchema, UpdateResultSchema, ForgetResultSchema, MoveResultSchema, RelateResultSchema, RecentResultSchema, MemoryGraphResultSchema, ProjectSummaryResultSchema, SyncResultSchema, WhereIsResultSchema, ConsolidateResultSchema, ProjectIdentityResultSchema, MigrationListResultSchema, MigrationExecuteResultSchema, PolicyResultSchema, } from "./structured-content.js";
|
|
22
|
+
import { RememberResultSchema, RecallResultSchema, ListResultSchema, GetResultSchema, UpdateResultSchema, ForgetResultSchema, MoveResultSchema, RelateResultSchema, RecentResultSchema, MemoryGraphResultSchema, ProjectSummaryResultSchema, SyncResultSchema, WhereIsResultSchema, ConsolidateResultSchema, ProjectIdentityResultSchema, MigrationListResultSchema, MigrationExecuteResultSchema, PolicyResultSchema, DiscoverTagsResultSchema, } from "./structured-content.js";
|
|
23
23
|
// ── CLI Migration Command ─────────────────────────────────────────────────────
|
|
24
24
|
if (process.argv[2] === "migrate") {
|
|
25
25
|
const VAULT_PATH = process.env["VAULT_PATH"]
|
|
@@ -615,6 +615,7 @@ function buildPersistenceStatus(args) {
|
|
|
615
615
|
git: {
|
|
616
616
|
commit: args.commit.status,
|
|
617
617
|
push: args.push.status,
|
|
618
|
+
commitOperation: args.commit.operation,
|
|
618
619
|
commitMessage: args.commitMessage,
|
|
619
620
|
commitBody: args.commitBody,
|
|
620
621
|
commitReason: args.commit.reason,
|
|
@@ -638,6 +639,7 @@ function buildMutationRetryContract(args) {
|
|
|
638
639
|
cwd: args.cwd,
|
|
639
640
|
vault: storageLabel(args.vault),
|
|
640
641
|
error: args.commit.error ?? "Unknown git commit failure",
|
|
642
|
+
operation: args.commit.operation,
|
|
641
643
|
},
|
|
642
644
|
mutationApplied: args.mutationApplied,
|
|
643
645
|
retrySafe: args.mutationApplied,
|
|
@@ -651,9 +653,11 @@ function formatRetrySummary(retry) {
|
|
|
651
653
|
return undefined;
|
|
652
654
|
}
|
|
653
655
|
const safety = retry.retrySafe ? "safe" : "requires review";
|
|
656
|
+
const opLabel = retry.attemptedCommit.operation === "add" ? "add" : "commit";
|
|
657
|
+
const error = retry.attemptedCommit.error;
|
|
654
658
|
return [
|
|
655
659
|
`Retry: ${safety} | vault=${retry.attemptedCommit.vault} | files=${retry.attemptedCommit.files.length}`,
|
|
656
|
-
`Git
|
|
660
|
+
`Git ${opLabel} error: ${error}`,
|
|
657
661
|
].join("\n");
|
|
658
662
|
}
|
|
659
663
|
function formatPersistenceSummary(persistence) {
|
|
@@ -666,7 +670,8 @@ function formatPersistenceSummary(persistence) {
|
|
|
666
670
|
lines[0] += ` | embedding reason=${persistence.embedding.reason}`;
|
|
667
671
|
}
|
|
668
672
|
if (persistence.git.commit === "failed" && persistence.git.commitError) {
|
|
669
|
-
|
|
673
|
+
const opLabel = persistence.git.commitOperation === "add" ? "add" : "commit";
|
|
674
|
+
lines.push(`Git ${opLabel} error: ${persistence.git.commitError}`);
|
|
670
675
|
}
|
|
671
676
|
if (persistence.git.push === "failed" && persistence.git.pushError) {
|
|
672
677
|
lines.push(`Git push error: ${persistence.git.pushError}`);
|
|
@@ -2164,6 +2169,106 @@ server.registerTool("list", {
|
|
|
2164
2169
|
};
|
|
2165
2170
|
return { content: [{ type: "text", text: textContent }], structuredContent };
|
|
2166
2171
|
});
|
|
2172
|
+
// ── discover_tags ───────────────────────────────────────────────────────────
|
|
2173
|
+
server.registerTool("discover_tags", {
|
|
2174
|
+
title: "Discover Tags",
|
|
2175
|
+
description: "Discover existing tags across vaults with usage statistics and examples.\n\n" +
|
|
2176
|
+
"Use this when:\n" +
|
|
2177
|
+
"- Before `remember` to find canonical tag names for consistent terminology\n" +
|
|
2178
|
+
"- Starting a new topic and unsure which tags exist (e.g., 'bug' vs 'bugs')\n" +
|
|
2179
|
+
"- Identifying tags only on temporary notes (cleanup candidates)\n\n" +
|
|
2180
|
+
"Do not use this when:\n" +
|
|
2181
|
+
"- You need to browse notes by tag; use `list` with `tags` filter instead\n" +
|
|
2182
|
+
"- You already know the exact tags you want to use\n\n" +
|
|
2183
|
+
"Returns:\n" +
|
|
2184
|
+
"- Tags sorted by usageCount (canonical tags first)\n" +
|
|
2185
|
+
"- Example note titles (up to 3 per tag) showing usage context\n" +
|
|
2186
|
+
"- lifecycleTypes showing temporary vs permanent distribution\n" +
|
|
2187
|
+
"- isTemporaryOnly flag identifying cleanup candidates\n\n" +
|
|
2188
|
+
"Typical next step:\n" +
|
|
2189
|
+
"- Use canonical tags from discover_tags when appropriate, or create new tags when genuinely novel.\n\n" +
|
|
2190
|
+
"Performance: O(n) where n = total notes scanned. Expect 100-200ms for 500 notes.\n\n" +
|
|
2191
|
+
"Read-only.",
|
|
2192
|
+
annotations: {
|
|
2193
|
+
readOnlyHint: true,
|
|
2194
|
+
idempotentHint: true,
|
|
2195
|
+
openWorldHint: false,
|
|
2196
|
+
},
|
|
2197
|
+
inputSchema: z.object({
|
|
2198
|
+
cwd: projectParam,
|
|
2199
|
+
scope: z
|
|
2200
|
+
.enum(["project", "global", "all"])
|
|
2201
|
+
.optional()
|
|
2202
|
+
.default("all")
|
|
2203
|
+
.describe("'project' = only this project's memories; " +
|
|
2204
|
+
"'global' = only unscoped memories; " +
|
|
2205
|
+
"'all' = everything visible (default)"),
|
|
2206
|
+
storedIn: z
|
|
2207
|
+
.enum(["project-vault", "main-vault", "any"])
|
|
2208
|
+
.optional()
|
|
2209
|
+
.default("any")
|
|
2210
|
+
.describe("Filter by vault storage label like list tool."),
|
|
2211
|
+
}),
|
|
2212
|
+
outputSchema: DiscoverTagsResultSchema,
|
|
2213
|
+
}, async ({ cwd, scope, storedIn }) => {
|
|
2214
|
+
await ensureBranchSynced(cwd);
|
|
2215
|
+
const startTime = Date.now();
|
|
2216
|
+
const { project, entries } = await collectVisibleNotes(cwd, scope, undefined, storedIn);
|
|
2217
|
+
const tagStats = new Map();
|
|
2218
|
+
for (const { note } of entries) {
|
|
2219
|
+
for (const tag of note.tags) {
|
|
2220
|
+
const stats = tagStats.get(tag) || { count: 0, examples: [], lifecycles: new Set() };
|
|
2221
|
+
stats.count++;
|
|
2222
|
+
if (stats.examples.length < 3) {
|
|
2223
|
+
stats.examples.push(note.title);
|
|
2224
|
+
}
|
|
2225
|
+
stats.lifecycles.add(note.lifecycle);
|
|
2226
|
+
tagStats.set(tag, stats);
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
const tags = Array.from(tagStats.entries())
|
|
2230
|
+
.map(([tag, stats]) => ({
|
|
2231
|
+
tag,
|
|
2232
|
+
usageCount: stats.count,
|
|
2233
|
+
examples: stats.examples,
|
|
2234
|
+
lifecycleTypes: Array.from(stats.lifecycles),
|
|
2235
|
+
isTemporaryOnly: stats.lifecycles.size === 1 && stats.lifecycles.has("temporary"),
|
|
2236
|
+
}))
|
|
2237
|
+
.sort((a, b) => b.usageCount - a.usageCount);
|
|
2238
|
+
const durationMs = Date.now() - startTime;
|
|
2239
|
+
const lines = [];
|
|
2240
|
+
if (project && scope !== "global") {
|
|
2241
|
+
lines.push(`Tags for ${project.name} (scope: ${scope}):`);
|
|
2242
|
+
}
|
|
2243
|
+
else {
|
|
2244
|
+
lines.push(`Tags (scope: ${scope}):`);
|
|
2245
|
+
}
|
|
2246
|
+
lines.push("");
|
|
2247
|
+
lines.push(`Total: ${tags.length} unique tags across ${entries.length} notes (${durationMs}ms)`);
|
|
2248
|
+
lines.push("");
|
|
2249
|
+
lines.push("Tags sorted by usage:");
|
|
2250
|
+
for (const t of tags.slice(0, 20)) {
|
|
2251
|
+
const lifecycleMark = t.isTemporaryOnly ? " [temp-only]" : "";
|
|
2252
|
+
lines.push(` ${t.tag} (${t.usageCount})${lifecycleMark}`);
|
|
2253
|
+
if (t.examples.length > 0) {
|
|
2254
|
+
lines.push(` Example: "${t.examples[0]}"`);
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
if (tags.length > 20) {
|
|
2258
|
+
lines.push(` ... and ${tags.length - 20} more`);
|
|
2259
|
+
}
|
|
2260
|
+
const structuredContent = {
|
|
2261
|
+
action: "tags_discovered",
|
|
2262
|
+
project: project ? { id: project.id, name: project.name } : undefined,
|
|
2263
|
+
scope: scope || "all",
|
|
2264
|
+
tags,
|
|
2265
|
+
totalTags: tags.length,
|
|
2266
|
+
totalNotes: entries.length,
|
|
2267
|
+
vaultsSearched: new Set(entries.map(e => storageLabel(e.vault))).size,
|
|
2268
|
+
durationMs,
|
|
2269
|
+
};
|
|
2270
|
+
return { content: [{ type: "text", text: lines.join("\n") }], structuredContent };
|
|
2271
|
+
});
|
|
2167
2272
|
// ── recent_memories ───────────────────────────────────────────────────────────
|
|
2168
2273
|
server.registerTool("recent_memories", {
|
|
2169
2274
|
title: "Recent Memories",
|
|
@@ -3746,6 +3851,10 @@ server.registerPrompt("mnemonic-workflow-hint", {
|
|
|
3746
3851
|
" Use `relate` to connect related memories.\n" +
|
|
3747
3852
|
" Use `consolidate` when several memories overlap.\n" +
|
|
3748
3853
|
" Use `move` when a memory is stored in the wrong place.\n\n" +
|
|
3854
|
+
"### Consistent tag terminology\n\n" +
|
|
3855
|
+
"Before `remember`, call `discover_tags` to find canonical tag names already in use.\n" +
|
|
3856
|
+
"This keeps terminology consistent across sessions (e.g., preferring 'bug' over 'bugs').\n" +
|
|
3857
|
+
"Use high-usage tags as the canonical forms, and avoid tags marked `isTemporaryOnly`.\n\n" +
|
|
3749
3858
|
"### Storage model\n\n" +
|
|
3750
3859
|
"Memories can live in:\n" +
|
|
3751
3860
|
"- `main-vault` for global knowledge\n" +
|