claude-mem-lite 2.32.7 → 2.32.8

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.32.7",
13
+ "version": "2.32.8",
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.32.7",
3
+ "version": "2.32.8",
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-mem-lite",
3
- "version": "2.32.7",
3
+ "version": "2.32.8",
4
4
  "description": "Lightweight persistent memory system for Claude Code",
5
5
  "type": "module",
6
6
  "engines": {
@@ -49,8 +49,8 @@ export const INTENTS = [
49
49
  // CJK: 开发/编写/创建/构建/做一个/写一个 from real prompts
50
50
  { pattern: /implement|feature\b|add\s+(?:a\s+)?new|实现|添加|新功能|新增|开发|编写|创建|构建|做一个|加一个|写一个/i, type: null, limit: 3 },
51
51
  // Recall/history intent (catch-all temporal, lowest priority)
52
- // CJK: 刚才/历史/回顾 from real prompts
53
- { pattern: /before|previously|last time|remember|之前|上次|以前|记得|刚才|历史|回顾/i, type: null, limit: 5, useRecent: true },
52
+ // CJK: 刚才/历史/回顾 from real prompts; 碰到过|遇到过|见过|同样的问题 from spoken CN
53
+ { pattern: /before|previously|last time|remember|seen this|same\s+issue|之前|上次|以前|记得|刚才|历史|回顾|碰到过|遇到过|见过|同样的问题|类似的问题/i, type: null, limit: 5, useRecent: true },
54
54
  ];
55
55
 
56
56
  export function detectIntent(text) {
@@ -75,6 +75,44 @@ export function detectIntent(text) {
75
75
  return first;
76
76
  }
77
77
 
78
+ // ─── Error Signature Extraction ─────────────────────────────────────────────
79
+
80
+ /**
81
+ * Extract a canonical error signature from prompt text.
82
+ *
83
+ * Matches named exception/error classes like:
84
+ * - "TypeError: Cannot read properties of undefined (reading 'foo')"
85
+ * - "Error [ERR_MODULE_NOT_FOUND]: module X not found"
86
+ * - "AssertionError: expected 'a' to equal 'b'"
87
+ * - "ValueError: invalid literal for int()"
88
+ * - "thread 'main' panicked at ..." (Rust) → captured via Panic class
89
+ *
90
+ * Intentionally skips bare "Error: ..." without a typed class, and skips
91
+ * lowercase matches — those carry too little signal vs. the intent-based
92
+ * FTS path which already catches them.
93
+ *
94
+ * Returns { className, errorCode, message, signature } or null.
95
+ * `signature` is suitable for direct FTS5 search (sanitizeFtsQuery applies).
96
+ */
97
+ export function extractErrorSignature(text) {
98
+ if (!text || typeof text !== 'string') return null;
99
+ // Pass 1: typed class — "<CapCase>(Error|Exception|Panic)" with optional [ERR_CODE]
100
+ const TYPED_RE = /\b([A-Z][A-Za-z0-9]+(?:Error|Exception|Panic))(?:\s*\[([A-Z_][A-Z0-9_]*)\])?\s*:\s*([^\n]{3,200})/;
101
+ // Pass 2: bare "Error|Exception|Panic" followed by required [ERR_CODE] (Node idiom).
102
+ // Bare class without a code is skipped — too noisy; intent-based path catches those.
103
+ const BARE_CODED_RE = /\b(Error|Exception|Panic)\s*\[([A-Z_][A-Z0-9_]*)\]\s*:\s*([^\n]{3,200})/;
104
+ const m = text.match(TYPED_RE) || text.match(BARE_CODED_RE);
105
+ if (!m) return null;
106
+ const className = m[1];
107
+ const errorCode = m[2] || null;
108
+ const message = m[3].trim().replace(/\s+/g, ' ').replace(/[`'"]+$/, '');
109
+ const sigMsg = message.slice(0, 80);
110
+ const signature = errorCode
111
+ ? `${className} ${errorCode} ${sigMsg}`
112
+ : `${className} ${sigMsg}`;
113
+ return { className, errorCode, message, signature };
114
+ }
115
+
78
116
  // ─── Result Dedup ───────────────────────────────────────────────────────────
79
117
 
80
118
  export const MAX_SESSION_INJECTIONS = 15;
@@ -8,7 +8,7 @@ import { sanitizeFtsQuery, relaxFtsQueryToOr, truncate, typeIcon, inferProject,
8
8
  import { writeFileSync, readFileSync, existsSync, renameSync } from 'fs';
9
9
  import { join } from 'path';
10
10
  import Database from 'better-sqlite3';
11
- import { shouldSkip, detectIntent, shouldSkipByDedup, extractFiles, DEDUP_STALE_MS, matchRegistrySkillName } from './prompt-search-utils.mjs';
11
+ import { shouldSkip, detectIntent, shouldSkipByDedup, extractFiles, extractErrorSignature, DEDUP_STALE_MS, matchRegistrySkillName } from './prompt-search-utils.mjs';
12
12
 
13
13
  // ─── Constants ──────────────────────────────────────────────────────────────
14
14
 
@@ -267,6 +267,18 @@ async function main() {
267
267
  const intent = detectIntent(promptText);
268
268
  let rows = [];
269
269
 
270
+ // A (v2.32.8): precision pass for named errors. When the prompt contains
271
+ // a typed exception signature (TypeError/ValueError/ReferenceError/...),
272
+ // seed results with exact-match bugfix observations before the intent-
273
+ // based FTS flow runs. These hits are the most directly relevant and
274
+ // take priority slots in the merged output.
275
+ const errSig = extractErrorSignature(promptText);
276
+ const sigRows = errSig
277
+ ? searchByFts(db, errSig.signature, project, 2, 'bugfix').filter(r =>
278
+ typeof r.relevance === 'number' && Math.abs(r.relevance) >= BM25_MIN_SCORE
279
+ )
280
+ : [];
281
+
270
282
  if (intent?.useRecent) {
271
283
  // Recall intent: show recent observations
272
284
  rows = searchRecent(db, project, intent.limit);
@@ -302,6 +314,12 @@ async function main() {
302
314
  rows = rows.slice(0, MAX_RESULTS);
303
315
  }
304
316
 
317
+ // A (v2.32.8): prepend error-signature hits (higher precision), dedup, cap.
318
+ if (sigRows.length > 0) {
319
+ const sigIds = new Set(sigRows.map(r => r.id));
320
+ rows = [...sigRows, ...rows.filter(r => !sigIds.has(r.id))].slice(0, MAX_RESULTS);
321
+ }
322
+
305
323
  const candidateIds = rows.map(r => r.id);
306
324
  const dedupSkip = shouldSkipByDedup(candidateIds, INJECTED_IDS_FILE);
307
325