claude-recall 0.18.7 → 0.18.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.
File without changes
@@ -1,48 +1 @@
1
- {
2
- "hooks": {
3
- "PreToolUse": [
4
- {
5
- "matcher": ".*",
6
- "hooks": [
7
- {
8
- "type": "command",
9
- "command": "python3 /home/ebiarao/repos-wsl/personal-projects/claude-recall/.claude/hooks/search_enforcer.py"
10
- }
11
- ]
12
- }
13
- ],
14
- "UserPromptSubmit": [
15
- {
16
- "hooks": [
17
- {
18
- "type": "command",
19
- "command": "node /home/ebiarao/repos-wsl/personal-projects/claude-recall/dist/cli/claude-recall-cli.js hook run correction-detector"
20
- }
21
- ]
22
- }
23
- ],
24
- "Stop": [
25
- {
26
- "hooks": [
27
- {
28
- "type": "command",
29
- "command": "node /home/ebiarao/repos-wsl/personal-projects/claude-recall/dist/cli/claude-recall-cli.js hook run memory-stop",
30
- "timeout": 30
31
- }
32
- ]
33
- }
34
- ],
35
- "PreCompact": [
36
- {
37
- "hooks": [
38
- {
39
- "type": "command",
40
- "command": "node /home/ebiarao/repos-wsl/personal-projects/claude-recall/dist/cli/claude-recall-cli.js hook run precompact-preserve",
41
- "timeout": 60
42
- }
43
- ]
44
- }
45
- ]
46
- },
47
- "hooksVersion": "6.0.0"
48
- }
1
+ {"hooks": {}, "hooksVersion": "1.0.0"}
@@ -8,7 +8,7 @@ source: claude-recall
8
8
 
9
9
  # Corrections
10
10
 
11
- Auto-generated from 17 memories. Last updated: 2026-03-21.
11
+ Auto-generated from 20 memories. Last updated: 2026-03-22.
12
12
 
13
13
  ## Rules
14
14
 
@@ -22,8 +22,11 @@ Auto-generated from 17 memories. Last updated: 2026-03-21.
22
22
  - CORRECTION: Memory with complex metadata
23
23
  - CORRECTION: Memory with complex metadata
24
24
  - CORRECTION: Memory with complex metadata
25
+ - CORRECTION: Memory with complex metadata
26
+ - CORRECTION: License copyright should include user's name instead of 'Claude Recall Contributors'
25
27
  - CORRECTION: Replace expired access token with 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
26
28
  - CORRECTION: use spaces not tabs for indentation
29
+ - CORRECTION: License copyright should list your name instead of 'Claude Recall Contributors'
27
30
  - CORRECTION: cited (loaded 5+ times): 19
28
31
  - CORRECTION: cited (loaded 5+ times): 19
29
32
  - CORRECTION: cited (loaded 5+ times): 19
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "topicId": "corrections",
3
- "sourceHash": "2c220ace8666e38374662c19a60f76118e125327cc273019414c6e8383e5e120",
4
- "memoryCount": 17,
5
- "generatedAt": "2026-03-21T15:18:51.279Z",
3
+ "sourceHash": "be94895a3ad6a5999508be2c149a1a2742178fff9f9adc97b5da6185c9a30398",
4
+ "memoryCount": 20,
5
+ "generatedAt": "2026-03-22T15:58:29.733Z",
6
6
  "memoryKeys": [
7
+ "memory_1774195109722_gij039r4m",
8
+ "memory_1774191618092_vuxyvq3mw",
7
9
  "memory_1774106331272_kg5w7ztfj",
8
10
  "memory_1774104588311_65kg08e05",
9
- "memory_1774020789453_x9xkjhbug",
10
11
  "memory_1773140547102_2wmy0cfga",
11
12
  "memory_1773065886725_rscm7v2qx",
12
13
  "memory_1773063478046_x0ryr9bk4",
@@ -14,8 +15,10 @@
14
15
  "memory_1772641994141_ddvzwdkd9",
15
16
  "memory_1772641570519_wmnb2b08w",
16
17
  "memory_1772641026962_tqm8ow04r",
18
+ "hook_correction_1774180798682_9b5a2hadq",
17
19
  "hook_correction_1772101108419_le80cln1w",
18
20
  "hook_correction_1771112125882_99ihypf8x",
21
+ "hook_correction_1774180805192_4ft4zhfsa",
19
22
  "hook_correction_1772638229134_otn9za2il",
20
23
  "hook_correction_1772636083381_th3eluwzv",
21
24
  "hook_correction_1772635851763_y91ugnjgy",
@@ -8,14 +8,12 @@ source: claude-recall
8
8
 
9
9
  # Failure Lessons
10
10
 
11
- Auto-generated from 26 memories. Last updated: 2026-03-21.
11
+ Auto-generated from 24 memories. Last updated: 2026-03-22.
12
12
 
13
13
  ## Rules
14
14
 
15
- - Check command syntax, file paths, and prerequisites before running
16
15
  - SQLite query syntax error: LIKE clause requires single quotes around string literal, not double quotes
17
16
  - Avoid: Command failed: claude-recall outcomes 2>&1 → Instead: Check command syntax, file paths, and prerequisites before running
18
- - Avoid: Command failed: npx jest tests/unit/outcome-storage.test.ts tests/unit/promotion-engine.test.ts tests/unit/memory... → Instead: Check command syntax, file paths, and prerequisites before running
19
17
  - Avoid: Command failed: npm whoami 2>&1 && npm config get registry 2>&1 → Instead: Check command syntax, file paths, and prerequisites before running
20
18
  - npm install -g claude-recall@0.15.36 failed with notarget error
21
19
  - claude-recall@0.15.36 does not exist on npm registry (ETARGET error)
@@ -1,13 +1,11 @@
1
1
  {
2
2
  "topicId": "failure-lessons",
3
- "sourceHash": "577fce05d7ff95d6661cf78c93b84ce7a8ff54d22964456ca73c045bac9c0986",
4
- "memoryCount": 26,
5
- "generatedAt": "2026-03-21T15:18:51.248Z",
3
+ "sourceHash": "00deb7a6c6d99c26e0d29a6a707b4c303dcb19731d27a8ce41db09fd2ff3eb55",
4
+ "memoryCount": 24,
5
+ "generatedAt": "2026-03-22T15:00:18.049Z",
6
6
  "memoryKeys": [
7
- "promoted_1774019382782_acm9j0h8u",
8
7
  "hook_failure_1772637584921_0tj4rrxnt",
9
8
  "hook_failure_non-zero-exit_1774020949485_6ubuoswae",
10
- "hook_failure_non-zero-exit_1773999564001_82kcdp0ag",
11
9
  "hook_failure_non-zero-exit_1773409269877_ful451241",
12
10
  "hook_failure_1773410916808_5k1r6zo4u",
13
11
  "hook_failure_1773410916789_xtrb8j9nw",
@@ -8,10 +8,19 @@ source: claude-recall
8
8
 
9
9
  # Preferences
10
10
 
11
- Auto-generated from 16 memories. Last updated: 2026-03-21.
11
+ Auto-generated from 21 memories. Last updated: 2026-03-22.
12
12
 
13
13
  ## Rules
14
14
 
15
+ - Test preference 1774195109742-2
16
+ - Test preference 1774195109742-1
17
+ - Test preference 1774195109742-0
18
+ - Test memory content
19
+ - Test preference 1774191618111-2
20
+ - Test preference 1774191618111-1
21
+ - Test preference 1774191618111-0
22
+ - Test memory content
23
+ - a normal preference
15
24
  - Session test preference 1774106331339
16
25
  - Test preference 1774106331283-2
17
26
  - Test preference 1774106331283-1
@@ -23,11 +32,7 @@ Auto-generated from 16 memories. Last updated: 2026-03-21.
23
32
  - Test preference 1774104588325-0
24
33
  - Test memory content
25
34
  - Session test preference 1774020789925
26
- - Test preference 1774020789520-2
27
- - Test preference 1774020789520-1
28
- - Test preference 1774020789520-0
29
- - Test memory content
30
- - Session test preference 1773999187736
35
+ - Upgrade all projects whenever a new version is pushed
31
36
 
32
37
  ---
33
38
  *Auto-generated by Claude Recall. Regenerate: `npx claude-recall skills generate`*
@@ -1,9 +1,18 @@
1
1
  {
2
2
  "topicId": "preferences",
3
- "sourceHash": "7123394f8e4ec18013e3a3c3d5f2a4761f1a0c14c20401f566541901de6f797c",
4
- "memoryCount": 16,
5
- "generatedAt": "2026-03-21T15:18:51.351Z",
3
+ "sourceHash": "29117b36ff390c0168d551c7f821d92567c91e252995d3786e0f0fd7462fc548",
4
+ "memoryCount": 21,
5
+ "generatedAt": "2026-03-22T15:58:29.796Z",
6
6
  "memoryKeys": [
7
+ "memory_1774195109780_8ffmflge1",
8
+ "memory_1774195109763_v6olh83ct",
9
+ "memory_1774195109743_i79j9a9rl",
10
+ "memory_1774195109624_nnh3wrwca",
11
+ "memory_1774191618164_wefq7s9x6",
12
+ "memory_1774191618139_k77izvxnq",
13
+ "memory_1774191618112_hzi64751y",
14
+ "memory_1774191617980_pzspevoct",
15
+ "valid1",
7
16
  "memory_1774106331340_cwt88yhle",
8
17
  "memory_1774106331311_491hg2a21",
9
18
  "memory_1774106331297_0b6bvacd7",
@@ -15,10 +24,6 @@
15
24
  "memory_1774104588325_g4o5ksubl",
16
25
  "memory_1774104588255_fy0wv845g",
17
26
  "memory_1774020789927_g99mrubvu",
18
- "memory_1774020789705_xhubujrxv",
19
- "memory_1774020789618_m1p55yhtz",
20
- "memory_1774020789521_saryqj7kw",
21
- "memory_1774020789257_wla5q38wq",
22
- "memory_1773999187737_qrdwbas47"
27
+ "hook_preference_1774106575282_45sk4ep52"
23
28
  ]
24
29
  }
package/LICENSE CHANGED
File without changes
@@ -123,7 +123,7 @@ async function handleBashFailureWatcher(input) {
123
123
  }
124
124
  }
125
125
  catch (err) {
126
- (0, shared_1.hookLog)(HOOK_NAME, `Error: ${err}`);
126
+ (0, shared_1.hookLog)(HOOK_NAME, `Error: ${(0, shared_1.safeErrorMessage)(err)}`);
127
127
  // Never throw — hooks must not block Claude
128
128
  }
129
129
  }
@@ -166,7 +166,7 @@ async function handleFailure(command, exitCode, output, sessionId) {
166
166
  });
167
167
  }
168
168
  catch (err) {
169
- (0, shared_1.hookLog)(HOOK_NAME, `Outcome event error: ${err}`);
169
+ (0, shared_1.hookLog)(HOOK_NAME, `Outcome event error: ${(0, shared_1.safeErrorMessage)(err)}`);
170
170
  }
171
171
  (0, shared_1.hookLog)(HOOK_NAME, `Stored failure: ${truncate(command, 60)} (exit ${exitCode})`);
172
172
  // Track pending failure for fix pairing
@@ -215,14 +215,14 @@ async function handleSuccess(command, sessionId) {
215
215
  });
216
216
  }
217
217
  catch (err) {
218
- (0, shared_1.hookLog)(HOOK_NAME, `Positive outcome event error: ${err}`);
218
+ (0, shared_1.hookLog)(HOOK_NAME, `Positive outcome event error: ${(0, shared_1.safeErrorMessage)(err)}`);
219
219
  }
220
220
  (0, shared_1.hookLog)(HOOK_NAME, `Paired fix: "${truncate(command, 60)}" → ${pf.memoryKey}`);
221
221
  matched = true;
222
222
  // Don't add to remaining — consumed
223
223
  }
224
224
  catch (err) {
225
- (0, shared_1.hookLog)(HOOK_NAME, `Fix pairing error: ${err}`);
225
+ (0, shared_1.hookLog)(HOOK_NAME, `Fix pairing error: ${(0, shared_1.safeErrorMessage)(err)}`);
226
226
  remaining.push(pf);
227
227
  }
228
228
  }
@@ -40,7 +40,7 @@ async function handleCorrectionDetector(input) {
40
40
  }
41
41
  }
42
42
  catch (err) {
43
- (0, shared_1.hookLog)('correction-detector', `Reask signal detection error: ${err}`);
43
+ (0, shared_1.hookLog)('correction-detector', `Reask signal detection error: ${(0, shared_1.safeErrorMessage)(err)}`);
44
44
  }
45
45
  const result = await (0, shared_1.classifyContent)(prompt);
46
46
  if (!result)
@@ -127,7 +127,18 @@ async function handleMemoryStop(input) {
127
127
  }
128
128
  }
129
129
  catch (err) {
130
- (0, shared_1.hookLog)('memory-stop', `Promotion error: ${err}`);
130
+ (0, shared_1.hookLog)('memory-stop', `Promotion error: ${(0, shared_1.safeErrorMessage)(err)}`);
131
+ }
132
+ // Prune old outcome data to prevent unbounded table growth
133
+ try {
134
+ const pruned = outcomeStorage.pruneOldData();
135
+ const total = pruned.episodes + pruned.events + pruned.lessons + pruned.stats;
136
+ if (total > 0) {
137
+ (0, shared_1.hookLog)('memory-stop', `Pruned: ${pruned.episodes} episodes, ${pruned.events} events, ${pruned.lessons} lessons, ${pruned.stats} orphaned stats`);
138
+ }
139
+ }
140
+ catch (err) {
141
+ (0, shared_1.hookLog)('memory-stop', `Prune error: ${(0, shared_1.safeErrorMessage)(err)}`);
131
142
  }
132
143
  }
133
144
  /**
@@ -331,7 +342,7 @@ function generateCandidateLessons(failures, episodeId, projectId) {
331
342
  }
332
343
  }
333
344
  catch (err) {
334
- (0, shared_1.hookLog)('memory-stop', `Candidate lesson generation error: ${err}`);
345
+ (0, shared_1.hookLog)('memory-stop', `Candidate lesson generation error: ${(0, shared_1.safeErrorMessage)(err)}`);
335
346
  }
336
347
  }
337
348
  function extractTagsFromContext(context) {
@@ -41,6 +41,7 @@ exports.jaccardSimilarity = jaccardSimilarity;
41
41
  exports.isDuplicate = isDuplicate;
42
42
  exports.storeMemory = storeMemory;
43
43
  exports.searchExisting = searchExisting;
44
+ exports.safeErrorMessage = safeErrorMessage;
44
45
  exports.hookLog = hookLog;
45
46
  exports.readTranscriptTail = readTranscriptTail;
46
47
  exports.isUserEntry = isUserEntry;
@@ -193,6 +194,16 @@ function searchExisting(query) {
193
194
  const memoryService = memory_1.MemoryService.getInstance();
194
195
  return memoryService.search(query);
195
196
  }
197
+ /**
198
+ * Extract a safe error message without exposing stack traces or internal paths.
199
+ */
200
+ function safeErrorMessage(err) {
201
+ if (err instanceof Error)
202
+ return err.message;
203
+ if (typeof err === 'string')
204
+ return err;
205
+ return 'unknown error';
206
+ }
196
207
  /**
197
208
  * Append a log line to ~/.claude-recall/hook-logs/{hookName}.log
198
209
  */
@@ -297,8 +297,23 @@ class MemoryTools {
297
297
  if (lowCompliance.length > 0) {
298
298
  sections.push('## Rule Health\nThese rules are loaded frequently but never cited — consider rewording or removing:\n' +
299
299
  lowCompliance.map(r => {
300
- const val = typeof r.value === 'string' ? (JSON.parse(r.value)?.content || r.value) : r.value;
301
- return `- "${String(val).substring(0, 80)}" (loaded ${r.load_count}x, cited 0x)`;
300
+ let val;
301
+ if (typeof r.value === 'string') {
302
+ try {
303
+ const parsed = JSON.parse(r.value);
304
+ val = typeof parsed === 'string' ? parsed : (parsed?.content || parsed?.value || r.value);
305
+ }
306
+ catch {
307
+ val = r.value;
308
+ }
309
+ }
310
+ else if (typeof r.value === 'object' && r.value !== null) {
311
+ val = r.value.content || r.value.value || JSON.stringify(r.value);
312
+ }
313
+ else {
314
+ val = String(r.value ?? '');
315
+ }
316
+ return `- "${val.substring(0, 80)}" (loaded ${r.load_count}x, cited 0x)`;
302
317
  }).join('\n'));
303
318
  }
304
319
  const totalRules = rules.preferences.length + rules.corrections.length +
@@ -193,6 +193,22 @@ class OutcomeStorage {
193
193
  times_unhelpful = times_unhelpful + 1
194
194
  `).run(key);
195
195
  }
196
+ /**
197
+ * Prune old data from outcome tables to prevent unbounded growth.
198
+ * - Episodes older than 90 days
199
+ * - Outcome events older than 90 days
200
+ * - Rejected/archived candidate lessons older than 14 days
201
+ * - Orphaned memory_stats entries (key no longer in memories table)
202
+ */
203
+ pruneOldData() {
204
+ const cutoff90 = new Date(Date.now() - 90 * 24 * 60 * 60 * 1000).toISOString();
205
+ const cutoff14 = new Date(Date.now() - 14 * 24 * 60 * 60 * 1000).toISOString();
206
+ const episodes = this.db.prepare('DELETE FROM episodes WHERE created_at < ?').run(cutoff90).changes;
207
+ const events = this.db.prepare('DELETE FROM outcome_events WHERE created_at < ?').run(cutoff90).changes;
208
+ const lessons = this.db.prepare("DELETE FROM candidate_lessons WHERE status IN ('rejected', 'archived') AND updated_at < ?").run(cutoff14).changes;
209
+ const stats = this.db.prepare('DELETE FROM memory_stats WHERE memory_key NOT IN (SELECT key FROM memories)').run().changes;
210
+ return { episodes, events, lessons, stats };
211
+ }
196
212
  }
197
213
  exports.OutcomeStorage = OutcomeStorage;
198
214
  // --- Helpers ---
@@ -34,13 +34,18 @@ class PromotionEngine {
34
34
  let promoted = 0;
35
35
  let archived = 0;
36
36
  for (const candidate of candidates) {
37
- if (this.shouldPromote(candidate)) {
38
- this.promote(candidate.id);
39
- promoted++;
37
+ try {
38
+ if (this.shouldPromote(candidate)) {
39
+ this.promote(candidate.id);
40
+ promoted++;
41
+ }
42
+ else if (this.shouldReject(candidate)) {
43
+ outcomeStorage.updateLessonStatus(candidate.id, 'rejected');
44
+ archived++;
45
+ }
40
46
  }
41
- else if (this.shouldReject(candidate)) {
42
- outcomeStorage.updateLessonStatus(candidate.id, 'rejected');
43
- archived++;
47
+ catch (err) {
48
+ console.error(`PromotionEngine: failed to process candidate ${candidate.id}`);
44
49
  }
45
50
  }
46
51
  // Check for demotion of existing memories
@@ -108,7 +113,13 @@ class PromotionEngine {
108
113
  // Not retrieved in 90 days with low strength
109
114
  if (stats.last_retrieved_at) {
110
115
  const daysSinceRetrieved = (Date.now() - new Date(stats.last_retrieved_at).getTime()) / (1000 * 60 * 60 * 24);
111
- const strength = retrieval_1.MemoryRetrieval.computeStrength(memory);
116
+ let strength = 0;
117
+ try {
118
+ strength = retrieval_1.MemoryRetrieval.computeStrength(memory);
119
+ }
120
+ catch {
121
+ strength = 0;
122
+ }
112
123
  if (daysSinceRetrieved > 90 && strength < 0.2) {
113
124
  return true;
114
125
  }
@@ -159,8 +170,8 @@ class PromotionEngine {
159
170
  }
160
171
  }
161
172
  }
162
- catch {
163
- // Non-critical
173
+ catch (err) {
174
+ console.error('PromotionEngine: demotion sweep failed');
164
175
  }
165
176
  return archived;
166
177
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-recall",
3
- "version": "0.18.7",
3
+ "version": "0.18.8",
4
4
  "description": "Persistent memory for Claude Code with native Skills integration, automatic capture, failure learning, and project scoping via MCP server",
5
5
  "main": "dist/index.js",
6
6
  "bin": {