claude-mem-lite 2.63.0 → 2.64.0

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.
@@ -10,7 +10,7 @@
10
10
  "plugins": [
11
11
  {
12
12
  "name": "claude-mem-lite",
13
- "version": "2.63.0",
13
+ "version": "2.64.0",
14
14
  "source": "./",
15
15
  "description": "Lightweight persistent memory system for Claude Code — FTS5 search, episode batching, error-triggered recall"
16
16
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "2.63.0",
3
+ "version": "2.64.0",
4
4
  "description": "Lightweight persistent memory system for Claude Code — FTS5 search, episode batching, error-triggered recall",
5
5
  "author": {
6
6
  "name": "sdsrss"
package/cli/fts-check.mjs CHANGED
@@ -8,7 +8,7 @@ export function cmdFtsCheck(db, args) {
8
8
  const { positional } = parseArgs(args);
9
9
  const action = positional[0];
10
10
  if (!action || !['check', 'rebuild'].includes(action)) {
11
- fail('[mem] Usage: mem fts-check <check|rebuild>');
11
+ fail('[mem] Usage: claude-mem-lite fts-check <check|rebuild>');
12
12
  return;
13
13
  }
14
14
 
package/install.mjs CHANGED
@@ -1295,14 +1295,29 @@ async function doctor() {
1295
1295
  dwarn('Database: not found (will be created)');
1296
1296
  }
1297
1297
 
1298
- // Check for stale processes
1298
+ // Check for stale processes — extends beyond legacy chroma/worker to
1299
+ // catch MCP launchers / servers from cached old plugin versions. Auto-update
1300
+ // bumps installed_plugins.json but cannot kill the MCP process spawned for
1301
+ // an active session, so v2.60.0/v2.61.0 launchers commonly outlive their
1302
+ // version (recurrent pattern, see #2580 for the gsd analogue). Filtering
1303
+ // strategy: legacy chroma/worker = always stale; cache-path launchers = only
1304
+ // when their version segment ≠ current package.json version; dev-install
1305
+ // paths (no version segment) are never flagged.
1299
1306
  try {
1300
- const procs = execFileSync('pgrep', ['-af', 'chroma|claude-mem.*worker'], { encoding: 'utf8', timeout: 5000, stdio: 'pipe' }).trim();
1301
- // Filter out the pgrep process itself (matches its own pattern)
1302
- const real = procs.split('\n').filter(l => !l.includes('pgrep'));
1303
- if (real.length > 0) {
1304
- warn('Old processes running:\n ' + real.join('\n '));
1307
+ const procs = execFileSync('pgrep', ['-af', 'chroma|claude-mem-lite.*(scripts/launch|server)\\.mjs|claude-mem.*worker'], { encoding: 'utf8', timeout: 5000, stdio: 'pipe' }).trim();
1308
+ const lines = procs.split('\n').filter(l => l && !l.includes('pgrep'));
1309
+ let currentVersion = '';
1310
+ try { currentVersion = JSON.parse(readFileSync(join(PROJECT_DIR, 'package.json'), 'utf8')).version; } catch { /* fall through with empty version */ }
1311
+ const stale = lines.filter(l => {
1312
+ if (/chroma|claude-mem.*worker/.test(l)) return true;
1313
+ const m = l.match(/claude-mem-lite\/(\d+\.\d+\.\d+)\/(scripts\/launch|server)\.mjs/);
1314
+ return m && currentVersion && m[1] !== currentVersion;
1315
+ });
1316
+ if (stale.length > 0) {
1317
+ warn(`Old processes running${currentVersion ? ` (current: v${currentVersion})` : ''}:\n ` + stale.join('\n '));
1305
1318
  issues++;
1319
+ } else {
1320
+ ok('No stale processes');
1306
1321
  }
1307
1322
  } catch {
1308
1323
  ok('No stale processes');
@@ -1333,19 +1348,31 @@ async function doctor() {
1333
1348
  }
1334
1349
 
1335
1350
  // Dev drift: in dev-mode installs, all SOURCE_FILES entries should be
1336
- // symlinks. A plain file means an earlier install (or manual cp) copied it,
1337
- // so edits in the repo won't propagate to INSTALL_DIR hook runtime and
1338
- // test runtime silently diverge.
1351
+ // symlinks. A plain file means an earlier install (or manual cp) copied it
1352
+ // (edits in the repo won't propagate). A missing entry (neither symlink nor
1353
+ // plain) means an earlier install never wrote the file — same divergence
1354
+ // class. Per #8043: "is this file present ≠ is this install consistent" —
1355
+ // missing is tracked separately by checkDevDrift but the caller MUST surface
1356
+ // it to honour #8268's "gate the all-green string on every counter" rule.
1339
1357
  try {
1340
1358
  const { checkDevDrift } = await import('./lib/doctor-drift.mjs');
1341
1359
  const r = checkDevDrift(INSTALL_DIR, SOURCE_FILES);
1342
- if (r.drift) {
1343
- const names = r.details.join(', ');
1344
- const suffix = r.plainCount > r.details.length ? ` +${r.plainCount - r.details.length} more` : '';
1345
- warn(`Dev drift: ${r.plainCount} non-symlink file(s) in dev install: ${names}${suffix} (re-run: node ${join(PROJECT_DIR, 'install.mjs')} install --dev)`);
1360
+ if (r.drift || (r.devMode && r.missingCount > 0)) {
1361
+ const parts = [];
1362
+ if (r.plainCount > 0) {
1363
+ const names = r.plainFiles.slice(0, 5).join(', ');
1364
+ const suffix = r.plainCount > 5 ? ` +${r.plainCount - 5} more` : '';
1365
+ parts.push(`${r.plainCount} non-symlink: ${names}${suffix}`);
1366
+ }
1367
+ if (r.missingCount > 0) {
1368
+ const names = r.missingFiles.join(', ');
1369
+ const suffix = r.missingCount > r.missingFiles.length ? ` +${r.missingCount - r.missingFiles.length} more` : '';
1370
+ parts.push(`${r.missingCount} missing: ${names}${suffix}`);
1371
+ }
1372
+ warn(`Dev drift: ${parts.join('; ')} (re-run: node ${join(PROJECT_DIR, 'install.mjs')} install --dev)`);
1346
1373
  issues++;
1347
1374
  } else if (r.devMode) {
1348
- ok(`Dev drift: clean (${r.symlinkCount} symlinks, 0 plain)`);
1375
+ ok(`Dev drift: clean (${r.symlinkCount} symlinks, 0 plain, 0 missing)`);
1349
1376
  }
1350
1377
  // Prod (all plain) install: no message — dev-drift is a dev-only concern.
1351
1378
  } catch (e) {
@@ -41,6 +41,7 @@ export function checkDevDrift(installDir, sourceFiles) {
41
41
  plainCount: plainFiles.length,
42
42
  plainFiles,
43
43
  missingCount: missing.length,
44
+ missingFiles: missing.slice(0, 5),
44
45
  details: plainFiles.slice(0, 5),
45
46
  };
46
47
  }
@@ -1,5 +1,5 @@
1
1
  // Shared "save one observation" pipeline — used by both mem-cli.mjs::cmdSave
2
- // (CLI `mem save`) and server.mjs::mem_save (MCP tool).
2
+ // (CLI `claude-mem-lite save`) and server.mjs::mem_save (MCP tool).
3
3
  //
4
4
  // Pre-extraction (v2.60.0) the same dedup → scrub → minhash → CJK-bigram →
5
5
  // transactional INSERT block lived inline in both call sites (~110 lines × 2,
package/mem-cli.mjs CHANGED
@@ -34,7 +34,7 @@ function cmdSearch(db, args) {
34
34
  const { positional, flags } = parseArgs(args);
35
35
  const query = positional.join(' ');
36
36
  if (!query) {
37
- fail('[mem] Usage: mem search <query> [--type TYPE] [--source SOURCE] [--limit N] [--project P] [--from DATE] [--to DATE] [--importance N] [--branch B] [--offset N] [--sort relevance|time|importance] [--include-noise]');
37
+ fail('[mem] Usage: claude-mem-lite search <query> [--type TYPE] [--source SOURCE] [--limit N] [--project P] [--from DATE] [--to DATE] [--importance N] [--branch B] [--offset N] [--sort relevance|time|importance] [--include-noise]');
38
38
  return;
39
39
  }
40
40
 
@@ -400,7 +400,7 @@ function cmdRecall(db, args) {
400
400
  const { positional, flags } = parseArgs(args);
401
401
  const file = positional.join(' ');
402
402
  if (!file) {
403
- fail('[mem] Usage: mem recall <file> [--limit N] [--include-noise]');
403
+ fail('[mem] Usage: claude-mem-lite recall <file> [--limit N] [--include-noise]');
404
404
  return;
405
405
  }
406
406
 
@@ -530,7 +530,7 @@ function cmdGet(db, args) {
530
530
  const { positional, flags } = parseArgs(args);
531
531
  const idStr = positional.join(',');
532
532
  if (!idStr) {
533
- fail('[mem] Usage: mem get <id1,id2,...> [--source obs|session|prompt] [--fields f1,f2,...]\n' +
533
+ fail('[mem] Usage: claude-mem-lite get <id1,id2,...> [--source obs|session|prompt] [--fields f1,f2,...]\n' +
534
534
  ' IDs accept prefix from search output: #123 (obs), P#123 (prompt), S#123 (session).');
535
535
  return;
536
536
  }
@@ -785,7 +785,7 @@ function cmdSave(db, args) {
785
785
  const { positional, flags } = parseArgs(args);
786
786
  const text = positional.join(' ');
787
787
  if (!text) {
788
- fail('[mem] Usage: mem save "<text>" [--type T] [--title T] [--importance N] [--project P] [--files f1,f2] [--lesson T]');
788
+ fail('[mem] Usage: claude-mem-lite save "<text>" [--type T] [--title T] [--importance N] [--project P] [--files f1,f2] [--lesson T]');
789
789
  return;
790
790
  }
791
791
 
@@ -1137,7 +1137,7 @@ function cmdDelete(db, args) {
1137
1137
  const { positional, flags } = parseArgs(args);
1138
1138
  const idStr = positional.join(',');
1139
1139
  if (!idStr) {
1140
- fail('[mem] Usage: mem delete <id1,id2,...> [--confirm]');
1140
+ fail('[mem] Usage: claude-mem-lite delete <id1,id2,...> [--confirm]');
1141
1141
  return;
1142
1142
  }
1143
1143
 
@@ -1147,7 +1147,7 @@ function cmdDelete(db, args) {
1147
1147
  const nonObs = tokens.filter(t => /^[PpSs]#?\d+$/.test(t));
1148
1148
  if (nonObs.length > 0) {
1149
1149
  fail(`[mem] delete only works on observations. Rejected: ${nonObs.join(', ')}. ` +
1150
- `Prompts and sessions are append-only — inspect with \`mem get P#N --source prompt\` / \`--source session\`.`);
1150
+ `Prompts and sessions are append-only — inspect with \`claude-mem-lite get P#N --source prompt\` / \`--source session\`.`);
1151
1151
  return;
1152
1152
  }
1153
1153
  const ids = tokens.map(t => {
@@ -1215,7 +1215,7 @@ function cmdUpdate(db, args) {
1215
1215
  const parsed = raw ? parseIdToken(raw) : null;
1216
1216
  const id = parsed && parsed.source === null ? parsed.id : parseInt(raw, 10);
1217
1217
  if (!id || isNaN(id)) {
1218
- fail('[mem] Usage: mem update <id> [--title T] [--type T] [--importance N] [--lesson T] [--narrative T] [--concepts T]');
1218
+ fail('[mem] Usage: claude-mem-lite update <id> [--title T] [--type T] [--importance N] [--lesson T] [--narrative T] [--concepts T]');
1219
1219
  return;
1220
1220
  }
1221
1221
 
@@ -1442,7 +1442,7 @@ function cmdMaintain(db, args) {
1442
1442
  const { positional, flags } = parseArgs(args);
1443
1443
  const action = positional[0];
1444
1444
  if (!action || !['scan', 'execute'].includes(action)) {
1445
- fail('[mem] Usage: mem maintain <scan|execute> [--ops cleanup,decay,boost,dedup,purge_stale,rebuild_vectors] [--project P] [--retain-days N] [--merge-ids keepId:removeId,...]');
1445
+ fail('[mem] Usage: claude-mem-lite maintain <scan|execute> [--ops cleanup,decay,boost,dedup,purge_stale,rebuild_vectors] [--project P] [--retain-days N] [--merge-ids keepId:removeId,...]');
1446
1446
  return;
1447
1447
  }
1448
1448
 
@@ -1722,7 +1722,7 @@ function cmdRegistry(_memDb, args) {
1722
1722
  const { positional, flags } = parseArgs(args);
1723
1723
  const action = positional[0];
1724
1724
  if (!action || !['list', 'stats', 'search', 'import', 'remove', 'reindex'].includes(action)) {
1725
- fail('[mem] Usage: mem registry <list|stats|search|import|remove|reindex> [--type skill|agent] [--query Q] [--name N] [--resource-type T]');
1725
+ fail('[mem] Usage: claude-mem-lite registry <list|stats|search|import|remove|reindex> [--type skill|agent] [--query Q] [--name N] [--resource-type T]');
1726
1726
  return;
1727
1727
  }
1728
1728
 
@@ -1738,7 +1738,7 @@ function cmdRegistry(_memDb, args) {
1738
1738
  try {
1739
1739
  if (action === 'search') {
1740
1740
  const query = flags.query || positional.slice(1).join(' ');
1741
- if (!query) { fail('[mem] Usage: mem registry search <query> [--type skill|agent] [--category C] [--quality Q]'); return; }
1741
+ if (!query) { fail('[mem] Usage: claude-mem-lite registry search <query> [--type skill|agent] [--category C] [--quality Q]'); return; }
1742
1742
  let results = searchResources(rdb, query, {
1743
1743
  type: flags.type || undefined,
1744
1744
  limit: (flags.category || flags.quality) ? 20 : 10,
@@ -1828,7 +1828,7 @@ function cmdRegistry(_memDb, args) {
1828
1828
  if (action === 'import') {
1829
1829
  const name = flags.name;
1830
1830
  const resourceType = flags['resource-type'];
1831
- if (!name || !resourceType) { fail('[mem] Usage: mem registry import --name N --resource-type skill|agent [--invocation-name I] [--capability-summary S]'); return; }
1831
+ if (!name || !resourceType) { fail('[mem] Usage: claude-mem-lite registry import --name N --resource-type skill|agent [--invocation-name I] [--capability-summary S]'); return; }
1832
1832
  const fields = { name, type: resourceType, status: 'active', source: flags.source || 'user' };
1833
1833
  for (const f of ['repo-url', 'local-path', 'invocation-name', 'intent-tags', 'domain-tags', 'trigger-patterns', 'capability-summary', 'keywords', 'tech-stack', 'use-cases']) {
1834
1834
  const camel = f.replace(/-([a-z])/g, (_, c) => '_' + c);
@@ -1849,7 +1849,7 @@ function cmdRegistry(_memDb, args) {
1849
1849
  if (action === 'remove') {
1850
1850
  const name = flags.name;
1851
1851
  const resourceType = flags['resource-type'];
1852
- if (!name || !resourceType) { fail('[mem] Usage: mem registry remove --name N --resource-type skill|agent'); return; }
1852
+ if (!name || !resourceType) { fail('[mem] Usage: claude-mem-lite registry remove --name N --resource-type skill|agent'); return; }
1853
1853
  const result = rdb.prepare('DELETE FROM resources WHERE type = ? AND name = ?').run(resourceType, name);
1854
1854
  out(result.changes > 0 ? `[mem] Removed: ${resourceType}:${name}` : '[mem] Not found.');
1855
1855
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "2.63.0",
3
+ "version": "2.64.0",
4
4
  "description": "Lightweight persistent memory system for Claude Code",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",