@danielmarbach/mnemonic-mcp 0.14.0 → 0.15.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 +18 -3
- package/build/index.js +28 -5
- package/build/index.js.map +1 -1
- package/build/projections.d.ts +46 -0
- package/build/projections.d.ts.map +1 -0
- package/build/projections.js +202 -0
- package/build/projections.js.map +1 -0
- package/build/storage.d.ts +5 -0
- package/build/storage.d.ts.map +1 -1
- package/build/storage.js +25 -0
- package/build/storage.js.map +1 -1
- package/build/structured-content.d.ts +11 -0
- package/build/structured-content.d.ts.map +1 -1
- package/build/structured-content.js.map +1 -1
- package/build/vault.d.ts.map +1 -1
- package/build/vault.js +9 -6
- package/build/vault.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.15.0] - 2026-03-23
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Projection layer: compact, deterministic derived representations of notes used as embedding input instead of raw title+content. Projections extract title, lifecycle, tags, summary, and headings (h1–h3) into a structured format capped at 1200 characters, improving embedding quality by removing prose noise and markdown formatting.
|
|
12
|
+
- `project_memory_summary` now uses projection summaries for related global note previews when available, falling back to content extraction. Previews are capped at 100 characters for consistent output.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- All embedding operations (`remember`, `update`, `embedMissingNotes`, `consolidate`) now use `embedTextForNote()` which builds projections on demand and falls back to raw title+content if projection fails. Embeddings are never blocked by projection errors.
|
|
17
|
+
- Projections are stored in `vaultPath/projections/` as JSON files alongside embeddings, gitignored and never synced. Each projection includes `noteId`, `title`, `summary`, `headings`, `tags`, `lifecycle`, `updatedAt` (staleness anchor), `projectionText` (what's embedded), and `generatedAt`.
|
|
18
|
+
- Staleness detection is timestamp-based: a projection is stale when `projection.updatedAt !== note.updatedAt`. No hashing required.
|
|
19
|
+
|
|
7
20
|
## [0.14.0] - 2026-03-22
|
|
8
21
|
|
|
9
22
|
### Added
|
package/README.md
CHANGED
|
@@ -259,11 +259,13 @@ Two vault types store notes:
|
|
|
259
259
|
|
|
260
260
|
```
|
|
261
261
|
~/mnemonic-vault/
|
|
262
|
-
.gitignore ← auto-created, gitignores embeddings/
|
|
262
|
+
.gitignore ← auto-created, gitignores embeddings/ and projections/
|
|
263
263
|
notes/
|
|
264
264
|
setup-notes-a1b2c3.md
|
|
265
265
|
embeddings/ ← local only, never committed
|
|
266
266
|
setup-notes-a1b2c3.json
|
|
267
|
+
projections/ ← local only, never committed
|
|
268
|
+
setup-notes-a1b2c3.json
|
|
267
269
|
```
|
|
268
270
|
|
|
269
271
|
**Project vault** — project-specific memories committed into the project repo:
|
|
@@ -271,11 +273,13 @@ Two vault types store notes:
|
|
|
271
273
|
```
|
|
272
274
|
<git-root>/
|
|
273
275
|
.mnemonic/
|
|
274
|
-
.gitignore ← auto-created, gitignores embeddings/
|
|
276
|
+
.gitignore ← auto-created, gitignores embeddings/ and projections/
|
|
275
277
|
notes/
|
|
276
278
|
auth-bug-fix-d4e5f6.md
|
|
277
279
|
embeddings/ ← local only, never committed
|
|
278
280
|
auth-bug-fix-d4e5f6.json
|
|
281
|
+
projections/ ← local only, never committed
|
|
282
|
+
auth-bug-fix-d4e5f6.json
|
|
279
283
|
```
|
|
280
284
|
|
|
281
285
|
### Routing
|
|
@@ -339,10 +343,21 @@ We fixed the JWT expiry issue by switching to RS256 and...
|
|
|
339
343
|
|
|
340
344
|
Content is markdown-linted on `remember`/`update`: fixable issues are auto-corrected before save; non-fixable issues are rejected.
|
|
341
345
|
|
|
342
|
-
### Embeddings and
|
|
346
|
+
### Embeddings and projections
|
|
343
347
|
|
|
344
348
|
Embeddings are generated by Ollama's `/api/embed` with truncation enabled, stored as local JSON alongside notes, and gitignored. `sync` backfills missing embeddings on every run; `sync { force: true }` rebuilds all.
|
|
345
349
|
|
|
350
|
+
**Projections** improve embedding quality by extracting structured representations instead of embedding raw markdown. Each note has a projection stored in `projections/<noteId>.json` (also gitignored) containing:
|
|
351
|
+
|
|
352
|
+
- `projectionText`: compact embedding input (max 1200 chars) with title, lifecycle, tags, summary, and h1–h3 headings
|
|
353
|
+
- `summary`: extracted from the first non-heading paragraph, first bullet list, or first 200 chars of body
|
|
354
|
+
- `headings`: up to 8 deduplicated h1–h3 headings (plain text, in order)
|
|
355
|
+
- `updatedAt`: staleness anchor matching the note's updatedAt timestamp
|
|
356
|
+
|
|
357
|
+
Projections are built lazily on first embed and rebuilt when `note.updatedAt !== projection.updatedAt`. No global rebuild needed — staleness is timestamp-based. If projection generation fails, the system falls back to raw `title + content` so embeds never block.
|
|
358
|
+
|
|
359
|
+
### Migrations
|
|
360
|
+
|
|
346
361
|
Each vault has its own `config.json` with a `schemaVersion`, so main and project vaults migrate independently:
|
|
347
362
|
|
|
348
363
|
- `list_migrations` reports schema version and pending migrations per vault.
|
package/build/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { promises as fs } from "fs";
|
|
|
8
8
|
import { NOTE_LIFECYCLES } from "./storage.js";
|
|
9
9
|
import { embed, cosineSimilarity, embedModel } from "./embeddings.js";
|
|
10
10
|
import { buildTemporalHistoryEntry, computeConfidence, getNoteProvenance } from "./provenance.js";
|
|
11
|
+
import { getOrBuildProjection } from "./projections.js";
|
|
11
12
|
import { filterRelationships, mergeRelationshipsFromNotes, normalizeMergePlanSourceIds, resolveEffectiveConsolidationMode, } from "./consolidate.js";
|
|
12
13
|
import { selectRecallResults } from "./recall.js";
|
|
13
14
|
import { cleanMarkdown } from "./markdown.js";
|
|
@@ -539,6 +540,20 @@ async function wouldRelationshipCleanupTouchProjectVault(noteIds) {
|
|
|
539
540
|
}
|
|
540
541
|
return false;
|
|
541
542
|
}
|
|
543
|
+
/**
|
|
544
|
+
* Get the text to use for embedding a note.
|
|
545
|
+
* Uses the projection's projectionText when available and fresh;
|
|
546
|
+
* falls back to the raw title+content if projection fails.
|
|
547
|
+
*/
|
|
548
|
+
async function embedTextForNote(storage, note) {
|
|
549
|
+
try {
|
|
550
|
+
const projection = await getOrBuildProjection(storage, note);
|
|
551
|
+
return projection.projectionText;
|
|
552
|
+
}
|
|
553
|
+
catch {
|
|
554
|
+
return `${note.title}\n\n${note.content}`;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
542
557
|
async function embedMissingNotes(storage, noteIds, force = false) {
|
|
543
558
|
const notes = noteIds
|
|
544
559
|
? (await Promise.all(noteIds.map((id) => storage.readNote(id)))).filter(Boolean)
|
|
@@ -560,7 +575,8 @@ async function embedMissingNotes(storage, noteIds, force = false) {
|
|
|
560
575
|
}
|
|
561
576
|
}
|
|
562
577
|
try {
|
|
563
|
-
const
|
|
578
|
+
const text = await embedTextForNote(storage, note);
|
|
579
|
+
const vector = await embed(text);
|
|
564
580
|
await storage.writeEmbedding({
|
|
565
581
|
id: note.id,
|
|
566
582
|
model: embedModel,
|
|
@@ -1359,7 +1375,8 @@ server.registerTool("remember", {
|
|
|
1359
1375
|
await vault.storage.writeNote(note);
|
|
1360
1376
|
let embeddingStatus = { status: "written" };
|
|
1361
1377
|
try {
|
|
1362
|
-
const
|
|
1378
|
+
const text = await embedTextForNote(vault.storage, note);
|
|
1379
|
+
const vector = await embed(text);
|
|
1363
1380
|
await vault.storage.writeEmbedding({ id, model: embedModel, embedding: vector, updatedAt: now });
|
|
1364
1381
|
}
|
|
1365
1382
|
catch (err) {
|
|
@@ -1838,7 +1855,8 @@ server.registerTool("update", {
|
|
|
1838
1855
|
await vault.storage.writeNote(updated);
|
|
1839
1856
|
let embeddingStatus = { status: "written" };
|
|
1840
1857
|
try {
|
|
1841
|
-
const
|
|
1858
|
+
const text = await embedTextForNote(vault.storage, updated);
|
|
1859
|
+
const vector = await embed(text);
|
|
1842
1860
|
await vault.storage.writeEmbedding({ id, model: embedModel, embedding: vector, updatedAt: now });
|
|
1843
1861
|
}
|
|
1844
1862
|
catch (err) {
|
|
@@ -2671,11 +2689,15 @@ server.registerTool("project_memory_summary", {
|
|
|
2671
2689
|
maxSim = sim;
|
|
2672
2690
|
}
|
|
2673
2691
|
if (maxSim > 0.4) {
|
|
2692
|
+
const projection = await entry.vault.storage.readProjection(entry.note.id);
|
|
2693
|
+
const preview = projection?.summary
|
|
2694
|
+
? projection.summary.slice(0, 100)
|
|
2695
|
+
: summarizePreview(entry.note.content, 100);
|
|
2674
2696
|
globalCandidates.push({
|
|
2675
2697
|
id: entry.note.id,
|
|
2676
2698
|
title: entry.note.title,
|
|
2677
2699
|
similarity: maxSim,
|
|
2678
|
-
preview
|
|
2700
|
+
preview,
|
|
2679
2701
|
});
|
|
2680
2702
|
}
|
|
2681
2703
|
}
|
|
@@ -3855,7 +3877,8 @@ async function executeMerge(entries, mergePlan, defaultConsolidationMode, projec
|
|
|
3855
3877
|
let embeddingStatus = { status: "written" };
|
|
3856
3878
|
// Generate embedding for consolidated note
|
|
3857
3879
|
try {
|
|
3858
|
-
const
|
|
3880
|
+
const text = await embedTextForNote(targetVault.storage, consolidatedNote);
|
|
3881
|
+
const vector = await embed(text);
|
|
3859
3882
|
await targetVault.storage.writeEmbedding({
|
|
3860
3883
|
id: targetId,
|
|
3861
3884
|
model: embedModel,
|