@pcircle/memesh 4.6.0 → 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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +21 -0
- package/README.de.md +12 -13
- package/README.md +12 -14
- package/README.zh-TW.md +12 -13
- package/dashboard/dist/index.html +14 -13
- 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/briefing.d.ts.map +1 -1
- package/dist/core/briefing.js +1 -0
- package/dist/core/briefing.js.map +1 -1
- 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/demo.d.ts.map +1 -1
- package/dist/core/demo.js +1 -1
- package/dist/core/demo.js.map +1 -1
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +25 -6
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts +2 -2
- package/dist/core/dreamer.d.ts.map +1 -1
- package/dist/core/dreamer.js +221 -0
- package/dist/core/dreamer.js.map +1 -1
- package/dist/core/embedder.d.ts +4 -1
- package/dist/core/embedder.d.ts.map +1 -1
- package/dist/core/embedder.js +67 -17
- package/dist/core/embedder.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/kg-backfill.d.ts +5 -1
- package/dist/core/kg-backfill.d.ts.map +1 -1
- package/dist/core/kg-backfill.js +155 -2
- package/dist/core/kg-backfill.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 +93 -15
- package/dist/core/operations.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/types.d.ts +1 -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 +3 -0
- package/dist/core/work-topology.d.ts.map +1 -1
- package/dist/core/work-topology.js +11 -2
- package/dist/core/work-topology.js.map +1 -1
- package/dist/db.d.ts +31 -4
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +163 -31
- package/dist/db.js.map +1 -1
- package/dist/skills-manifest.json +29 -19
- 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.js +198 -61
- 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 +43 -5
- package/dist/transports/http/server.js.map +1 -1
- package/dist/transports/mcp/handlers.d.ts.map +1 -1
- package/dist/transports/mcp/handlers.js +2 -2
- package/dist/transports/mcp/handlers.js.map +1 -1
- package/dist/transports/schemas.d.ts +8 -0
- package/dist/transports/schemas.d.ts.map +1 -1
- package/dist/transports/schemas.js +7 -0
- package/dist/transports/schemas.js.map +1 -1
- package/hooks/hooks.json +10 -0
- package/package.json +1 -1
- package/scripts/hooks/_generated/guards.js +110 -0
- package/scripts/hooks/_generated/sqlite.js +1 -1
- package/scripts/hooks/_generated/work-topology.js +11 -2
- package/scripts/hooks/_shared.js +77 -4
- package/scripts/hooks/guard-check.js +76 -0
- package/scripts/hooks/post-commit.js +27 -0
- package/scripts/hooks/pre-edit-recall.js +157 -121
- package/scripts/hooks/session-start.js +34 -23
- package/scripts/hooks/session-summary.js +43 -90
- package/skills/memesh/SKILL.md +11 -0
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
AUTO_CAPTURE_TAG,
|
|
30
30
|
captureEntity,
|
|
31
31
|
decideAutoUpdateHook,
|
|
32
|
+
extractCitedMemoryIds,
|
|
32
33
|
getMemeshDirFromDbPath,
|
|
33
34
|
getProjectName,
|
|
34
35
|
importFromPluginRoot,
|
|
@@ -475,56 +476,58 @@ process.stdin.on('end', async () => {
|
|
|
475
476
|
}
|
|
476
477
|
|
|
477
478
|
if (injectedData) {
|
|
478
|
-
const { entityIds
|
|
479
|
+
const { entityIds } = injectedData;
|
|
479
480
|
|
|
480
481
|
if (entityIds && entityIds.length > 0) {
|
|
481
482
|
// Check if recall_hits column exists (v4.0+ migration)
|
|
482
483
|
const colCheck = db.prepare("PRAGMA table_info(entities)").all();
|
|
483
484
|
if (colCheck.some(c => c.name === 'recall_hits')) {
|
|
484
485
|
// Drop the records Claude Code created FROM our own hook
|
|
485
|
-
// output before
|
|
486
|
-
//
|
|
487
|
-
//
|
|
488
|
-
//
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
//
|
|
496
|
-
|
|
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);
|
|
497
511
|
const updateHit = db.prepare(
|
|
498
512
|
'UPDATE entities SET recall_hits = COALESCE(recall_hits, 0) + 1 WHERE id = ?'
|
|
499
513
|
);
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
);
|
|
503
|
-
|
|
504
|
-
// The injected block shows an entity's TITLE, not its name
|
|
505
|
-
// (A1 — a machine key like `commit-a1b2c3d` cost tokens and
|
|
506
|
-
// taught the model nothing). Matching on the name alone would
|
|
507
|
-
// therefore score a miss against a string the session was never
|
|
508
|
-
// shown, and a miss is not inert: it lowers the entity's impact
|
|
509
|
-
// factor in core ranking. Match either.
|
|
510
|
-
const titleStmt = db.prepare('SELECT title FROM entities WHERE id = ?');
|
|
511
|
-
for (let i = 0; i < entityIds.length; i++) {
|
|
512
|
-
const name = (entityNames[i] || '').toLowerCase();
|
|
513
|
-
// Skip names that carry no recall signal: too short, or a
|
|
514
|
-
// machine identifier (auto-capture entities) that can never
|
|
515
|
-
// substring-match prose. Scoring those would be a guaranteed
|
|
516
|
-
// unearned miss — see isMeasurableRecallName.
|
|
517
|
-
let title = null;
|
|
518
|
-
try { title = titleStmt.get(entityIds[i])?.title ?? null; } catch { /* pre-title schema */ }
|
|
519
|
-
// A row is measurable if EITHER string could plausibly appear
|
|
520
|
-
// in prose; a machine-named row with a human title now can.
|
|
521
|
-
if (!isMeasurableRecallName(name) && !isMeasurableRecallName(title)) continue;
|
|
522
|
-
if (isRecallHit(sessionText, name) || isRecallHit(sessionText, title)) {
|
|
523
|
-
updateHit.run(entityIds[i]);
|
|
524
|
-
} else {
|
|
525
|
-
updateMiss.run(entityIds[i]);
|
|
526
|
-
}
|
|
514
|
+
for (const id of entityIds) {
|
|
515
|
+
if (cited.has(id)) updateHit.run(id);
|
|
527
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');
|
|
528
531
|
}
|
|
529
532
|
}
|
|
530
533
|
}
|
|
@@ -837,56 +840,6 @@ export function stripHookEchoes(rawTranscript) {
|
|
|
837
840
|
return kept.join('\n');
|
|
838
841
|
}
|
|
839
842
|
|
|
840
|
-
/**
|
|
841
|
-
* Did the session actually USE the memory named `name`, or does the name only
|
|
842
|
-
* appear because memesh injected it at session start?
|
|
843
|
-
*
|
|
844
|
-
* The caller passes `sessionText` with memesh's own SessionStart injection
|
|
845
|
-
* already stripped structurally (see `stripHookEchoes` — matches on
|
|
846
|
-
* `attachment.type`, so it is independent of JSON escaping and of how many
|
|
847
|
-
* times Claude Code echoes one injection). That removal is what stops an
|
|
848
|
-
* injected name from scoring a false hit; once the echo is gone, a plain
|
|
849
|
-
* substring match is the whole test.
|
|
850
|
-
*
|
|
851
|
-
* (This replaced an earlier `transcript.replace(injectedBlob, '')` + match,
|
|
852
|
-
* which silently failed on JSON-encoded transcripts and scored every entity a
|
|
853
|
-
* hit — see the callsite comment.)
|
|
854
|
-
*
|
|
855
|
-
* Ignores names shorter than 4 chars (too generic to match reliably).
|
|
856
|
-
*
|
|
857
|
-
* CONTRACT: `sessionText` must already be lowercased. The haystack is a
|
|
858
|
-
* multi-megabyte transcript and this runs twice per injected entity (name +
|
|
859
|
-
* title) — re-lowercasing it inside the function copied the whole transcript
|
|
860
|
-
* on every call, hundreds of MB of transient allocation in the Stop hook.
|
|
861
|
-
* The caller lowercases once; only the needle is normalized here.
|
|
862
|
-
*/
|
|
863
|
-
export function isRecallHit(sessionText, name) {
|
|
864
|
-
if (!name || name.length < 4) return false;
|
|
865
|
-
return String(sessionText ?? '').includes(String(name).toLowerCase());
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
/**
|
|
869
|
-
* Whether an injected entity's NAME can serve as a recall-effectiveness signal.
|
|
870
|
-
*
|
|
871
|
-
* Recall-effectiveness decides "was this injected memory used?" by substring-
|
|
872
|
-
* matching the entity NAME in the session transcript (isRecallHit). That only
|
|
873
|
-
* works for names a human might type. Auto-capture entities are named with
|
|
874
|
-
* machine identifiers — `session-<pid>-<ts>-files`, `commit-<hash>`,
|
|
875
|
-
* `pre-compact-<id>` — which never appear verbatim in conversation prose, so
|
|
876
|
-
* they take a `recall_miss` they didn't earn on every injection. Over repeated
|
|
877
|
-
* sessions that drags their Laplace-smoothed impact factor (scoring.ts, 10%
|
|
878
|
-
* weight) down and quietly suppresses auto-captured memories from future recall.
|
|
879
|
-
*
|
|
880
|
-
* We can't measure their usefulness by name, so we don't count them either way —
|
|
881
|
-
* they keep the neutral 0.5 impact. The prefix set is coupled to the auto-capture
|
|
882
|
-
* producers' `<kind>-<id>` naming (post-commit / session-summary / pre-compact);
|
|
883
|
-
* a new auto-capture producer should add its prefix here.
|
|
884
|
-
*/
|
|
885
|
-
export function isMeasurableRecallName(name) {
|
|
886
|
-
if (!name || name.length < 4) return false;
|
|
887
|
-
return !/^(session-|commit-|pre-compact-)/i.test(name);
|
|
888
|
-
}
|
|
889
|
-
|
|
890
843
|
export function maybeTriggerDream(projectName, config, pluginRoot) {
|
|
891
844
|
dreamTrigTrace('enter', { projectName, hasLlm: Boolean(config?.llm) });
|
|
892
845
|
if (!projectName || projectName === 'unknown') {
|
package/skills/memesh/SKILL.md
CHANGED
|
@@ -56,6 +56,16 @@ clear it.
|
|
|
56
56
|
Run `memesh briefing` (or `--project <name>`) and answer from it. For specific
|
|
57
57
|
follow-up questions, use `recall`.
|
|
58
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
|
+
|
|
59
69
|
## What's Already Automatic (Claude Code Plugin Hooks)
|
|
60
70
|
|
|
61
71
|
If MeMesh is installed as a Claude Code plugin, these happen **without any action from you**:
|
|
@@ -68,6 +78,7 @@ If MeMesh is installed as a Claude Code plugin, these happen **without any actio
|
|
|
68
78
|
| **PostToolUse (Bash)** | After `git commit` | Auto-tracks the commit with diff stats as a memory entity |
|
|
69
79
|
| **Stop** | Session ends | Auto-captures session knowledge + runs LLM failure analysis → lessons |
|
|
70
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 |
|
|
71
82
|
|
|
72
83
|
Because of the SessionStart hook: **in Claude Code, do NOT call `briefing` at
|
|
73
84
|
session start — it is already in your context.** Call it only mid-session
|