claude-mem-lite 2.31.0 → 2.31.2
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/hook-update.mjs +35 -0
- package/hook.mjs +28 -0
- package/install.mjs +44 -5
- package/package.json +1 -1
package/hook-update.mjs
CHANGED
|
@@ -196,6 +196,7 @@ const SOURCE_FILES = [
|
|
|
196
196
|
'cli.mjs', 'server.mjs', 'server-internals.mjs', 'tool-schemas.mjs',
|
|
197
197
|
'hook.mjs', 'hook-shared.mjs', 'hook-llm.mjs', 'hook-memory.mjs', 'skip-tools.mjs',
|
|
198
198
|
'hook-semaphore.mjs', 'hook-episode.mjs', 'hook-context.mjs', 'hook-handoff.mjs', 'hook-update.mjs',
|
|
199
|
+
'hook-optimize.mjs', 'plugin-cache-guard.mjs',
|
|
199
200
|
'haiku-client.mjs', 'utils.mjs', 'schema.mjs', 'package.json', 'package-lock.json', 'skill.md',
|
|
200
201
|
'registry.mjs', 'registry-scanner.mjs', 'registry-indexer.mjs',
|
|
201
202
|
'registry-retriever.mjs', 'resource-discovery.mjs',
|
|
@@ -289,6 +290,13 @@ export function installExtractedRelease(sourceDir, targetDir = INSTALL_DIR) {
|
|
|
289
290
|
// Post-update: prune old plugin cache versions (keep latest 3)
|
|
290
291
|
try { prunePluginCache(); } catch (e) { debugCatch(e, 'prunePluginCache'); }
|
|
291
292
|
|
|
293
|
+
// Post-update: clear cache hooks.json in every remaining version. Claude Code
|
|
294
|
+
// runtime reads plugin hooks from cache, not marketplace source — leaving populated
|
|
295
|
+
// cache hooks.json alongside install.mjs-written settings.json causes double firing.
|
|
296
|
+
// Inline impl (no import of plugin-cache-guard.mjs — this module must run even when
|
|
297
|
+
// the guard module is absent on disk, e.g. auto-upgrading from pre-2.31.2).
|
|
298
|
+
try { clearCacheHookResidue(); } catch (e) { debugCatch(e, 'clearCacheHookResidue'); }
|
|
299
|
+
|
|
292
300
|
debugLog('DEBUG', 'hook-update', `Auto-update: switched ${installed.length} paths`);
|
|
293
301
|
return true;
|
|
294
302
|
} catch (err) {
|
|
@@ -348,6 +356,33 @@ function copyReleaseIntoStaging(sourceDir, stagingDir) {
|
|
|
348
356
|
debugLog('DEBUG', 'hook-update', `Auto-update staged ${copied} source files`);
|
|
349
357
|
}
|
|
350
358
|
|
|
359
|
+
// ── Cache hook residue clearing ────────────────────────────
|
|
360
|
+
// Inline (does not import plugin-cache-guard.mjs) so hook-update.mjs keeps working
|
|
361
|
+
// even if plugin-cache-guard.mjs is missing on disk in degraded installs.
|
|
362
|
+
export function clearCacheHookResidue() {
|
|
363
|
+
const cacheBase = join(homedir(), '.claude', 'plugins', 'cache', 'sdsrss', 'claude-mem-lite');
|
|
364
|
+
if (!existsSync(cacheBase)) return 0;
|
|
365
|
+
let cleared = 0;
|
|
366
|
+
for (const ver of readdirSync(cacheBase)) {
|
|
367
|
+
const p = join(cacheBase, ver, 'hooks', 'hooks.json');
|
|
368
|
+
if (!existsSync(p)) continue;
|
|
369
|
+
try {
|
|
370
|
+
const h = JSON.parse(readFileSync(p, 'utf8'));
|
|
371
|
+
if (!h.hooks || Object.keys(h.hooks).length === 0) continue;
|
|
372
|
+
writeFileSync(p, JSON.stringify({
|
|
373
|
+
description: h.description || 'claude-mem-lite hooks',
|
|
374
|
+
_note: `Auto-cleared by hook-update.mjs post-install — prevents double hook registration (cache ver: ${ver})`,
|
|
375
|
+
hooks: {},
|
|
376
|
+
}, null, 2) + '\n');
|
|
377
|
+
cleared++;
|
|
378
|
+
} catch { /* ignore single bad entry */ }
|
|
379
|
+
}
|
|
380
|
+
if (cleared > 0) {
|
|
381
|
+
debugLog('DEBUG', 'hook-update', `Cache hooks residue cleared in ${cleared} version(s)`);
|
|
382
|
+
}
|
|
383
|
+
return cleared;
|
|
384
|
+
}
|
|
385
|
+
|
|
351
386
|
// ── Plugin Cache Pruning ──────────────────────────────────
|
|
352
387
|
const PLUGIN_CACHE_KEEP = 3;
|
|
353
388
|
|
package/hook.mjs
CHANGED
|
@@ -32,6 +32,16 @@ import { searchRelevantMemories } from './hook-memory.mjs';
|
|
|
32
32
|
import { buildAndSaveHandoff, detectContinuationIntent, renderHandoffInjection, extractUnfinishedSummary } from './hook-handoff.mjs';
|
|
33
33
|
import { checkForUpdate } from './hook-update.mjs';
|
|
34
34
|
import { handleLLMOptimize } from './hook-optimize.mjs';
|
|
35
|
+
// plugin-cache-guard.mjs loaded dynamically — pre-2.31.2 installs that auto-upgraded
|
|
36
|
+
// from an older hook-update.mjs SOURCE_FILES (which did not list this module) would
|
|
37
|
+
// crash on static import. Degrade gracefully to no-op when the module is absent.
|
|
38
|
+
let _cacheGuardCache = null;
|
|
39
|
+
async function loadCacheGuard() {
|
|
40
|
+
if (_cacheGuardCache !== null) return _cacheGuardCache;
|
|
41
|
+
try { _cacheGuardCache = await import('./plugin-cache-guard.mjs'); }
|
|
42
|
+
catch { _cacheGuardCache = {}; }
|
|
43
|
+
return _cacheGuardCache;
|
|
44
|
+
}
|
|
35
45
|
import { SKIP_TOOLS, SKIP_PREFIXES } from './skip-tools.mjs';
|
|
36
46
|
import { getVocabulary } from './tfidf.mjs';
|
|
37
47
|
|
|
@@ -397,6 +407,24 @@ async function handleStop() {
|
|
|
397
407
|
// ─── SessionStart Handler + CLAUDE.md Persistence (Tier 1 A, E) ─────────────
|
|
398
408
|
|
|
399
409
|
async function handleSessionStart() {
|
|
410
|
+
// Plugin cache self-heal: Claude Code auto-updates the marketplace plugin can
|
|
411
|
+
// re-populate cache/<ver>/hooks/hooks.json, reintroducing duplicate hook
|
|
412
|
+
// registration alongside install.mjs-managed settings.json entries. Silently
|
|
413
|
+
// clear — gated by hasInstallManagedHooks to avoid breaking plugin-only users.
|
|
414
|
+
// Dynamic-import fallback: if plugin-cache-guard.mjs is missing (pre-2.31.2
|
|
415
|
+
// auto-upgrade install), skip self-heal instead of crashing the entire hook.
|
|
416
|
+
try {
|
|
417
|
+
const guard = await loadCacheGuard();
|
|
418
|
+
if (guard.hasInstallManagedHooks && guard.hasInstallManagedHooks()) {
|
|
419
|
+
const cleared = guard.clearPluginCacheHooks({
|
|
420
|
+
reason: 'Auto-healed by hook.mjs session-start — install.mjs-managed hooks active in settings.json',
|
|
421
|
+
});
|
|
422
|
+
if (cleared.length > 0) {
|
|
423
|
+
debugLog('DEBUG', 'session-start', `auto-healed stale plugin cache hooks.json in version(s): ${cleared.join(', ')}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
} catch (e) { debugCatch(e, 'session-start-cache-heal'); }
|
|
427
|
+
|
|
400
428
|
// Read CC real session_id from hook stdin — used to scope handoff rows so parallel
|
|
401
429
|
// sessions for the same project don't clobber each other (see docs/bug.txt).
|
|
402
430
|
let ccSessionId = null;
|
package/install.mjs
CHANGED
|
@@ -26,6 +26,7 @@ const PLUGIN_KEY = `claude-mem-lite@${MARKETPLACE_KEY}`;
|
|
|
26
26
|
const NPM_INSTALL_CMD = 'npm install --omit=dev --no-audit --no-fund';
|
|
27
27
|
|
|
28
28
|
import { RESOURCE_METADATA } from './install-metadata.mjs';
|
|
29
|
+
import { scanPluginCacheHookPollution } from './plugin-cache-guard.mjs';
|
|
29
30
|
|
|
30
31
|
/**
|
|
31
32
|
* Derive invocation_name from resource name when metadata doesn't provide one.
|
|
@@ -205,6 +206,7 @@ async function install() {
|
|
|
205
206
|
'cli.mjs', 'server.mjs', 'server-internals.mjs', 'tool-schemas.mjs',
|
|
206
207
|
'hook.mjs', 'hook-shared.mjs', 'hook-llm.mjs', 'hook-memory.mjs', 'skip-tools.mjs',
|
|
207
208
|
'hook-semaphore.mjs', 'hook-episode.mjs', 'hook-context.mjs', 'hook-handoff.mjs', 'hook-update.mjs', 'hook-optimize.mjs',
|
|
209
|
+
'plugin-cache-guard.mjs',
|
|
208
210
|
'haiku-client.mjs', 'utils.mjs', 'schema.mjs', 'package.json', 'package-lock.json', 'skill.md',
|
|
209
211
|
'registry.mjs', 'registry-scanner.mjs', 'registry-indexer.mjs',
|
|
210
212
|
'registry-retriever.mjs', 'resource-discovery.mjs',
|
|
@@ -401,18 +403,43 @@ async function install() {
|
|
|
401
403
|
}
|
|
402
404
|
} catch (e) { warn(`Marketplace hooks dedup: ${e.message}`); }
|
|
403
405
|
|
|
404
|
-
// Sync launch.mjs to plugin cache — ensures MCP server loads dev code via symlink detection
|
|
406
|
+
// Sync launch.mjs to plugin cache — ensures MCP server loads dev code via symlink detection.
|
|
407
|
+
// ALSO clear cached hooks.json in every version dir — Claude Code runtime reads hooks from
|
|
408
|
+
// ~/.claude/plugins/cache/<mp>/<plugin>/<ver>/hooks/hooks.json, NOT from the marketplace source.
|
|
409
|
+
// Clearing only the marketplace source (above) leaves stale cache copies that double-register
|
|
410
|
+
// hooks alongside install.mjs-written settings.json entries.
|
|
405
411
|
try {
|
|
406
412
|
const cacheBase = join(homedir(), '.claude', 'plugins', 'cache', MARKETPLACE_KEY, 'claude-mem-lite');
|
|
407
413
|
if (existsSync(cacheBase)) {
|
|
408
414
|
const srcLaunch = join(PROJECT_DIR, 'scripts', 'launch.mjs');
|
|
415
|
+
let clearedHooks = 0;
|
|
409
416
|
for (const ver of readdirSync(cacheBase)) {
|
|
410
|
-
const
|
|
411
|
-
|
|
412
|
-
|
|
417
|
+
const verDir = join(cacheBase, ver);
|
|
418
|
+
|
|
419
|
+
// Sync launch.mjs
|
|
420
|
+
if (existsSync(join(verDir, 'scripts'))) {
|
|
421
|
+
try { copyFileSync(srcLaunch, join(verDir, 'scripts', 'launch.mjs')); } catch {}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Clear cached hooks.json (runtime reads here, not marketplace source)
|
|
425
|
+
const cachedHooksPath = join(verDir, 'hooks', 'hooks.json');
|
|
426
|
+
if (existsSync(cachedHooksPath)) {
|
|
427
|
+
try {
|
|
428
|
+
const h = JSON.parse(readFileSync(cachedHooksPath, 'utf8'));
|
|
429
|
+
if (h.hooks && Object.keys(h.hooks).length > 0) {
|
|
430
|
+
writeFileSync(cachedHooksPath, JSON.stringify({
|
|
431
|
+
description: h.description || 'claude-mem-lite hooks',
|
|
432
|
+
_note: `Hooks managed by install.mjs in settings.json — cache hooks.json cleared to prevent duplicate registration (cache ver: ${ver})`,
|
|
433
|
+
hooks: {}
|
|
434
|
+
}, null, 2) + '\n');
|
|
435
|
+
clearedHooks++;
|
|
436
|
+
}
|
|
437
|
+
} catch { /* silent — never block install on one bad cache entry */ }
|
|
413
438
|
}
|
|
414
439
|
}
|
|
415
|
-
|
|
440
|
+
const parts = ['launch.mjs synced (dev mode MCP routing)'];
|
|
441
|
+
if (clearedHooks > 0) parts.push(`${clearedHooks} stale hooks.json cleared`);
|
|
442
|
+
ok(`Plugin cache: ${parts.join('; ')}`);
|
|
416
443
|
}
|
|
417
444
|
} catch (e) { warn(`Plugin cache sync: ${e.message}`); }
|
|
418
445
|
}
|
|
@@ -962,6 +989,18 @@ async function status() {
|
|
|
962
989
|
fail('Hooks: not configured');
|
|
963
990
|
}
|
|
964
991
|
|
|
992
|
+
// Plugin cache pollution: populated hooks.json in cache AND install.mjs-managed
|
|
993
|
+
// settings.json hooks → runtime registers both → duplicate firing.
|
|
994
|
+
const polluted = scanPluginCacheHookPollution();
|
|
995
|
+
if (polluted.length > 0 && hasHooks) {
|
|
996
|
+
fail(`Plugin cache: stale hooks.json in version(s) ${polluted.join(', ')} — duplicate firing alongside settings.json (run 'install' to auto-clear)`);
|
|
997
|
+
} else if (polluted.length > 0) {
|
|
998
|
+
// plugin-only mode (no settings.json hooks) — cache hooks.json is the sole source, expected
|
|
999
|
+
ok(`Plugin cache: ${polluted.length} version(s) with hooks.json (plugin-only mode)`);
|
|
1000
|
+
} else if (pluginEnabled || hasHooks) {
|
|
1001
|
+
ok('Plugin cache: no stale hooks.json (no duplicate firing)');
|
|
1002
|
+
}
|
|
1003
|
+
|
|
965
1004
|
// Database
|
|
966
1005
|
if (existsSync(DB_PATH)) {
|
|
967
1006
|
try {
|