@pcircle/memesh 4.5.1 → 4.6.1

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.
Files changed (202) hide show
  1. package/.claude-plugin/marketplace.json +5 -3
  2. package/.claude-plugin/plugin.json +6 -4
  3. package/AGENTS.md +116 -0
  4. package/README.de.md +141 -48
  5. package/README.md +173 -48
  6. package/README.zh-TW.md +142 -48
  7. package/dashboard/dist/index.html +15 -14
  8. package/dist/cli/view-live.js +3 -3
  9. package/dist/core/analytics.d.ts +9 -0
  10. package/dist/core/analytics.d.ts.map +1 -1
  11. package/dist/core/analytics.js +36 -18
  12. package/dist/core/analytics.js.map +1 -1
  13. package/dist/core/auto-tagger.d.ts.map +1 -1
  14. package/dist/core/auto-tagger.js +4 -9
  15. package/dist/core/auto-tagger.js.map +1 -1
  16. package/dist/core/briefing.d.ts +8 -0
  17. package/dist/core/briefing.d.ts.map +1 -0
  18. package/dist/core/briefing.js +92 -0
  19. package/dist/core/briefing.js.map +1 -0
  20. package/dist/core/capture-flag.d.ts +5 -0
  21. package/dist/core/capture-flag.d.ts.map +1 -0
  22. package/dist/core/capture-flag.js +10 -0
  23. package/dist/core/capture-flag.js.map +1 -0
  24. package/dist/core/config.d.ts +0 -1
  25. package/dist/core/config.d.ts.map +1 -1
  26. package/dist/core/config.js.map +1 -1
  27. package/dist/core/conflict-candidates.d.ts +20 -0
  28. package/dist/core/conflict-candidates.d.ts.map +1 -0
  29. package/dist/core/conflict-candidates.js +79 -0
  30. package/dist/core/conflict-candidates.js.map +1 -0
  31. package/dist/core/conflict-judge.d.ts +47 -0
  32. package/dist/core/conflict-judge.d.ts.map +1 -0
  33. package/dist/core/conflict-judge.js +189 -0
  34. package/dist/core/conflict-judge.js.map +1 -0
  35. package/dist/core/demo.d.ts.map +1 -1
  36. package/dist/core/demo.js +1 -1
  37. package/dist/core/demo.js.map +1 -1
  38. package/dist/core/digest-validator.d.ts.map +1 -1
  39. package/dist/core/digest-validator.js +3 -5
  40. package/dist/core/digest-validator.js.map +1 -1
  41. package/dist/core/doctor.d.ts +2 -0
  42. package/dist/core/doctor.d.ts.map +1 -1
  43. package/dist/core/doctor.js +59 -62
  44. package/dist/core/doctor.js.map +1 -1
  45. package/dist/core/dreamer.d.ts +5 -2
  46. package/dist/core/dreamer.d.ts.map +1 -1
  47. package/dist/core/dreamer.js +329 -25
  48. package/dist/core/dreamer.js.map +1 -1
  49. package/dist/core/embedder.d.ts +8 -4
  50. package/dist/core/embedder.d.ts.map +1 -1
  51. package/dist/core/embedder.js +82 -24
  52. package/dist/core/embedder.js.map +1 -1
  53. package/dist/core/failure-analyzer.d.ts.map +1 -1
  54. package/dist/core/failure-analyzer.js +7 -12
  55. package/dist/core/failure-analyzer.js.map +1 -1
  56. package/dist/core/graph.d.ts +12 -0
  57. package/dist/core/graph.d.ts.map +1 -1
  58. package/dist/core/graph.js +56 -1
  59. package/dist/core/graph.js.map +1 -1
  60. package/dist/core/guards.d.ts +20 -0
  61. package/dist/core/guards.d.ts.map +1 -0
  62. package/dist/core/guards.js +103 -0
  63. package/dist/core/guards.js.map +1 -0
  64. package/dist/core/install-channel.d.ts +1 -1
  65. package/dist/core/install-channel.d.ts.map +1 -1
  66. package/dist/core/install-channel.js +16 -5
  67. package/dist/core/install-channel.js.map +1 -1
  68. package/dist/core/install-hooks.d.ts +5 -0
  69. package/dist/core/install-hooks.d.ts.map +1 -1
  70. package/dist/core/install-hooks.js +0 -0
  71. package/dist/core/install-hooks.js.map +1 -1
  72. package/dist/core/json-utils.d.ts +1 -0
  73. package/dist/core/json-utils.d.ts.map +1 -1
  74. package/dist/core/json-utils.js +19 -10
  75. package/dist/core/json-utils.js.map +1 -1
  76. package/dist/core/kg-backfill.d.ts +5 -2
  77. package/dist/core/kg-backfill.d.ts.map +1 -1
  78. package/dist/core/kg-backfill.js +155 -5
  79. package/dist/core/kg-backfill.js.map +1 -1
  80. package/dist/core/lifecycle.d.ts.map +1 -1
  81. package/dist/core/lifecycle.js +14 -21
  82. package/dist/core/lifecycle.js.map +1 -1
  83. package/dist/core/memory-tool.d.ts.map +1 -1
  84. package/dist/core/memory-tool.js +4 -4
  85. package/dist/core/memory-tool.js.map +1 -1
  86. package/dist/core/operations.d.ts +13 -2
  87. package/dist/core/operations.d.ts.map +1 -1
  88. package/dist/core/operations.js +115 -28
  89. package/dist/core/operations.js.map +1 -1
  90. package/dist/core/prompt-safety.d.ts +1 -0
  91. package/dist/core/prompt-safety.d.ts.map +1 -1
  92. package/dist/core/prompt-safety.js +7 -0
  93. package/dist/core/prompt-safety.js.map +1 -1
  94. package/dist/core/schema-export.d.ts.map +1 -1
  95. package/dist/core/schema-export.js +31 -0
  96. package/dist/core/schema-export.js.map +1 -1
  97. package/dist/core/serializer.d.ts.map +1 -1
  98. package/dist/core/serializer.js +8 -0
  99. package/dist/core/serializer.js.map +1 -1
  100. package/dist/core/setup.d.ts +29 -0
  101. package/dist/core/setup.d.ts.map +1 -0
  102. package/dist/core/setup.js +127 -0
  103. package/dist/core/setup.js.map +1 -0
  104. package/dist/core/task-state-store.d.ts +17 -0
  105. package/dist/core/task-state-store.d.ts.map +1 -0
  106. package/dist/core/task-state-store.js +45 -0
  107. package/dist/core/task-state-store.js.map +1 -0
  108. package/dist/core/task-state.d.ts +19 -0
  109. package/dist/core/task-state.d.ts.map +1 -0
  110. package/dist/core/task-state.js +91 -0
  111. package/dist/core/task-state.js.map +1 -0
  112. package/dist/core/time-utils.d.ts +2 -0
  113. package/dist/core/time-utils.d.ts.map +1 -0
  114. package/dist/core/time-utils.js +14 -0
  115. package/dist/core/time-utils.js.map +1 -0
  116. package/dist/core/title.d.ts +5 -0
  117. package/dist/core/title.d.ts.map +1 -0
  118. package/dist/core/title.js +14 -0
  119. package/dist/core/title.js.map +1 -0
  120. package/dist/core/transcript-source.d.ts.map +1 -1
  121. package/dist/core/transcript-source.js +2 -3
  122. package/dist/core/transcript-source.js.map +1 -1
  123. package/dist/core/types.d.ts +5 -0
  124. package/dist/core/types.d.ts.map +1 -1
  125. package/dist/core/why.d.ts +54 -0
  126. package/dist/core/why.d.ts.map +1 -0
  127. package/dist/core/why.js +168 -0
  128. package/dist/core/why.js.map +1 -0
  129. package/dist/core/work-topology.d.ts +36 -0
  130. package/dist/core/work-topology.d.ts.map +1 -0
  131. package/dist/core/work-topology.js +192 -0
  132. package/dist/core/work-topology.js.map +1 -0
  133. package/dist/db.d.ts +33 -11
  134. package/dist/db.d.ts.map +1 -1
  135. package/dist/db.js +307 -315
  136. package/dist/db.js.map +1 -1
  137. package/dist/knowledge-graph.d.ts +1 -0
  138. package/dist/knowledge-graph.d.ts.map +1 -1
  139. package/dist/knowledge-graph.js +50 -40
  140. package/dist/knowledge-graph.js.map +1 -1
  141. package/dist/skills-manifest.json +62 -22
  142. package/dist/storage/conflicts.d.ts.map +1 -1
  143. package/dist/storage/conflicts.js +2 -7
  144. package/dist/storage/conflicts.js.map +1 -1
  145. package/dist/storage/fts-index.d.ts +4 -2
  146. package/dist/storage/fts-index.d.ts.map +1 -1
  147. package/dist/storage/fts-index.js +16 -4
  148. package/dist/storage/fts-index.js.map +1 -1
  149. package/dist/storage/schema.d.ts +20 -0
  150. package/dist/storage/schema.d.ts.map +1 -0
  151. package/dist/storage/schema.js +274 -0
  152. package/dist/storage/schema.js.map +1 -0
  153. package/dist/storage/sqlite.d.ts.map +1 -1
  154. package/dist/storage/sqlite.js +1 -1
  155. package/dist/storage/sqlite.js.map +1 -1
  156. package/dist/transports/cli/cli.d.ts +1 -4
  157. package/dist/transports/cli/cli.d.ts.map +1 -1
  158. package/dist/transports/cli/cli.js +579 -66
  159. package/dist/transports/cli/cli.js.map +1 -1
  160. package/dist/transports/http/server.d.ts.map +1 -1
  161. package/dist/transports/http/server.js +242 -303
  162. package/dist/transports/http/server.js.map +1 -1
  163. package/dist/transports/mcp/handlers.d.ts +46 -0
  164. package/dist/transports/mcp/handlers.d.ts.map +1 -1
  165. package/dist/transports/mcp/handlers.js +59 -4
  166. package/dist/transports/mcp/handlers.js.map +1 -1
  167. package/dist/transports/schemas.d.ts +29 -10
  168. package/dist/transports/schemas.d.ts.map +1 -1
  169. package/dist/transports/schemas.js +33 -8
  170. package/dist/transports/schemas.js.map +1 -1
  171. package/hooks/hooks.json +10 -0
  172. package/llms-install.md +138 -0
  173. package/package.json +14 -9
  174. package/scripts/hooks/_generated/capture-flag.js +17 -0
  175. package/scripts/hooks/_generated/fts-index.js +16 -4
  176. package/scripts/hooks/_generated/guards.js +110 -0
  177. package/scripts/hooks/_generated/schema.js +281 -0
  178. package/scripts/hooks/_generated/sqlite.js +1 -1
  179. package/scripts/hooks/_generated/task-state.js +98 -0
  180. package/scripts/hooks/_generated/time-utils.js +21 -0
  181. package/scripts/hooks/_generated/title.js +21 -0
  182. package/scripts/hooks/_generated/work-topology.js +199 -0
  183. package/scripts/hooks/_shared.js +197 -480
  184. package/scripts/hooks/guard-check.js +76 -0
  185. package/scripts/hooks/post-commit.js +31 -1
  186. package/scripts/hooks/pre-compact.js +13 -1
  187. package/scripts/hooks/pre-edit-recall.js +158 -120
  188. package/scripts/hooks/session-start.js +169 -82
  189. package/scripts/hooks/session-summary.js +78 -90
  190. package/skills/memesh/SKILL.md +108 -76
  191. package/README.es.md +0 -467
  192. package/README.fr.md +0 -459
  193. package/README.ja.md +0 -467
  194. package/README.ko.md +0 -467
  195. package/README.pt.md +0 -459
  196. package/README.th.md +0 -460
  197. package/README.vi.md +0 -459
  198. package/README.zh-CN.md +0 -466
  199. package/dist/cli/view.d.ts +0 -3
  200. package/dist/cli/view.d.ts.map +0 -1
  201. package/dist/cli/view.js +0 -523
  202. package/dist/cli/view.js.map +0 -1
@@ -24,12 +24,12 @@ import { createRequire } from 'module';
24
24
  import { basename, join } from 'path';
25
25
  import { existsSync, readFileSync, writeFileSync, mkdirSync, appendFileSync } from 'fs';
26
26
  import { spawn } from 'child_process';
27
- import os from 'os';
28
27
  import { pathToFileURL } from 'url';
29
28
  import {
30
29
  AUTO_CAPTURE_TAG,
31
30
  captureEntity,
32
31
  decideAutoUpdateHook,
32
+ extractCitedMemoryIds,
33
33
  getMemeshDirFromDbPath,
34
34
  getProjectName,
35
35
  importFromPluginRoot,
@@ -41,6 +41,7 @@ import {
41
41
  resolveAutoUpdatePolicy,
42
42
  resolvePluginRoot,
43
43
  spawnAutoUpdate,
44
+ truncateTitle,
44
45
  } from './_shared.js';
45
46
 
46
47
  const require = createRequire(import.meta.url);
@@ -90,9 +91,15 @@ function parseTranscript(transcriptPath) {
90
91
  const errorsEncountered = [];
91
92
  let toolCallCount = 0;
92
93
  let readFailed = false;
94
+ // The raw file content, returned so downstream consumers (the
95
+ // recall-effectiveness block) reuse this single read instead of a second
96
+ // readFileSync — real transcripts reach 47MB, so a second full read plus
97
+ // re-parse doubles the Stop hook's dominant I/O cost.
98
+ let rawText = '';
93
99
 
94
100
  try {
95
- const lines = readFileSync(transcriptPath, 'utf8').split('\n').filter(l => l.trim());
101
+ rawText = readFileSync(transcriptPath, 'utf8');
102
+ const lines = rawText.split('\n').filter(l => l.trim());
96
103
  for (const line of lines) {
97
104
  try {
98
105
  const entry = JSON.parse(line);
@@ -171,7 +178,7 @@ function parseTranscript(transcriptPath) {
171
178
  }
172
179
  }
173
180
 
174
- return { filesEdited: [...filesEdited], bashCommands, errorsEncountered, toolCallCount, readFailed };
181
+ return { filesEdited: [...filesEdited], bashCommands, errorsEncountered, toolCallCount, readFailed, rawText };
175
182
  }
176
183
 
177
184
  // Main: read stdin, extract insights, store in DB
@@ -268,8 +275,9 @@ process.stdin.on('end', async () => {
268
275
  return exit0();
269
276
  }
270
277
 
271
- // Parse transcript
272
- const { filesEdited, bashCommands, errorsEncountered, toolCallCount, readFailed } = parseTranscript(transcriptPath);
278
+ // Parse transcript (single read — rawText is reused by the
279
+ // recall-effectiveness block below)
280
+ const { filesEdited, bashCommands, errorsEncountered, toolCallCount, readFailed, rawText: transcriptRawText } = parseTranscript(transcriptPath);
273
281
 
274
282
  // An unreadable transcript is NOT a quiet session: the capture was
275
283
  // LOST (permissions, I/O), and a heartbeat here would keep doctor green
@@ -354,14 +362,21 @@ process.stdin.on('end', async () => {
354
362
  // entities_fts too. This copy used to insert entity + observations + tags
355
363
  // only, skipping the FTS reindex the sibling hooks did — which left every
356
364
  // session-insight memory unrecallable via the FTS keyword path.
357
- function storeMemory(name, type, observations, tags) {
365
+ function storeMemory(name, type, observations, tags, title) {
358
366
  // null = the entity row could not be resolved = this write did NOT
359
367
  // happen (captureEntity's contract). A run with a failed write must
360
368
  // not stamp the heartbeat below — "alive" would be a lie about the
361
369
  // exact thing the heartbeat certifies.
362
- if (!captureEntity(db, { name, type, observations, tags })) writeFailed = true;
370
+ if (!captureEntity(db, { name, type, observations, tags, title })) writeFailed = true;
363
371
  }
364
372
 
373
+ // No free-form human text exists for these three entities the way a
374
+ // commit subject does — title is synthesized from the same structured
375
+ // counts the observations already report. date+project+verb, per the
376
+ // heuristic the design settled on for hooks with no natural title source.
377
+ const titleDate = new Date().toISOString().slice(0, 10);
378
+ const titlePrefix = `${titleDate} ${projectName}`;
379
+
365
380
  // Rule 1: File editing session summary
366
381
  if (filesEdited.length > 0) {
367
382
  storeMemory(
@@ -371,7 +386,8 @@ process.stdin.on('end', async () => {
371
386
  `Session edited ${filesEdited.length} file(s): ${filesEdited.join(', ')}`,
372
387
  `Total tool calls: ${toolCallCount}`,
373
388
  ],
374
- [...baseTags, ...fileTagsFor(filesEdited)]
389
+ [...baseTags, ...fileTagsFor(filesEdited)],
390
+ truncateTitle(`${titlePrefix}: edited ${filesEdited.length} file(s)`)
375
391
  );
376
392
  }
377
393
 
@@ -384,7 +400,8 @@ process.stdin.on('end', async () => {
384
400
  `Fixed ${errorsEncountered.length} error(s) by editing ${filesEdited.join(', ')}`,
385
401
  ...errorsEncountered.slice(0, 3).map(e => `Error: ${e.slice(0, 100)}`),
386
402
  ],
387
- [...baseTags, 'type:bugfix', ...fileTagsFor(filesEdited)]
403
+ [...baseTags, 'type:bugfix', ...fileTagsFor(filesEdited)],
404
+ truncateTitle(`${titlePrefix}: fixed ${errorsEncountered.length} error(s)`)
388
405
  );
389
406
  }
390
407
 
@@ -397,7 +414,8 @@ process.stdin.on('end', async () => {
397
414
  `Significant session: ${toolCallCount} tool calls, ${filesEdited.length} files edited`,
398
415
  ...bashCommands.slice(0, 3).map(c => `Command: ${c}`),
399
416
  ],
400
- [...baseTags, 'type:heavy-session']
417
+ [...baseTags, 'type:heavy-session'],
418
+ truncateTitle(`${titlePrefix}: significant session (${toolCallCount} tool calls)`)
401
419
  );
402
420
  }
403
421
 
@@ -458,43 +476,58 @@ process.stdin.on('end', async () => {
458
476
  }
459
477
 
460
478
  if (injectedData) {
461
- const { entityIds, entityNames } = injectedData;
479
+ const { entityIds } = injectedData;
462
480
 
463
481
  if (entityIds && entityIds.length > 0) {
464
482
  // Check if recall_hits column exists (v4.0+ migration)
465
483
  const colCheck = db.prepare("PRAGMA table_info(entities)").all();
466
484
  if (colCheck.some(c => c.name === 'recall_hits')) {
467
485
  // Drop the records Claude Code created FROM our own hook
468
- // output before matching. One SessionStart injection lands in
469
- // the transcript 2+ times (hook_success + hook_additional_context),
470
- // so any count-based discount depends on guessing an
471
- // undocumented internal get it wrong and every entity scores
472
- // a hit instead of a miss. Structural removal is copy-count
473
- // and encoding independent.
474
- const sessionText = stripHookEchoes(readFileSync(transcriptPath, 'utf8')).toLowerCase();
475
-
476
- // Hit/miss decision lives in `isRecallHit` (exported, unit-tested).
477
-
486
+ // output before scanning: the injected block itself prints a
487
+ // `[mem:id]` handle on every line, and counting those would
488
+ // score every injection as a hit. Structural removal is
489
+ // copy-count and encoding independent. Reuse the raw text
490
+ // parseTranscript already read a second readFileSync
491
+ // doubles the Stop hook's I/O on 47MB transcripts.
492
+ const sessionText = stripHookEchoes(transcriptRawText);
493
+
494
+ // Citation accounting. A hit is an EXPLICIT `[mem:id]` marker
495
+ // the agent wrote for an id this session injected — the
496
+ // instruction line session-start appends after the fenced
497
+ // block. Literal-content matching (the previous accounting)
498
+ // was retired after measuring 0% signal across ten real
499
+ // sessions and three matching strategies: every injected
500
+ // memory drifted toward an unearned recall_miss, and misses
501
+ // feed the impact factor in core ranking.
502
+ //
503
+ // Markers are self-reported: an agent that used a memory
504
+ // silently earns it nothing, so the signal UNDERCOUNTS and
505
+ // never overcounts. That asymmetry is why misses are FROZEN —
506
+ // recall_misses stays untouched until measured marker
507
+ // compliance (the counters below) justifies reading silence
508
+ // as non-use. The mode stamp keeps the two eras of numbers
509
+ // apart.
510
+ const cited = extractCitedMemoryIds(sessionText);
478
511
  const updateHit = db.prepare(
479
512
  'UPDATE entities SET recall_hits = COALESCE(recall_hits, 0) + 1 WHERE id = ?'
480
513
  );
481
- const updateMiss = db.prepare(
482
- 'UPDATE entities SET recall_misses = COALESCE(recall_misses, 0) + 1 WHERE id = ?'
483
- );
484
-
485
- for (let i = 0; i < entityIds.length; i++) {
486
- const name = (entityNames[i] || '').toLowerCase();
487
- // Skip names that carry no recall signal: too short, or a
488
- // machine identifier (auto-capture entities) that can never
489
- // substring-match prose. Scoring those would be a guaranteed
490
- // unearned miss — see isMeasurableRecallName.
491
- if (!isMeasurableRecallName(name)) continue;
492
- if (isRecallHit(sessionText, name)) {
493
- updateHit.run(entityIds[i]);
494
- } else {
495
- updateMiss.run(entityIds[i]);
496
- }
514
+ for (const id of entityIds) {
515
+ if (cited.has(id)) updateHit.run(id);
497
516
  }
517
+
518
+ // Accounting-mode stamp (constant value, rewritten every
519
+ // session so it survives DB restores from either era) plus
520
+ // the compliance denominators: sessions that HAD an injection
521
+ // vs sessions whose transcript carried any citation marker.
522
+ db.prepare(
523
+ 'INSERT OR REPLACE INTO memesh_metadata (key, value) VALUES (?, ?)'
524
+ ).run('recall_accounting_mode', 'citation-v1 since 2026-08-16');
525
+ const bump = db.prepare(
526
+ `INSERT INTO memesh_metadata (key, value) VALUES (?, '1')
527
+ ON CONFLICT(key) DO UPDATE SET value = CAST(CAST(value AS INTEGER) + 1 AS TEXT)`
528
+ );
529
+ bump.run('citation_sessions_total');
530
+ if (cited.size > 0) bump.run('citation_sessions_cited');
498
531
  }
499
532
  }
500
533
  }
@@ -671,15 +704,15 @@ function dreamHistoryPath() {
671
704
  // is in pure string comparisons, which we don't do here. The trace
672
705
  // below shows the resolved value verbatim so a Windows diagnosis run
673
706
  // can confirm what actually arrived.
674
- const fromEnv = process.env.MEMESH_DIR;
675
- const fromDbPath = !fromEnv ? getMemeshDirFromDbPath() : null;
676
- const fromHome = (!fromEnv && !fromDbPath)
677
- ? join(os.homedir() || (os.userInfo()?.homedir ?? '.'), '.memesh')
678
- : null;
679
- const dir = fromEnv || fromDbPath || fromHome;
707
+ // The helper IS the precedence (MEMESH_DB_PATH > MEMESH_DIR > home).
708
+ // A hand-rolled version here inverted it (MEMESH_DIR won over
709
+ // MEMESH_DB_PATH), so with both set, dream history landed in a different
710
+ // directory than every sibling state file — plus a dead home-fallback
711
+ // branch, since the helper always returns a string.
712
+ const dir = getMemeshDirFromDbPath();
680
713
  dreamTrigTrace('resolve', {
681
- src: fromEnv ? 'env' : (fromDbPath ? 'db-path' : 'home'),
682
- MEMESH_DIR: fromEnv,
714
+ src: process.env.MEMESH_DB_PATH ? 'db-path' : (process.env.MEMESH_DIR ? 'env' : 'home'),
715
+ MEMESH_DIR: process.env.MEMESH_DIR,
683
716
  MEMESH_DB_PATH: process.env.MEMESH_DB_PATH,
684
717
  dir,
685
718
  platform: process.platform,
@@ -807,51 +840,6 @@ export function stripHookEchoes(rawTranscript) {
807
840
  return kept.join('\n');
808
841
  }
809
842
 
810
- /**
811
- * Did the session actually USE the memory named `name`, or does the name only
812
- * appear because memesh injected it at session start?
813
- *
814
- * The caller passes `sessionText` with memesh's own SessionStart injection
815
- * already stripped structurally (see `stripHookEchoes` — matches on
816
- * `attachment.type`, so it is independent of JSON escaping and of how many
817
- * times Claude Code echoes one injection). That removal is what stops an
818
- * injected name from scoring a false hit; once the echo is gone, a plain
819
- * substring match is the whole test.
820
- *
821
- * (This replaced an earlier `transcript.replace(injectedBlob, '')` + match,
822
- * which silently failed on JSON-encoded transcripts and scored every entity a
823
- * hit — see the callsite comment.)
824
- *
825
- * Self-contained for its unit tests: lowercases both sides and ignores names
826
- * shorter than 4 chars (too generic to match reliably).
827
- */
828
- export function isRecallHit(sessionText, name) {
829
- if (!name || name.length < 4) return false;
830
- return String(sessionText ?? '').toLowerCase().includes(String(name).toLowerCase());
831
- }
832
-
833
- /**
834
- * Whether an injected entity's NAME can serve as a recall-effectiveness signal.
835
- *
836
- * Recall-effectiveness decides "was this injected memory used?" by substring-
837
- * matching the entity NAME in the session transcript (isRecallHit). That only
838
- * works for names a human might type. Auto-capture entities are named with
839
- * machine identifiers — `session-<pid>-<ts>-files`, `commit-<hash>`,
840
- * `pre-compact-<id>` — which never appear verbatim in conversation prose, so
841
- * they take a `recall_miss` they didn't earn on every injection. Over repeated
842
- * sessions that drags their Laplace-smoothed impact factor (scoring.ts, 10%
843
- * weight) down and quietly suppresses auto-captured memories from future recall.
844
- *
845
- * We can't measure their usefulness by name, so we don't count them either way —
846
- * they keep the neutral 0.5 impact. The prefix set is coupled to the auto-capture
847
- * producers' `<kind>-<id>` naming (post-commit / session-summary / pre-compact);
848
- * a new auto-capture producer should add its prefix here.
849
- */
850
- export function isMeasurableRecallName(name) {
851
- if (!name || name.length < 4) return false;
852
- return !/^(session-|commit-|pre-compact-)/i.test(name);
853
- }
854
-
855
843
  export function maybeTriggerDream(projectName, config, pluginRoot) {
856
844
  dreamTrigTrace('enter', { projectName, hasLlm: Boolean(config?.llm) });
857
845
  if (!projectName || projectName === 'unknown') {
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: memesh
3
- description: Use MeMesh to remember, recall, and manage AI knowledge across sessions. Triggers when the user asks to remember something, recall past decisions, forget outdated info, learn from mistakes, or analyze work patterns. Also triggers proactively when you make important decisions, fix bugs, or learn lessons worth preserving.
3
+ description: Use MeMesh to remember, recall, and manage AI knowledge across sessions. Triggers when the user asks to remember something, recall past decisions, forget outdated info, learn from mistakes, or analyze work patterns. Also triggers when the user asks "what do you remember", "where did we leave off", or wants to catch up on a project; when a session starts and project context is needed; and proactively when you make important decisions, fix bugs, or learn lessons worth preserving.
4
4
  user-invocable: true
5
5
  ---
6
6
 
7
7
  # MeMesh — AI Memory Management
8
8
 
9
- Persistent memory layer for AI agents. Remember decisions, recall context, learn from mistakesacross sessions.
9
+ Persistent memory for AI agents. The point is continuity: the next session starts where this one stopped, instead of re-spending thousands of tokens re-discovering project state and the human never has to re-explain it.
10
10
 
11
11
  ## How to Access (auto-detect)
12
12
 
@@ -23,39 +23,85 @@ Persistent memory layer for AI agents. Remember decisions, recall context, learn
23
23
 
24
24
  All examples below use CLI. MCP tools accept the same parameters as JSON objects.
25
25
 
26
+ ## The Loop
27
+
28
+ Four moments. Everything else in this file is detail.
29
+
30
+ **SESSION START → load the briefing (once).**
31
+ Call the `briefing` MCP tool or run `memesh briefing`. It returns the assembled
32
+ work topology: where the work was left off (goal / next / blocked / done),
33
+ decisions and direction, lessons not to repeat, what is known, recent activity.
34
+ One call is cheaper than re-exploring the repo to reconstruct the same picture.
35
+ Exception: under Claude Code the session-start hook has ALREADY injected this
36
+ exact block — do not call it again (see "What's Already Automatic").
37
+
38
+ **USER STATES a goal, next step, or blocker → record it immediately.**
39
+ ```bash
40
+ memesh task --goal "Ship the work-topology injection" --next "Open the PR once CI is green"
41
+ memesh task --blocked "Waiting on the Windows runner"
42
+ memesh task --blocked "" # blocker resolved — empty string clears the field
43
+ ```
44
+ Fields: `--goal` `--next` `--blocked` `--done` (MCP tool: `task_state`).
45
+ Record ONLY what the user actually said. This state is injected at the top of
46
+ the next session and read as fact — a goal you guessed from which files were
47
+ edited reaches that session with nothing to correct it. If it was not said,
48
+ leave the field out.
49
+
50
+ **SESSION END or milestone → make the task state match reality.**
51
+ `memesh task` (no flags) shows exactly what the next session will be told.
52
+ If "next" is now done, record what is actually next; if the blocker cleared,
53
+ clear it.
54
+
55
+ **USER ASKS "what do you remember / where were we" → briefing, then relay.**
56
+ Run `memesh briefing` (or `--project <name>`) and answer from it. For specific
57
+ follow-up questions, use `recall`.
58
+
59
+ **MEMESH UNAVAILABLE or RECALL EMPTY → say so, never invent.** Report that
60
+ memory is unavailable (or found nothing) and continue without it. Never
61
+ fabricate a memory or cite a `[mem:id]` that was not actually returned.
62
+ Recall is bounded by `limit` — a small hit count is not a graph-wide count,
63
+ and an empty result is not proof nothing was stored: vary the wording or
64
+ narrow by tag before concluding. Every recall answer includes a `retrieval`
65
+ block — `truncated: true` means the window filled (more may exist);
66
+ `degraded: true` means semantic search could not run and these are
67
+ keyword-only results right now (`memesh doctor` explains why).
68
+
26
69
  ## What's Already Automatic (Claude Code Plugin Hooks)
27
70
 
28
71
  If MeMesh is installed as a Claude Code plugin, these happen **without any action from you**:
29
72
 
30
73
  | Hook | When | What it does |
31
74
  |------|------|-------------|
32
- | **SessionStart** | Every session begins | Auto-recalls top memories for current project + surfaces lesson warnings |
33
- | **PreToolUse (Edit)** | Before editing files | Injects memories related to the file or project |
34
- | **UserPromptSubmit** | When you submit a prompt | Detects "remember this" intent (5 languages: en, es, fr, pt, zh-TW) and reminds Claude to use memesh |
35
- | **PostToolUse (Commit)** | After `git commit` | Auto-tracks commit with diff stats as a memory entity |
75
+ | **SessionStart** | Every session begins | Injects the briefing: task state → lessons → project memories recent activity |
76
+ | **PreToolUse (Edit/Write)** | Before editing files | Injects memories related to the file or project |
77
+ | **UserPromptSubmit** | When you submit a prompt | Detects "remember this" intent (5 languages) and reminds Claude to use memesh |
78
+ | **PostToolUse (Bash)** | After `git commit` | Auto-tracks the commit with diff stats as a memory entity |
36
79
  | **Stop** | Session ends | Auto-captures session knowledge + runs LLM failure analysis → lessons |
37
- | **PreCompact** | Before context compaction | Saves important knowledge before conversation history is compressed |
38
-
39
- **You do NOT need to manually:**
40
- - Recall at session start (SessionStart hook does it)
41
- - Remember commits (PostToolUse hook does it)
42
- - Summarize sessions (Stop hook does it)
43
- - Remember when you say "記下來" / "remember this" (UserPromptSubmit hook reminds Claude)
80
+ | **PreCompact** | Before context compaction | Saves important knowledge before history is compressed |
81
+ | **PreToolUse (Bash)** | Before a command runs | Fires accepted lesson-guards — warns when a recorded mistake is about to repeat |
44
82
 
45
- **You DO need to manually** use the commands below for intentional knowledge management.
83
+ Because of the SessionStart hook: **in Claude Code, do NOT call `briefing` at
84
+ session start — it is already in your context.** Call it only mid-session
85
+ (context was compacted, or the user asks what you remember) or on hosts
86
+ without these hooks (other MCP clients, shell-only agents). Double-injection
87
+ spends the very tokens this system exists to save.
46
88
 
47
- ## When to Use
89
+ Hooks capture what *happened*. You still act manually for what they cannot
90
+ know: what the user **meant** (task state), deliberate decisions and lessons,
91
+ and retiring outdated info.
48
92
 
49
- ### Proactive triggers — do these WITHOUT being asked
93
+ ## Proactive triggers — do these WITHOUT being asked
50
94
 
51
95
  | Situation | Action |
52
96
  |-----------|--------|
97
+ | User states what they're working on / what's next / what's blocking | `memesh task --goal "…"` / `--next "…"` / `--blocked "…"` |
53
98
  | Design decision made | `memesh remember --name "auth-choice" --type decision --obs "Use OAuth 2.0 with PKCE" --tags "project:myapp"` |
54
99
  | Bug fixed | `memesh learn --error "what broke" --fix "what fixed it" --root-cause "why" --severity major` |
55
- | Pattern established | `memesh remember --name "validation-pattern" --type pattern --obs "Always use Zod"` |
56
100
  | Starting work on a feature | `memesh recall "feature-name" --json` |
57
101
  | User asks "what did we decide?" | `memesh recall "topic" --tag "project:myapp"` |
58
- | Info is outdated | `memesh forget --name "old-decision"` |
102
+ | User asks "where did we leave off?" | `memesh briefing` relay it |
103
+ | Info is outdated | New memory with `--supersedes "old-name"`, or `memesh forget` |
104
+ | Context about the user's work habits needed | `user_patterns` MCP tool (MCP/HTTP only — no CLI command) |
59
105
 
60
106
  ### When NOT to remember
61
107
  - Trivial implementation details (variable names, import paths)
@@ -69,83 +115,69 @@ If MeMesh is installed as a Claude Code plugin, these happen **without any actio
69
115
  memesh learn \
70
116
  --error "SIGSEGV when running vitest with threads" \
71
117
  --fix "Use pool: 'forks' instead of 'threads' for native modules" \
72
- --root-cause "better-sqlite3 native module is not thread-safe" \
73
- --prevention "Check if test framework supports native modules before choosing pool" \
118
+ --root-cause "the native module is not thread-safe" \
119
+ --prevention "Check if the test framework supports native modules before choosing pool" \
74
120
  --severity major
75
121
  ```
76
- This creates a `lesson_learned` entity. Lessons are surfaced as **proactive warnings** at next session start.
77
-
78
- ### You need context before working
79
- ```bash
80
- memesh recall "authentication" --json
81
- memesh recall --tag "project:myapp" --limit 10
82
- memesh recall --cross-project # search across all projects
83
- ```
84
- Results are ranked by relevance, recency, frequency, confidence, and recall impact.
122
+ Creates a `lesson_learned` entity. Lessons are surfaced as **proactive warnings** at the next session start.
85
123
 
86
124
  ### A decision was just made
87
125
  ```bash
88
126
  memesh remember \
89
- --name "db-choice-2026" \
90
- --type decision \
127
+ --name "db-choice" --type decision \
128
+ --title "SQLite for local-first storage" \
91
129
  --obs "Use SQLite for local-first" "Rejected PostgreSQL due to deployment complexity" \
92
130
  --tags "project:myapp" "topic:database"
93
131
  ```
132
+ Use a **stable name** (`db-choice`, not `db-choice-2026-08-16`): reusing the
133
+ name appends to the same entity instead of scattering duplicates. `--title` is
134
+ the human-readable headline; the name stays the machine key. If this replaces
135
+ an older decision, add `--supersedes "old-db-choice"`.
94
136
  Types: `decision` `pattern` `lesson_learned` `bug_fix` `architecture` `convention` `feature` `best_practice` `concept` `tool` `note`
95
137
 
96
- ### Old info needs updating
138
+ ### You need context on a specific topic
97
139
  ```bash
98
- memesh forget --name "old-auth-approach" # archive entire entity
99
- memesh forget --name "auth-approach" --observation "Use JWT" # remove one fact only
140
+ memesh recall "authentication" --json
141
+ memesh recall --tag "project:myapp" --limit 10
142
+ memesh recall --cross-project # search across all projects
100
143
  ```
101
- Archives (soft-delete). Never permanently removes.
144
+ Query words are OR-ed and ranked by relevance — a naturally phrased question
145
+ works; extra words narrow the ranking, not the result set.
102
146
 
103
- ### Memories are getting verbose
147
+ ### Old info needs updating
104
148
  ```bash
105
- memesh dream run --project myapp # propose digests for clusters of noisy memories
106
- memesh dream run --from-transcripts # OR: mine this project's Claude Code sessions for memory
107
- memesh dream list # review what it proposed
108
- memesh dream show <id> # inspect one proposal in full before accepting
109
- memesh dream accept <id> # apply one, or: memesh dream reject <id>
149
+ memesh forget --name "auth-approach" --observation "Use JWT" # remove one fact only
150
+ memesh forget --name "old-auth-approach" # archive the whole entity
110
151
  ```
111
- Nothing changes until a proposal is accepted, and sources are archived rather
112
- than deleted. Requires Smart Mode configured. Works on episodic memories
113
- (commits, session notes) — lessons, decisions, architecture notes and pinned
114
- entities are never touched.
152
+ Both are soft (recoverable) nothing is permanently removed.
115
153
 
116
- `memesh consolidate` was retired: it rewrote a memory with an LLM summary and
117
- deleted the originals on the spot, with no review step.
154
+ ### Memories are getting verbose or stale
155
+ Use the **memesh-review** skill: it analyzes health, finds stale, conflicting
156
+ and redundant memories, and proposes cleanup (including `memesh dream`, the
157
+ reviewed digest pipeline). Do not hand-compress memories yourself.
118
158
 
119
- ### Backup or share memories
159
+ ### Backup, share, health
120
160
  ```bash
121
161
  memesh export --tag "project:myapp" > memories.json
122
- memesh import memories.json --merge skip # skip | overwrite | append
123
- ```
124
-
125
- ### Check MeMesh health
126
- ```bash
127
- memesh status # version, search level, embeddings
128
- memesh config list # current configuration
162
+ memesh import memories.json --merge skip # skip | overwrite | append
163
+ memesh status # version, search level, embeddings
164
+ memesh reindex # rebuild embeddings after provider change
129
165
  ```
130
166
 
131
- ### Regenerate embeddings after provider change
132
- ```bash
133
- memesh reindex # rebuild all embeddings
134
- memesh reindex --namespace personal # reindex only one namespace
135
- memesh reindex --json # structured progress output
136
- ```
137
- Use this when you change embedding provider (e.g., Ollama OpenAI) or dimension. The database auto-drops old embeddings on provider change, but you need to run `reindex` to regenerate them for existing memories.
138
-
139
- ## MCP-Only Features
140
-
141
- These require MCP tools or the HTTP API (`memesh serve` + REST calls):
142
-
143
- - **user_patterns** Analyzes work patterns (schedule, tool preferences, strengths) from existing memories. Categories: `workSchedule`, `toolPreferences`, `strengths`, `focusAreas`.
144
-
145
- ## Best Practices
146
-
147
- 1. **Be specific** — "Use OAuth 2.0 with PKCE" not "auth stuff decided"
148
- 2. **Tag by project** — Always include `project:<name>` tag
149
- 3. **Use `--json`** — When you need to parse output programmatically
150
- 4. **Learn from every bug** — Every fix is a future warning. Use `learn`, not just `remember`.
151
- 5. **Don't over-remember** — Decisions that took > 5 minutes. Patterns worth preserving. Not trivia.
167
+ ## Memory hygiene
168
+
169
+ 1. **Stable names append.** Remembering under an existing name adds
170
+ observations and dedupes tags it never replaces the entity. Reuse the
171
+ name to grow one memory; do not mint `-v2` / dated variants of it.
172
+ 2. **`supersedes` retires the loser.** When a new memory replaces an old one,
173
+ record it with `--supersedes <old-name>` (MCP: a relation of type
174
+ `supersedes`). The old entity is archived — recoverable, out of recall.
175
+ 3. **`contradicts` flags real conflicts.** When two memories cannot both be
176
+ true and neither is clearly wrong yet, link them with `--contradicts`
177
+ (MCP: relation type `contradicts`). Both surface as a conflict on every
178
+ recall until someone resolves it.
179
+ 4. **Prefer observation-level forgetting.** `forget --observation "…"` removes
180
+ one wrong fact and keeps the entity. Plain `forget` archives the whole
181
+ entity out of visibility — use it only when everything in it is dead.
182
+ 5. **Tag by project** (`project:<name>`) and **be specific** — "Use OAuth 2.0
183
+ with PKCE", not "auth stuff decided".