claude-mem-lite 2.71.1 → 2.71.3

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.71.1",
13
+ "version": "2.71.3",
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.71.1",
3
+ "version": "2.71.3",
4
4
  "description": "Lightweight persistent memory system for Claude Code — FTS5 search, episode batching, error-triggered recall",
5
5
  "author": {
6
6
  "name": "sdsrss"
@@ -30,10 +30,10 @@ export function computeQualityStats(db, { project, days }) {
30
30
  const windowRow = db.prepare(`
31
31
  SELECT
32
32
  COUNT(*) as total,
33
- SUM(CASE WHEN lesson_learned IS NOT NULL AND lesson_learned != '' THEN 1 ELSE 0 END) as with_lesson,
34
- SUM(CASE WHEN ${lowSignalIsMatchExpr} THEN 1 ELSE 0 END) as low_signal,
35
- SUM(CASE WHEN type = 'bugfix' THEN 1 ELSE 0 END) as bugfix_total,
36
- SUM(CASE WHEN type = 'bugfix' AND ${unresolvedNarrativeExpr} THEN 1 ELSE 0 END) as bugfix_unresolved
33
+ COALESCE(SUM(CASE WHEN lesson_learned IS NOT NULL AND lesson_learned != '' THEN 1 ELSE 0 END), 0) as with_lesson,
34
+ COALESCE(SUM(CASE WHEN ${lowSignalIsMatchExpr} THEN 1 ELSE 0 END), 0) as low_signal,
35
+ COALESCE(SUM(CASE WHEN type = 'bugfix' THEN 1 ELSE 0 END), 0) as bugfix_total,
36
+ COALESCE(SUM(CASE WHEN type = 'bugfix' AND ${unresolvedNarrativeExpr} THEN 1 ELSE 0 END), 0) as bugfix_unresolved
37
37
  FROM observations
38
38
  WHERE created_at_epoch >= ? ${projectFilter}
39
39
  `).get(cutoff, ...baseParams);
@@ -41,8 +41,8 @@ export function computeQualityStats(db, { project, days }) {
41
41
  const allTimeRow = db.prepare(`
42
42
  SELECT
43
43
  COUNT(*) as total,
44
- SUM(CASE WHEN lesson_learned IS NOT NULL AND lesson_learned != '' THEN 1 ELSE 0 END) as with_lesson,
45
- SUM(CASE WHEN ${lowSignalIsMatchExpr} THEN 1 ELSE 0 END) as low_signal
44
+ COALESCE(SUM(CASE WHEN lesson_learned IS NOT NULL AND lesson_learned != '' THEN 1 ELSE 0 END), 0) as with_lesson,
45
+ COALESCE(SUM(CASE WHEN ${lowSignalIsMatchExpr} THEN 1 ELSE 0 END), 0) as low_signal
46
46
  FROM observations
47
47
  WHERE 1=1 ${projectFilter}
48
48
  `).get(...baseParams);
@@ -51,8 +51,8 @@ export function computeQualityStats(db, { project, days }) {
51
51
  SELECT
52
52
  type,
53
53
  COUNT(*) as total,
54
- SUM(CASE WHEN COALESCE(access_count, 0) > 0 THEN 1 ELSE 0 END) as accessed,
55
- SUM(CASE WHEN lesson_learned IS NOT NULL AND lesson_learned != '' THEN 1 ELSE 0 END) as with_lesson
54
+ COALESCE(SUM(CASE WHEN COALESCE(access_count, 0) > 0 THEN 1 ELSE 0 END), 0) as accessed,
55
+ COALESCE(SUM(CASE WHEN lesson_learned IS NOT NULL AND lesson_learned != '' THEN 1 ELSE 0 END), 0) as with_lesson
56
56
  FROM observations
57
57
  WHERE created_at_epoch >= ? ${projectFilter}
58
58
  GROUP BY type
@@ -75,8 +75,8 @@ export function computeQualityStats(db, { project, days }) {
75
75
  // age > 37d, so a sudden write surge inflates this until the cohort ages out.
76
76
  const purgeRow = db.prepare(`
77
77
  SELECT
78
- SUM(CASE WHEN compressed_into IS NOT NULL AND compressed_into != 0 THEN 1 ELSE 0 END) as compressed,
79
- SUM(CASE WHEN compressed_into = ${COMPRESSED_PENDING_PURGE} THEN 1 ELSE 0 END) as pending_purge
78
+ COALESCE(SUM(CASE WHEN compressed_into IS NOT NULL AND compressed_into != 0 THEN 1 ELSE 0 END), 0) as compressed,
79
+ COALESCE(SUM(CASE WHEN compressed_into = ${COMPRESSED_PENDING_PURGE} THEN 1 ELSE 0 END), 0) as pending_purge
80
80
  FROM observations
81
81
  WHERE 1=1 ${projectFilter}
82
82
  `).get(...baseParams);
package/mem-cli.mjs CHANGED
@@ -1832,12 +1832,12 @@ function cmdMaintain(db, args) {
1832
1832
  const stats = db.prepare(`
1833
1833
  SELECT
1834
1834
  COUNT(*) as total,
1835
- SUM(CASE WHEN COALESCE(importance, 1) = 1 AND COALESCE(access_count, 0) = 0
1836
- AND created_at_epoch < ? THEN 1 ELSE 0 END) as stale,
1837
- SUM(CASE WHEN (title IS NULL OR title = '') AND (narrative IS NULL OR narrative = '')
1838
- THEN 1 ELSE 0 END) as broken,
1839
- SUM(CASE WHEN COALESCE(access_count, 0) > 3 AND COALESCE(importance, 1) < 3
1840
- THEN 1 ELSE 0 END) as boostable
1835
+ COALESCE(SUM(CASE WHEN COALESCE(importance, 1) = 1 AND COALESCE(access_count, 0) = 0
1836
+ AND created_at_epoch < ? THEN 1 ELSE 0 END), 0) as stale,
1837
+ COALESCE(SUM(CASE WHEN (title IS NULL OR title = '') AND (narrative IS NULL OR narrative = '')
1838
+ THEN 1 ELSE 0 END), 0) as broken,
1839
+ COALESCE(SUM(CASE WHEN COALESCE(access_count, 0) > 3 AND COALESCE(importance, 1) < 3
1840
+ THEN 1 ELSE 0 END), 0) as boostable
1841
1841
  FROM observations
1842
1842
  WHERE COALESCE(compressed_into, 0) = 0 ${projectFilter}
1843
1843
  `).get(staleAge, ...baseParams);
@@ -2421,6 +2421,13 @@ Commands:
2421
2421
  import-jsonl <file-or-dir> Import Claude Code JSONL transcripts (cold-start backfill)
2422
2422
  --project P Project name (default: inferred from cwd)
2423
2423
 
2424
+ import <github-url> Import skills/agents into the resource registry from a GitHub repo
2425
+ --enrich Auto-enrich each imported resource with a Haiku capability summary
2426
+
2427
+ enrich <name> Re-enrich a single registry resource (Haiku capability summary)
2428
+ --all Enrich every active resource missing or failed enrichment
2429
+ --batch Skip the inter-call delay (use only with low rate-limit risk)
2430
+
2424
2431
  activity <action> Non-memdir event log (v2.31) — bugfix/lesson/bug/discovery/etc.
2425
2432
  save --type T "<title>" [--body "<text>"] [--files f1,f2] [--file path] [--importance 1-3] [--project P]
2426
2433
  search "<query>" Search events [--type T] [--limit N] [--project P]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "2.71.1",
3
+ "version": "2.71.3",
4
4
  "description": "Lightweight persistent memory system for Claude Code",
5
5
  "type": "module",
6
6
  "packageManager": "npm@10.9.2",
package/schema.mjs CHANGED
@@ -181,6 +181,21 @@ export function initSchema(db) {
181
181
  if (e.message?.startsWith('DB schema is v')) throw e;
182
182
  }
183
183
 
184
+ // Concurrent-init guard: serialize schema setup against peer processes via
185
+ // BEGIN IMMEDIATE (busy_timeout=3000 from ensureDb makes peers wait). Required
186
+ // because the sdk_sessions_id_mix_check_{ai,au} migration uses DROP+CREATE
187
+ // without IF NOT EXISTS to update the trigger body, which races at cold-start.
188
+ // Re-check schema_version under the lock — a peer may have completed init
189
+ // while we were blocked. Connection close auto-rollbacks if body throws.
190
+ db.exec('BEGIN IMMEDIATE');
191
+ try {
192
+ const underlock = db.prepare('SELECT version FROM schema_version LIMIT 1').get();
193
+ if (underlock && underlock.version === CURRENT_SCHEMA_VERSION) {
194
+ db.exec('COMMIT');
195
+ return db;
196
+ }
197
+ } catch { /* table absent — proceed */ }
198
+
184
199
  // Create core tables
185
200
  db.exec(CORE_SCHEMA);
186
201
 
@@ -264,7 +279,6 @@ export function initSchema(db) {
264
279
  });
265
280
  dedupAndIndex();
266
281
  }
267
- db.pragma('foreign_keys = ON');
268
282
 
269
283
  // Performance indexes
270
284
  db.exec(`CREATE INDEX IF NOT EXISTS idx_obs_epoch_project ON observations(created_at_epoch DESC, project)`);
@@ -580,6 +594,10 @@ export function initSchema(db) {
580
594
  db.prepare('INSERT INTO schema_version (version) VALUES (?)').run(CURRENT_SCHEMA_VERSION);
581
595
  })();
582
596
 
597
+ db.exec('COMMIT');
598
+ // PRAGMA foreign_keys must run OUTSIDE the transaction (no-op inside).
599
+ db.pragma('foreign_keys = ON');
600
+
583
601
  return db;
584
602
  }
585
603
 
@@ -9,10 +9,12 @@ import { basename, join } from 'path';
9
9
  import { homedir } from 'os';
10
10
  import { buildNotLowSignalSql } from '../lib/low-signal-patterns.mjs';
11
11
 
12
- // CLAUDE_MEM_DB_PATH / CLAUDE_MEM_RUNTIME_DIR env overrides allow tests and debug tools to
13
- // point the hook at an isolated DB + cooldown dir without touching the user's real state.
14
- const DB_PATH = process.env.CLAUDE_MEM_DB_PATH || join(homedir(), '.claude-mem-lite', 'claude-mem-lite.db');
15
- const RUNTIME_DIR = process.env.CLAUDE_MEM_RUNTIME_DIR || join(homedir(), '.claude-mem-lite', 'runtime');
12
+ // CLAUDE_MEM_DIR matches schema.mjs / main CLI one env var sandboxes the
13
+ // whole system. CLAUDE_MEM_DB_PATH / CLAUDE_MEM_RUNTIME_DIR remain as
14
+ // per-component overrides for tests that mix isolated + real paths.
15
+ const DATA_DIR = process.env.CLAUDE_MEM_DIR || join(homedir(), '.claude-mem-lite');
16
+ const DB_PATH = process.env.CLAUDE_MEM_DB_PATH || join(DATA_DIR, 'claude-mem-lite.db');
17
+ const RUNTIME_DIR = process.env.CLAUDE_MEM_RUNTIME_DIR || join(DATA_DIR, 'runtime');
16
18
  // v2.33.1: cooldown path is session-scoped so same-file-twice within one
17
19
  // session never re-injects (was: global file, 5-min window). Cross-session:
18
20
  // fresh file, fresh nudges — this is intended. No session_id → fall back to
package/server.mjs CHANGED
@@ -1329,12 +1329,12 @@ server.registerTool(
1329
1329
  const stats = db.prepare(`
1330
1330
  SELECT
1331
1331
  COUNT(*) as total,
1332
- SUM(CASE WHEN COALESCE(importance, 1) = 1 AND COALESCE(access_count, 0) = 0
1333
- AND created_at_epoch < ? THEN 1 ELSE 0 END) as stale,
1334
- SUM(CASE WHEN (title IS NULL OR title = '') AND (narrative IS NULL OR narrative = '')
1335
- THEN 1 ELSE 0 END) as broken,
1336
- SUM(CASE WHEN COALESCE(access_count, 0) > 3 AND COALESCE(importance, 1) < 3
1337
- THEN 1 ELSE 0 END) as boostable
1332
+ COALESCE(SUM(CASE WHEN COALESCE(importance, 1) = 1 AND COALESCE(access_count, 0) = 0
1333
+ AND created_at_epoch < ? THEN 1 ELSE 0 END), 0) as stale,
1334
+ COALESCE(SUM(CASE WHEN (title IS NULL OR title = '') AND (narrative IS NULL OR narrative = '')
1335
+ THEN 1 ELSE 0 END), 0) as broken,
1336
+ COALESCE(SUM(CASE WHEN COALESCE(access_count, 0) > 3 AND COALESCE(importance, 1) < 3
1337
+ THEN 1 ELSE 0 END), 0) as boostable
1338
1338
  FROM observations
1339
1339
  WHERE COALESCE(compressed_into, 0) = 0 ${projectFilter}
1340
1340
  `).get(staleAge, ...baseParams);