@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.
- package/.claude-plugin/marketplace.json +5 -3
- package/.claude-plugin/plugin.json +6 -4
- package/AGENTS.md +116 -0
- package/README.de.md +141 -48
- package/README.md +173 -48
- package/README.zh-TW.md +142 -48
- package/dashboard/dist/index.html +15 -14
- package/dist/cli/view-live.js +3 -3
- package/dist/core/analytics.d.ts +9 -0
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/core/analytics.js +36 -18
- package/dist/core/analytics.js.map +1 -1
- package/dist/core/auto-tagger.d.ts.map +1 -1
- package/dist/core/auto-tagger.js +4 -9
- package/dist/core/auto-tagger.js.map +1 -1
- package/dist/core/briefing.d.ts +8 -0
- package/dist/core/briefing.d.ts.map +1 -0
- package/dist/core/briefing.js +92 -0
- package/dist/core/briefing.js.map +1 -0
- package/dist/core/capture-flag.d.ts +5 -0
- package/dist/core/capture-flag.d.ts.map +1 -0
- package/dist/core/capture-flag.js +10 -0
- package/dist/core/capture-flag.js.map +1 -0
- package/dist/core/config.d.ts +0 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js.map +1 -1
- package/dist/core/conflict-candidates.d.ts +20 -0
- package/dist/core/conflict-candidates.d.ts.map +1 -0
- package/dist/core/conflict-candidates.js +79 -0
- package/dist/core/conflict-candidates.js.map +1 -0
- package/dist/core/conflict-judge.d.ts +47 -0
- package/dist/core/conflict-judge.d.ts.map +1 -0
- package/dist/core/conflict-judge.js +189 -0
- package/dist/core/conflict-judge.js.map +1 -0
- package/dist/core/demo.d.ts.map +1 -1
- package/dist/core/demo.js +1 -1
- package/dist/core/demo.js.map +1 -1
- package/dist/core/digest-validator.d.ts.map +1 -1
- package/dist/core/digest-validator.js +3 -5
- package/dist/core/digest-validator.js.map +1 -1
- package/dist/core/doctor.d.ts +2 -0
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +59 -62
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts +5 -2
- package/dist/core/dreamer.d.ts.map +1 -1
- package/dist/core/dreamer.js +329 -25
- package/dist/core/dreamer.js.map +1 -1
- package/dist/core/embedder.d.ts +8 -4
- package/dist/core/embedder.d.ts.map +1 -1
- package/dist/core/embedder.js +82 -24
- package/dist/core/embedder.js.map +1 -1
- package/dist/core/failure-analyzer.d.ts.map +1 -1
- package/dist/core/failure-analyzer.js +7 -12
- package/dist/core/failure-analyzer.js.map +1 -1
- package/dist/core/graph.d.ts +12 -0
- package/dist/core/graph.d.ts.map +1 -1
- package/dist/core/graph.js +56 -1
- package/dist/core/graph.js.map +1 -1
- package/dist/core/guards.d.ts +20 -0
- package/dist/core/guards.d.ts.map +1 -0
- package/dist/core/guards.js +103 -0
- package/dist/core/guards.js.map +1 -0
- package/dist/core/install-channel.d.ts +1 -1
- package/dist/core/install-channel.d.ts.map +1 -1
- package/dist/core/install-channel.js +16 -5
- package/dist/core/install-channel.js.map +1 -1
- package/dist/core/install-hooks.d.ts +5 -0
- package/dist/core/install-hooks.d.ts.map +1 -1
- package/dist/core/install-hooks.js +0 -0
- package/dist/core/install-hooks.js.map +1 -1
- package/dist/core/json-utils.d.ts +1 -0
- package/dist/core/json-utils.d.ts.map +1 -1
- package/dist/core/json-utils.js +19 -10
- package/dist/core/json-utils.js.map +1 -1
- package/dist/core/kg-backfill.d.ts +5 -2
- package/dist/core/kg-backfill.d.ts.map +1 -1
- package/dist/core/kg-backfill.js +155 -5
- package/dist/core/kg-backfill.js.map +1 -1
- package/dist/core/lifecycle.d.ts.map +1 -1
- package/dist/core/lifecycle.js +14 -21
- package/dist/core/lifecycle.js.map +1 -1
- package/dist/core/memory-tool.d.ts.map +1 -1
- package/dist/core/memory-tool.js +4 -4
- package/dist/core/memory-tool.js.map +1 -1
- package/dist/core/operations.d.ts +13 -2
- package/dist/core/operations.d.ts.map +1 -1
- package/dist/core/operations.js +115 -28
- package/dist/core/operations.js.map +1 -1
- package/dist/core/prompt-safety.d.ts +1 -0
- package/dist/core/prompt-safety.d.ts.map +1 -1
- package/dist/core/prompt-safety.js +7 -0
- package/dist/core/prompt-safety.js.map +1 -1
- package/dist/core/schema-export.d.ts.map +1 -1
- package/dist/core/schema-export.js +31 -0
- package/dist/core/schema-export.js.map +1 -1
- package/dist/core/serializer.d.ts.map +1 -1
- package/dist/core/serializer.js +8 -0
- package/dist/core/serializer.js.map +1 -1
- package/dist/core/setup.d.ts +29 -0
- package/dist/core/setup.d.ts.map +1 -0
- package/dist/core/setup.js +127 -0
- package/dist/core/setup.js.map +1 -0
- package/dist/core/task-state-store.d.ts +17 -0
- package/dist/core/task-state-store.d.ts.map +1 -0
- package/dist/core/task-state-store.js +45 -0
- package/dist/core/task-state-store.js.map +1 -0
- package/dist/core/task-state.d.ts +19 -0
- package/dist/core/task-state.d.ts.map +1 -0
- package/dist/core/task-state.js +91 -0
- package/dist/core/task-state.js.map +1 -0
- package/dist/core/time-utils.d.ts +2 -0
- package/dist/core/time-utils.d.ts.map +1 -0
- package/dist/core/time-utils.js +14 -0
- package/dist/core/time-utils.js.map +1 -0
- package/dist/core/title.d.ts +5 -0
- package/dist/core/title.d.ts.map +1 -0
- package/dist/core/title.js +14 -0
- package/dist/core/title.js.map +1 -0
- package/dist/core/transcript-source.d.ts.map +1 -1
- package/dist/core/transcript-source.js +2 -3
- package/dist/core/transcript-source.js.map +1 -1
- package/dist/core/types.d.ts +5 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/why.d.ts +54 -0
- package/dist/core/why.d.ts.map +1 -0
- package/dist/core/why.js +168 -0
- package/dist/core/why.js.map +1 -0
- package/dist/core/work-topology.d.ts +36 -0
- package/dist/core/work-topology.d.ts.map +1 -0
- package/dist/core/work-topology.js +192 -0
- package/dist/core/work-topology.js.map +1 -0
- package/dist/db.d.ts +33 -11
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +307 -315
- package/dist/db.js.map +1 -1
- package/dist/knowledge-graph.d.ts +1 -0
- package/dist/knowledge-graph.d.ts.map +1 -1
- package/dist/knowledge-graph.js +50 -40
- package/dist/knowledge-graph.js.map +1 -1
- package/dist/skills-manifest.json +62 -22
- package/dist/storage/conflicts.d.ts.map +1 -1
- package/dist/storage/conflicts.js +2 -7
- package/dist/storage/conflicts.js.map +1 -1
- package/dist/storage/fts-index.d.ts +4 -2
- package/dist/storage/fts-index.d.ts.map +1 -1
- package/dist/storage/fts-index.js +16 -4
- package/dist/storage/fts-index.js.map +1 -1
- package/dist/storage/schema.d.ts +20 -0
- package/dist/storage/schema.d.ts.map +1 -0
- package/dist/storage/schema.js +274 -0
- package/dist/storage/schema.js.map +1 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +1 -1
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/transports/cli/cli.d.ts +1 -4
- package/dist/transports/cli/cli.d.ts.map +1 -1
- package/dist/transports/cli/cli.js +579 -66
- package/dist/transports/cli/cli.js.map +1 -1
- package/dist/transports/http/server.d.ts.map +1 -1
- package/dist/transports/http/server.js +242 -303
- package/dist/transports/http/server.js.map +1 -1
- package/dist/transports/mcp/handlers.d.ts +46 -0
- package/dist/transports/mcp/handlers.d.ts.map +1 -1
- package/dist/transports/mcp/handlers.js +59 -4
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/dist/transports/schemas.d.ts +29 -10
- package/dist/transports/schemas.d.ts.map +1 -1
- package/dist/transports/schemas.js +33 -8
- package/dist/transports/schemas.js.map +1 -1
- package/hooks/hooks.json +10 -0
- package/llms-install.md +138 -0
- package/package.json +14 -9
- package/scripts/hooks/_generated/capture-flag.js +17 -0
- package/scripts/hooks/_generated/fts-index.js +16 -4
- package/scripts/hooks/_generated/guards.js +110 -0
- package/scripts/hooks/_generated/schema.js +281 -0
- package/scripts/hooks/_generated/sqlite.js +1 -1
- package/scripts/hooks/_generated/task-state.js +98 -0
- package/scripts/hooks/_generated/time-utils.js +21 -0
- package/scripts/hooks/_generated/title.js +21 -0
- package/scripts/hooks/_generated/work-topology.js +199 -0
- package/scripts/hooks/_shared.js +197 -480
- package/scripts/hooks/guard-check.js +76 -0
- package/scripts/hooks/post-commit.js +31 -1
- package/scripts/hooks/pre-compact.js +13 -1
- package/scripts/hooks/pre-edit-recall.js +158 -120
- package/scripts/hooks/session-start.js +169 -82
- package/scripts/hooks/session-summary.js +78 -90
- package/skills/memesh/SKILL.md +108 -76
- package/README.es.md +0 -467
- package/README.fr.md +0 -459
- package/README.ja.md +0 -467
- package/README.ko.md +0 -467
- package/README.pt.md +0 -459
- package/README.th.md +0 -460
- package/README.vi.md +0 -459
- package/README.zh-CN.md +0 -466
- package/dist/cli/view.d.ts +0 -3
- package/dist/cli/view.d.ts.map +0 -1
- package/dist/cli/view.js +0 -523
- 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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
469
|
-
//
|
|
470
|
-
//
|
|
471
|
-
//
|
|
472
|
-
//
|
|
473
|
-
//
|
|
474
|
-
const sessionText = stripHookEchoes(
|
|
475
|
-
|
|
476
|
-
//
|
|
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
|
|
482
|
-
|
|
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
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
const dir =
|
|
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:
|
|
682
|
-
MEMESH_DIR:
|
|
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') {
|
package/skills/memesh/SKILL.md
CHANGED
|
@@ -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
|
|
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 |
|
|
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
|
|
35
|
-
| **PostToolUse (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
|
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 "
|
|
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
|
-
|
|
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
|
|
90
|
-
--
|
|
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
|
-
###
|
|
138
|
+
### You need context on a specific topic
|
|
97
139
|
```bash
|
|
98
|
-
memesh
|
|
99
|
-
memesh
|
|
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
|
-
|
|
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
|
-
###
|
|
147
|
+
### Old info needs updating
|
|
104
148
|
```bash
|
|
105
|
-
memesh
|
|
106
|
-
memesh
|
|
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
|
-
|
|
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
|
-
|
|
117
|
-
|
|
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
|
|
159
|
+
### Backup, share, health
|
|
120
160
|
```bash
|
|
121
161
|
memesh export --tag "project:myapp" > memories.json
|
|
122
|
-
memesh import memories.json --merge skip
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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".
|