@pcircle/memesh 4.1.2 → 4.1.4
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/.mcp.json +1 -3
- package/README.de.md +12 -1
- package/README.es.md +14 -2
- package/README.fr.md +14 -2
- package/README.ja.md +12 -1
- package/README.ko.md +12 -1
- package/README.md +17 -2
- package/README.pt.md +14 -2
- package/README.th.md +14 -2
- package/README.vi.md +14 -2
- package/README.zh-CN.md +12 -1
- package/README.zh-TW.md +12 -1
- package/dashboard/dist/index.html +13 -4
- package/dist/cli/view-live.d.ts +2 -0
- package/dist/cli/view-live.d.ts.map +1 -0
- package/dist/cli/view-live.js +2177 -0
- package/dist/cli/view-live.js.map +1 -0
- package/dist/cli/view.d.ts +0 -1
- package/dist/cli/view.d.ts.map +1 -1
- package/dist/cli/view.js +0 -2169
- package/dist/cli/view.js.map +1 -1
- package/dist/core/analytics.d.ts +23 -0
- package/dist/core/analytics.d.ts.map +1 -1
- package/dist/core/analytics.js +79 -0
- package/dist/core/analytics.js.map +1 -1
- package/dist/core/config.d.ts +9 -4
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +16 -9
- package/dist/core/config.js.map +1 -1
- package/dist/core/consolidator.d.ts.map +1 -1
- package/dist/core/consolidator.js +1 -0
- package/dist/core/consolidator.js.map +1 -1
- package/dist/core/demo.d.ts +9 -0
- package/dist/core/demo.d.ts.map +1 -0
- package/dist/core/demo.js +79 -0
- package/dist/core/demo.js.map +1 -0
- package/dist/core/doctor.d.ts +2 -1
- package/dist/core/doctor.d.ts.map +1 -1
- package/dist/core/doctor.js +198 -8
- package/dist/core/doctor.js.map +1 -1
- package/dist/core/dreamer.d.ts +65 -0
- package/dist/core/dreamer.d.ts.map +1 -0
- package/dist/core/dreamer.js +477 -0
- package/dist/core/dreamer.js.map +1 -0
- package/dist/core/embedder.d.ts.map +1 -1
- package/dist/core/embedder.js +9 -5
- package/dist/core/embedder.js.map +1 -1
- package/dist/core/extractor.d.ts.map +1 -1
- package/dist/core/extractor.js +44 -22
- package/dist/core/extractor.js.map +1 -1
- package/dist/core/graph.d.ts +1 -0
- package/dist/core/graph.d.ts.map +1 -1
- package/dist/core/graph.js +8 -2
- package/dist/core/graph.js.map +1 -1
- package/dist/core/install-hooks.d.ts +41 -0
- package/dist/core/install-hooks.d.ts.map +1 -0
- package/dist/core/install-hooks.js +181 -0
- package/dist/core/install-hooks.js.map +1 -0
- package/dist/core/install-id.d.ts +9 -0
- package/dist/core/install-id.d.ts.map +1 -0
- package/dist/core/install-id.js +51 -0
- package/dist/core/install-id.js.map +1 -0
- package/dist/core/lesson-engine.d.ts +1 -5
- package/dist/core/lesson-engine.d.ts.map +1 -1
- package/dist/core/lesson-engine.js +13 -13
- package/dist/core/lesson-engine.js.map +1 -1
- package/dist/core/llm-validator.d.ts +16 -0
- package/dist/core/llm-validator.d.ts.map +1 -0
- package/dist/core/llm-validator.js +182 -0
- package/dist/core/llm-validator.js.map +1 -0
- package/dist/core/operations.d.ts.map +1 -1
- package/dist/core/operations.js +6 -2
- package/dist/core/operations.js.map +1 -1
- package/dist/core/projects.d.ts +14 -0
- package/dist/core/projects.d.ts.map +1 -0
- package/dist/core/projects.js +70 -0
- package/dist/core/projects.js.map +1 -0
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/scoring.d.ts +0 -4
- package/dist/core/scoring.d.ts.map +1 -1
- package/dist/core/scoring.js +3 -10
- package/dist/core/scoring.js.map +1 -1
- package/dist/core/serializer.d.ts.map +1 -1
- package/dist/core/serializer.js +2 -0
- package/dist/core/serializer.js.map +1 -1
- package/dist/core/signal-scorer.d.ts +9 -0
- package/dist/core/signal-scorer.d.ts.map +1 -0
- package/dist/core/signal-scorer.js +70 -0
- package/dist/core/signal-scorer.js.map +1 -0
- package/dist/core/types.d.ts +2 -30
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/updater.d.ts +16 -0
- package/dist/core/updater.d.ts.map +1 -1
- package/dist/core/updater.js +85 -0
- package/dist/core/updater.js.map +1 -1
- package/dist/core/version-check.d.ts +2 -1
- package/dist/core/version-check.d.ts.map +1 -1
- package/dist/core/version-check.js +154 -30
- package/dist/core/version-check.js.map +1 -1
- package/dist/db.d.ts +7 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +91 -0
- package/dist/db.js.map +1 -1
- package/dist/knowledge-graph.d.ts +7 -3
- package/dist/knowledge-graph.d.ts.map +1 -1
- package/dist/knowledge-graph.js +43 -18
- package/dist/knowledge-graph.js.map +1 -1
- package/dist/skills-manifest.json +23 -18
- package/dist/storage/conflicts.d.ts +4 -1
- package/dist/storage/conflicts.d.ts.map +1 -1
- package/dist/storage/conflicts.js +5 -2
- package/dist/storage/conflicts.js.map +1 -1
- package/dist/transports/cli/cli.js +393 -37
- 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 +167 -13
- package/dist/transports/http/server.js.map +1 -1
- package/dist/transports/schemas.d.ts +2 -2
- package/hooks/hooks.json +12 -0
- package/package.json +16 -6
- package/plugin.json +1 -1
- package/scripts/hooks/_shared.js +257 -5
- package/scripts/hooks/pre-compact.js +19 -9
- package/scripts/hooks/session-start.js +364 -15
- package/scripts/hooks/session-summary.js +131 -29
- package/scripts/hooks/user-prompt-intent.js +159 -0
- package/skills/memesh/SKILL.md +9 -5
- package/skills/memesh-review/SKILL.md +24 -0
|
@@ -7,12 +7,58 @@
|
|
|
7
7
|
import { createRequire } from 'module';
|
|
8
8
|
import { join, basename } from 'path';
|
|
9
9
|
import { existsSync, readFileSync } from 'fs';
|
|
10
|
-
import {
|
|
10
|
+
import { pathToFileURL } from 'url';
|
|
11
|
+
import {
|
|
12
|
+
decideAutoUpdateHook,
|
|
13
|
+
getMemeshDir,
|
|
14
|
+
isAutoCaptureEnabled,
|
|
15
|
+
openHookDb,
|
|
16
|
+
readUpdateCheckCache,
|
|
17
|
+
resolveAutoUpdatePolicy,
|
|
18
|
+
resolvePluginRoot,
|
|
19
|
+
spawnAutoUpdate,
|
|
20
|
+
} from './_shared.js';
|
|
11
21
|
|
|
12
22
|
const require = createRequire(import.meta.url);
|
|
13
23
|
|
|
24
|
+
// Pre-load dist/core/install-channel.js for auto-update channel detection.
|
|
25
|
+
// Same pattern as session-start.js: async ESM import at process init,
|
|
26
|
+
// falls back to null if dist is missing (source checkout pre-build).
|
|
27
|
+
let _installChannelMod = null;
|
|
28
|
+
try {
|
|
29
|
+
const _pluginRootForInit = resolvePluginRoot(import.meta.url);
|
|
30
|
+
const _modPath = join(_pluginRootForInit, 'dist/core/install-channel.js');
|
|
31
|
+
if (existsSync(_modPath)) {
|
|
32
|
+
_installChannelMod = await import(pathToFileURL(_modPath).href);
|
|
33
|
+
}
|
|
34
|
+
} catch { /* best-effort */ }
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Run auto-update at Stop hook: reads cache, evaluates policy, and spawns
|
|
38
|
+
* npm install -g if warranted. Runs after all session work completes,
|
|
39
|
+
* avoiding the TOCTOU race where install would overwrite dist/ mid-session.
|
|
40
|
+
*/
|
|
41
|
+
async function runAutoUpdateAtStop() {
|
|
42
|
+
try {
|
|
43
|
+
const pluginRoot = resolvePluginRoot(import.meta.url);
|
|
44
|
+
const pkg = JSON.parse(readFileSync(join(pluginRoot, 'package.json'), 'utf8'));
|
|
45
|
+
const installedVersion = typeof pkg.version === 'string' ? pkg.version : null;
|
|
46
|
+
if (!installedVersion) return;
|
|
47
|
+
|
|
48
|
+
const cache = readUpdateCheckCache(installedVersion);
|
|
49
|
+
const policy = resolveAutoUpdatePolicy(process.env);
|
|
50
|
+
const decision = decideAutoUpdateHook(installedVersion, cache, policy);
|
|
51
|
+
if (decision.run) {
|
|
52
|
+
spawnAutoUpdate(decision.latest, decision.deprecationOverride, _installChannelMod);
|
|
53
|
+
}
|
|
54
|
+
} catch {
|
|
55
|
+
// Best-effort — never crash the hook.
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
14
59
|
// Parse a JSONL transcript file.
|
|
15
|
-
//
|
|
60
|
+
// Handles the current Claude Code transcript format where tool_use/tool_result
|
|
61
|
+
// are nested inside assistant/user message entries, not top-level entries.
|
|
16
62
|
// Defensive: never throws — malformed lines are silently skipped.
|
|
17
63
|
function parseTranscript(transcriptPath) {
|
|
18
64
|
const filesEdited = new Set();
|
|
@@ -26,30 +72,67 @@ function parseTranscript(transcriptPath) {
|
|
|
26
72
|
try {
|
|
27
73
|
const entry = JSON.parse(line);
|
|
28
74
|
|
|
29
|
-
//
|
|
30
|
-
if (entry.type === '
|
|
75
|
+
// Current format: assistant entries contain tool_use blocks in message.content
|
|
76
|
+
if (entry.type === 'assistant' && Array.isArray(entry.message?.content)) {
|
|
77
|
+
for (const block of entry.message.content) {
|
|
78
|
+
if (block.type !== 'tool_use') continue;
|
|
79
|
+
toolCallCount++;
|
|
31
80
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
81
|
+
if (block.name === 'Write' || block.name === 'Edit') {
|
|
82
|
+
const fp = block.input?.file_path ?? block.input?.path;
|
|
83
|
+
if (fp && typeof fp === 'string') filesEdited.add(basename(fp));
|
|
84
|
+
}
|
|
85
|
+
if (block.name === 'Bash') {
|
|
86
|
+
const cmd = block.input?.command ?? '';
|
|
87
|
+
if (typeof cmd === 'string' && cmd.length > 10 && !cmd.startsWith('ls') && !cmd.startsWith('cd')) {
|
|
88
|
+
bashCommands.push(cmd.slice(0, 100));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
37
92
|
}
|
|
38
93
|
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
94
|
+
// Current format: user entries contain tool_result blocks in message.content.
|
|
95
|
+
//
|
|
96
|
+
// Use the explicit `is_error` flag the transcript records on each
|
|
97
|
+
// tool_result instead of substring-matching the result text. The
|
|
98
|
+
// earlier substring approach treated any Read/Bash output that
|
|
99
|
+
// happened to contain the word "Error" (READMEs documenting errors,
|
|
100
|
+
// CHANGELOG entries, source files mentioning "Error", grep over docs)
|
|
101
|
+
// as a real error, drowning analyzeFailure() in noise — a 47MB
|
|
102
|
+
// transcript reported 315 "errors" against ~28 real ones. The flag
|
|
103
|
+
// is the canonical signal Claude Code itself uses to mark a tool as
|
|
104
|
+
// having failed.
|
|
105
|
+
if (entry.type === 'user' && Array.isArray(entry.message?.content)) {
|
|
106
|
+
for (const block of entry.message.content) {
|
|
107
|
+
if (block.type !== 'tool_result') continue;
|
|
108
|
+
if (block.is_error !== true) continue;
|
|
109
|
+
const text = typeof block.content === 'string'
|
|
110
|
+
? block.content
|
|
111
|
+
: JSON.stringify(block.content);
|
|
112
|
+
errorsEncountered.push(text.slice(0, 200));
|
|
44
113
|
}
|
|
45
114
|
}
|
|
46
115
|
|
|
47
|
-
//
|
|
116
|
+
// Legacy format (older Claude Code versions): top-level tool_use/tool_result
|
|
117
|
+
if (entry.type === 'tool_use' || entry.tool_name) {
|
|
118
|
+
toolCallCount++;
|
|
119
|
+
if (entry.tool_name === 'Write' || entry.tool_name === 'Edit') {
|
|
120
|
+
const fp = (entry.tool_input?.file_path ?? entry.tool_input?.path);
|
|
121
|
+
if (fp && typeof fp === 'string') filesEdited.add(basename(fp));
|
|
122
|
+
}
|
|
123
|
+
if (entry.tool_name === 'Bash') {
|
|
124
|
+
const cmd = entry.tool_input?.command ?? '';
|
|
125
|
+
if (typeof cmd === 'string' && cmd.length > 10 && !cmd.startsWith('ls') && !cmd.startsWith('cd')) {
|
|
126
|
+
bashCommands.push(cmd.slice(0, 100));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
48
130
|
if (entry.type === 'tool_result' && entry.content != null) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
131
|
+
// Same fix as the current-format branch: trust the flag, not text.
|
|
132
|
+
if (entry.is_error !== true) {
|
|
133
|
+
// empty
|
|
134
|
+
} else {
|
|
135
|
+
const text = typeof entry.content === 'string' ? entry.content : JSON.stringify(entry.content);
|
|
53
136
|
errorsEncountered.push(text.slice(0, 200));
|
|
54
137
|
}
|
|
55
138
|
}
|
|
@@ -99,6 +182,14 @@ process.stdin.on('end', async () => {
|
|
|
99
182
|
// Skip sessions with too little activity
|
|
100
183
|
if (toolCallCount < 3) return exit0();
|
|
101
184
|
|
|
185
|
+
// Hoisted to outer-try scope so the LLM failure-analysis block
|
|
186
|
+
// below (which runs AFTER db.close()) can reference it. Earlier
|
|
187
|
+
// version defined projectName inside the inner try-finally and the
|
|
188
|
+
// LLM path threw `projectName is not defined` silently — caught by
|
|
189
|
+
// the LLM try/catch but logged to stderr. Result: lesson_learned
|
|
190
|
+
// creation never actually happened in production.
|
|
191
|
+
const projectName = basename(cwd);
|
|
192
|
+
|
|
102
193
|
// Open DB via shared helper — applies SCHEMA_SQL + status migration.
|
|
103
194
|
// sqlite-vec is loaded separately because only this hook needs it
|
|
104
195
|
// (for embedding-aware recall-effectiveness tracking).
|
|
@@ -107,14 +198,19 @@ process.stdin.on('end', async () => {
|
|
|
107
198
|
try {
|
|
108
199
|
sqliteVec.load(db);
|
|
109
200
|
|
|
110
|
-
// Duplicate detection: if we already captured this session, bail
|
|
111
|
-
|
|
112
|
-
|
|
201
|
+
// Duplicate detection: if we already captured this session, bail.
|
|
202
|
+
//
|
|
203
|
+
// Use the FULL session_id rather than the first 8 chars: real
|
|
204
|
+
// Claude Code UUIDs collide on 8 chars only with cosmically small
|
|
205
|
+
// probability, but artificial test IDs (verify-fix-001 vs -002)
|
|
206
|
+
// share the prefix and silently skipped the second session
|
|
207
|
+
// entirely. The contract is one stored capture per distinct
|
|
208
|
+
// session_id, so the dedup key has to be the full id.
|
|
209
|
+
const alreadyCaptured = db.prepare("SELECT id FROM entities WHERE name = ?").get(`session-${sessionId}-files`);
|
|
113
210
|
if (alreadyCaptured) return exit0();
|
|
114
211
|
|
|
115
212
|
// Build and store session memories
|
|
116
|
-
const
|
|
117
|
-
const baseTags = ['source:auto-capture', `session:${shortId}`, `project:${projectName}`];
|
|
213
|
+
const baseTags = ['source:auto-capture', `session:${sessionId}`, `project:${projectName}`];
|
|
118
214
|
|
|
119
215
|
const insertEntity = db.prepare('INSERT OR IGNORE INTO entities (name, type) VALUES (?, ?)');
|
|
120
216
|
const selectEntity = db.prepare('SELECT id FROM entities WHERE name = ?');
|
|
@@ -132,7 +228,7 @@ process.stdin.on('end', async () => {
|
|
|
132
228
|
// Rule 1: File editing session summary
|
|
133
229
|
if (filesEdited.length > 0) {
|
|
134
230
|
storeMemory(
|
|
135
|
-
`session-${
|
|
231
|
+
`session-${sessionId}-files`,
|
|
136
232
|
'session-insight',
|
|
137
233
|
[
|
|
138
234
|
`Session edited ${filesEdited.length} file(s): ${filesEdited.join(', ')}`,
|
|
@@ -145,7 +241,7 @@ process.stdin.on('end', async () => {
|
|
|
145
241
|
// Rule 2: Error -> Fix pattern detection
|
|
146
242
|
if (errorsEncountered.length > 0 && filesEdited.length > 0) {
|
|
147
243
|
storeMemory(
|
|
148
|
-
`session-${
|
|
244
|
+
`session-${sessionId}-fixes`,
|
|
149
245
|
'session-insight',
|
|
150
246
|
[
|
|
151
247
|
`Fixed ${errorsEncountered.length} error(s) by editing ${filesEdited.join(', ')}`,
|
|
@@ -158,7 +254,7 @@ process.stdin.on('end', async () => {
|
|
|
158
254
|
// Rule 3: Heavy session summary (20+ tool calls = significant work)
|
|
159
255
|
if (toolCallCount >= 20) {
|
|
160
256
|
storeMemory(
|
|
161
|
-
`session-${
|
|
257
|
+
`session-${sessionId}-summary`,
|
|
162
258
|
'session-insight',
|
|
163
259
|
[
|
|
164
260
|
`Significant session: ${toolCallCount} tool calls, ${filesEdited.length} files edited`,
|
|
@@ -277,8 +373,10 @@ process.stdin.on('end', async () => {
|
|
|
277
373
|
closeDatabase();
|
|
278
374
|
}
|
|
279
375
|
}
|
|
280
|
-
} catch {
|
|
281
|
-
// LLM analysis failed — rule-based extraction already captured the session
|
|
376
|
+
} catch (llmErr) {
|
|
377
|
+
// LLM analysis failed — rule-based extraction already captured the session.
|
|
378
|
+
// Log to stderr so config issues (e.g. invalid API key) are visible.
|
|
379
|
+
try { process.stderr.write(`[memesh] LLM failure analysis skipped: ${llmErr?.message || llmErr}\n`); } catch {}
|
|
282
380
|
}
|
|
283
381
|
}
|
|
284
382
|
} catch (err) {
|
|
@@ -286,6 +384,10 @@ process.stdin.on('end', async () => {
|
|
|
286
384
|
try { process.stderr.write(`[memesh session-summary] ${err?.message || err}\n`); } catch {}
|
|
287
385
|
}
|
|
288
386
|
|
|
387
|
+
// Spawn auto-update if policy + cache permit. Runs after all session work
|
|
388
|
+
// so npm install -g doesn't overwrite dist/ while peer hooks are reading it.
|
|
389
|
+
await runAutoUpdateAtStop();
|
|
390
|
+
|
|
289
391
|
// Silent output — don't clutter Claude's response
|
|
290
392
|
console.log(JSON.stringify({ suppressOutput: true }));
|
|
291
393
|
exit0();
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// User Prompt Intent — UserPromptSubmit hook
|
|
4
|
+
//
|
|
5
|
+
// Detects when the user explicitly asks Claude to remember / save / memorize
|
|
6
|
+
// content from the current session, and injects a context hint reminding Claude
|
|
7
|
+
// to use `mcp__memesh__remember` for cross-project recall.
|
|
8
|
+
//
|
|
9
|
+
// Why a hint instead of autonomous capture? The user's intent is clear, but
|
|
10
|
+
// "what to remember" usually depends on the surrounding conversation —
|
|
11
|
+
// extracting that requires an LLM round and policy decisions (name, type,
|
|
12
|
+
// observations, namespace). A polite reminder keeps the calling agent in the
|
|
13
|
+
// loop with full conversation context, while still preventing the
|
|
14
|
+
// "I forgot to use memesh" failure mode that motivated this hook.
|
|
15
|
+
//
|
|
16
|
+
// Defensive: never blocks user prompts, even on hook failure. Errors are
|
|
17
|
+
// surfaced to stderr (visible in Claude Code debug logs) rather than
|
|
18
|
+
// swallowed — stderr does not affect prompt submission.
|
|
19
|
+
// Gated by `autoCapture` flag (same as other memesh write hooks).
|
|
20
|
+
|
|
21
|
+
import { pathToFileURL } from 'url';
|
|
22
|
+
import { isAutoCaptureEnabled } from './_shared.js';
|
|
23
|
+
|
|
24
|
+
// Patterns compiled at module load — invalid regex MUST fail loudly. Do
|
|
25
|
+
// NOT move into a try block "for safety": a regex compile error is a
|
|
26
|
+
// programmer error, not a runtime condition, and silencing it would hide
|
|
27
|
+
// real bugs.
|
|
28
|
+
//
|
|
29
|
+
// Design principle: when in doubt between matching and not matching, do
|
|
30
|
+
// NOT match — a missed hint is recoverable (user repeats themselves), but
|
|
31
|
+
// a false hint pollutes context and pressures the LLM into a wrong action.
|
|
32
|
+
//
|
|
33
|
+
// Disambiguation policy:
|
|
34
|
+
// - All imperatives anchored to sentence start (^ or after .!?\n) to skip
|
|
35
|
+
// interrogatives ("do you remember X?", "What does save to memesh do?").
|
|
36
|
+
// - For save-class verbs, "memesh" suffix required to avoid false positives
|
|
37
|
+
// on generic "save this" (could mean clipboard, file, bookmark, etc.).
|
|
38
|
+
// - Supported languages: English, Spanish, French, Portuguese, Traditional Chinese.
|
|
39
|
+
// Additional languages welcome via PR (provide native-speaker validation).
|
|
40
|
+
export const INTENT_PATTERNS = [
|
|
41
|
+
// English: "Remember/memorize this|that"
|
|
42
|
+
/(?:^|[.!?\n]\s*)(?:please\s+)?(?:remember|memorize)\s+(?:this|that)\b/im,
|
|
43
|
+
// English: "save/add/store to memesh"
|
|
44
|
+
/(?:^|[.!?\n]\s*)(?:please\s+)?(?:save|add|put|store|write)\s+(?:(?:this|that|it)\s+)?(?:to|in|into)\s+memesh\b/im,
|
|
45
|
+
|
|
46
|
+
// Spanish: "Recordar/memorizar esto|eso"
|
|
47
|
+
/(?:^|[.!?\n]\s*)(?:por favor\s+)?(?:recordar|memorizar)\s+(?:esto|eso)\b/im,
|
|
48
|
+
// Spanish: "guardar en memesh"
|
|
49
|
+
/(?:^|[.!?\n]\s*)(?:por favor\s+)?(?:guardar|añadir|almacenar)\s+(?:(?:esto|eso)\s+)?(?:en|a)\s+memesh\b/im,
|
|
50
|
+
|
|
51
|
+
// French: "Rappeler/mémoriser ceci|cela"
|
|
52
|
+
/(?:^|[.!?\n]\s*)(?:s'il vous plaît\s+)?(?:rappeler|mémoriser)\s+(?:ceci|cela|ça)\b/im,
|
|
53
|
+
// French: "sauvegarder dans memesh"
|
|
54
|
+
/(?:^|[.!?\n]\s*)(?:s'il vous plaît\s+)?(?:sauvegarder|enregistrer|ajouter)\s+(?:(?:ceci|cela|ça)\s+)?(?:dans|à)\s+memesh\b/im,
|
|
55
|
+
|
|
56
|
+
// Portuguese: "Lembrar/memorizar isto|isso"
|
|
57
|
+
/(?:^|[.!?\n]\s*)(?:por favor\s+)?(?:lembrar|memorizar)\s+(?:isto|isso)\b/im,
|
|
58
|
+
// Portuguese: "salvar em memesh"
|
|
59
|
+
/(?:^|[.!?\n]\s*)(?:por favor\s+)?(?:salvar|guardar|adicionar|armazenar)\s+(?:(?:isto|isso)\s+)?(?:em|no)\s+memesh\b/im,
|
|
60
|
+
|
|
61
|
+
// Traditional Chinese: 記下來, 記到/存到/寫進/存進 memesh|記憶
|
|
62
|
+
/記下來|記到\s*(?:memesh|記憶)|存到\s*(?:memesh|記憶)|寫進\s*(?:memesh|記憶)|存進\s*(?:memesh|記憶)/,
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
export function detectRememberIntent(prompt) {
|
|
66
|
+
if (!prompt || typeof prompt !== 'string') return false;
|
|
67
|
+
for (const re of INTENT_PATTERNS) {
|
|
68
|
+
if (re.test(prompt)) return true;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// The returned string is consumed BY THE LLM as additionalContext, NOT by
|
|
74
|
+
// configuration or by Claude Code itself. Edits here change LLM behavior,
|
|
75
|
+
// not hook behavior.
|
|
76
|
+
export function buildHint() {
|
|
77
|
+
return [
|
|
78
|
+
'<memesh-remember-intent>',
|
|
79
|
+
'The user just asked you to save / remember content. Use memesh for cross-project recall:',
|
|
80
|
+
'',
|
|
81
|
+
'1. Decide WHAT to remember from the conversation context. Be specific — pick observations',
|
|
82
|
+
' that will be useful in *future* sessions, not session-local state.',
|
|
83
|
+
'',
|
|
84
|
+
'2. Decide the SCOPE (this drives namespace + tags):',
|
|
85
|
+
' • Machine-level / cross-project / preferences → memesh namespace=personal',
|
|
86
|
+
' • Project-internal decision / pattern / lesson → memesh + project tag (e.g. tag:project:memesh)',
|
|
87
|
+
' • Universal / public best practice → memesh namespace=global (rare)',
|
|
88
|
+
'',
|
|
89
|
+
'3. Call `mcp__memesh__remember` with:',
|
|
90
|
+
' • name: descriptive entity name (e.g., "aws-cdk-stack-pattern")',
|
|
91
|
+
' • type: one of (decision, pattern, lesson_learned, bug, process, preference, etc.)',
|
|
92
|
+
' • observations: array of specific facts / steps / rationale',
|
|
93
|
+
' • tags: relevant tags (programming language, framework, domain)',
|
|
94
|
+
' • namespace: personal (default) | team | global',
|
|
95
|
+
'',
|
|
96
|
+
'4. Confirm to the user with: entity name + memesh ID returned by the tool.',
|
|
97
|
+
'</memesh-remember-intent>',
|
|
98
|
+
].join('\n');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function logError(scope, msg) {
|
|
102
|
+
// Hooks may write to stderr without blocking prompt submission. Use this
|
|
103
|
+
// to surface failures in Claude Code debug logs instead of swallowing.
|
|
104
|
+
try {
|
|
105
|
+
process.stderr.write(`[memesh:${scope}] ${msg}\n`);
|
|
106
|
+
} catch {
|
|
107
|
+
// stderr itself failing is unrecoverable; stay silent.
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Only run the stdin pipeline when invoked directly as a script — not when
|
|
112
|
+
// imported by the test suite for in-process unit testing. On Windows,
|
|
113
|
+
// `file://${process.argv[1]}` produces an invalid URL because the path uses
|
|
114
|
+
// backslashes; pathToFileURL() correctly normalizes to a file:// URL on
|
|
115
|
+
// every platform, so the comparison is portable.
|
|
116
|
+
const isMainModule = import.meta.url === pathToFileURL(process.argv[1]).href;
|
|
117
|
+
if (isMainModule) {
|
|
118
|
+
let input = '';
|
|
119
|
+
process.stdin.setEncoding('utf8');
|
|
120
|
+
process.stdin.on('data', (chunk) => { input += chunk; });
|
|
121
|
+
process.stdin.on('end', () => {
|
|
122
|
+
try {
|
|
123
|
+
if (!isAutoCaptureEnabled(process.env)) return process.exit(0);
|
|
124
|
+
|
|
125
|
+
// Distinguish empty stdin (legitimate degenerate event) from malformed
|
|
126
|
+
// input (protocol drift). Both stay non-blocking, but only malformed
|
|
127
|
+
// input is logged — empty is normal, garbage indicates a real bug.
|
|
128
|
+
let data = {};
|
|
129
|
+
const trimmed = input.trim();
|
|
130
|
+
if (trimmed) {
|
|
131
|
+
try {
|
|
132
|
+
data = JSON.parse(trimmed);
|
|
133
|
+
} catch (parseErr) {
|
|
134
|
+
logError('user-prompt-intent', `malformed stdin JSON (len=${input.length}): ${parseErr.message}`);
|
|
135
|
+
return process.exit(0);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Claude Code sends `prompt`. The `user_prompt` fallback is defensive:
|
|
140
|
+
// Claude Code's transcript format changed once before (2026-05-07), so
|
|
141
|
+
// we accept either name to survive a similar rename. If both are absent
|
|
142
|
+
// or non-string, detectRememberIntent's type guard returns false safely.
|
|
143
|
+
const prompt = data.prompt ?? data.user_prompt ?? '';
|
|
144
|
+
if (!detectRememberIntent(prompt)) return process.exit(0);
|
|
145
|
+
|
|
146
|
+
const out = {
|
|
147
|
+
hookSpecificOutput: {
|
|
148
|
+
hookEventName: 'UserPromptSubmit',
|
|
149
|
+
additionalContext: buildHint(),
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
process.stdout.write(JSON.stringify(out));
|
|
153
|
+
process.exit(0);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
logError('user-prompt-intent', err?.message || err);
|
|
156
|
+
process.exit(0);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
package/skills/memesh/SKILL.md
CHANGED
|
@@ -29,15 +29,19 @@ If MeMesh is installed as a Claude Code plugin, these happen **without any actio
|
|
|
29
29
|
|
|
30
30
|
| Hook | When | What it does |
|
|
31
31
|
|------|------|-------------|
|
|
32
|
-
| **SessionStart** | Every session begins | Auto-recalls top memories for current project + surfaces lesson warnings |
|
|
33
|
-
| **
|
|
32
|
+
| **SessionStart** | Every session begins | Auto-recalls top memories for current project + surfaces lesson warnings + (opt-in) agentic-orchestration banner |
|
|
33
|
+
| **PreToolUse (Edit)** | Before editing files | Injects memories related to the file or project |
|
|
34
|
+
| **PreToolUse (Bash)** | Before bash commands | (Opt-in) Nudges Claude to dispatch high-verifiability commands as background agents |
|
|
35
|
+
| **UserPromptSubmit** | When you submit a prompt | Detects "remember this" intent (5 languages: en, es, fr, pt, zh-TW) and reminds Claude to use memesh |
|
|
36
|
+
| **PostToolUse (Commit)** | After `git commit` | Auto-tracks commit with diff stats as a memory entity |
|
|
34
37
|
| **Stop** | Session ends | Auto-captures session knowledge + runs LLM failure analysis → lessons |
|
|
35
38
|
| **PreCompact** | Before context compaction | Saves important knowledge before conversation history is compressed |
|
|
36
39
|
|
|
37
40
|
**You do NOT need to manually:**
|
|
38
|
-
- Recall at session start (hook does it)
|
|
39
|
-
- Remember commits (hook does it)
|
|
40
|
-
- Summarize sessions (hook does it)
|
|
41
|
+
- Recall at session start (SessionStart hook does it)
|
|
42
|
+
- Remember commits (PostToolUse hook does it)
|
|
43
|
+
- Summarize sessions (Stop hook does it)
|
|
44
|
+
- Remember when you say "記下來" / "remember this" (UserPromptSubmit hook reminds Claude)
|
|
41
45
|
|
|
42
46
|
**You DO need to manually** use the commands below for intentional knowledge management.
|
|
43
47
|
|
|
@@ -89,9 +89,33 @@ memesh remember --name "missing-knowledge" --type decision --obs "..."
|
|
|
89
89
|
memesh recall --limit 5 --json # confirm changes took effect
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
## Documentation & Code Quality Checks
|
|
93
|
+
|
|
94
|
+
**For comprehensive documentation synchronization and lint checks**, use:
|
|
95
|
+
```bash
|
|
96
|
+
@sa:comprehensive-code-review
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
This skill includes:
|
|
100
|
+
- **Dim 17: Documentation Synchronization** — version consistency, API docs, architecture docs, feature docs, deprecated terms, breaking changes
|
|
101
|
+
- **Dim 18: Code Style & Lint** — lint errors/warnings, security rules, disabled rules, style consistency
|
|
102
|
+
|
|
103
|
+
**MeMesh-specific automation**:
|
|
104
|
+
```bash
|
|
105
|
+
# Quick verification (memesh-specific checks)
|
|
106
|
+
./scripts/verify-docs-sync.sh
|
|
107
|
+
# Exit code 0 = all checks pass
|
|
108
|
+
|
|
109
|
+
# Lint check
|
|
110
|
+
npm run lint # 0 errors expected, ~83 warnings (technical debt)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
92
115
|
## Tips
|
|
93
116
|
|
|
94
117
|
- Run every 1-2 weeks to keep memory healthy
|
|
95
118
|
- Health score < 50 → too many stale or low-quality memories
|
|
96
119
|
- Noise > 80% → encourage deliberate `memesh remember` for decisions
|
|
97
120
|
- Dashboard available at: http://localhost:3737/dashboard (run `memesh serve` first)
|
|
121
|
+
- **Before merge**: Run documentation sync checklist above to prevent drift
|