claude-recall 0.20.13 → 0.20.15

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.
@@ -8,14 +8,14 @@ source: claude-recall
8
8
 
9
9
  # Preferences
10
10
 
11
- Auto-generated from 5 memories. Last updated: 2026-04-09.
11
+ Auto-generated from 5 memories. Last updated: 2026-04-10.
12
12
 
13
13
  ## Rules
14
14
 
15
- - Session test preference 1775768726677
16
- - Test preference 1775768726629-2
17
- - Test preference 1775768726629-1
18
- - Test preference 1775768726629-0
15
+ - Session test preference 1775821892450
16
+ - Test preference 1775821892400-2
17
+ - Test preference 1775821892400-1
18
+ - Test preference 1775821892400-0
19
19
  - Test memory content
20
20
 
21
21
  ---
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "topicId": "preferences",
3
- "sourceHash": "6deab3a85a84a13804c09fd95225d54f4d11e646d8109ec83cd968d1b7af771e",
3
+ "sourceHash": "9b2ae714e4bdd82091b4fcc5347c2b1a8a2a10d3be57ca57877e943e380a5457",
4
4
  "memoryCount": 5,
5
- "generatedAt": "2026-04-09T21:05:26.692Z",
5
+ "generatedAt": "2026-04-10T11:51:32.469Z",
6
6
  "memoryKeys": [
7
- "memory_1775768726678_aa8631cqc",
8
- "memory_1775768726653_3tkihcis0",
9
- "memory_1775768726640_ii27bz2av",
10
- "memory_1775768726630_p95bn61ba",
11
- "memory_1775768726593_fb0syx1bo"
7
+ "memory_1775821892451_dzjdxx6ef",
8
+ "memory_1775821892427_462vxb2gq",
9
+ "memory_1775821892416_8taocju9a",
10
+ "memory_1775821892402_3x8ksma5p",
11
+ "memory_1775821892372_4khhv0wc2"
12
12
  ]
13
13
  }
package/README.md CHANGED
@@ -142,17 +142,76 @@ action → outcome event → episode → candidate lesson → promotion → acti
142
142
 
143
143
  ## CLI Reference
144
144
 
145
- ### Common Commands
145
+ ### Health Check (run these first)
146
146
 
147
147
  ```bash
148
- claude-recall stats # Memory statistics
149
- claude-recall search "query" # Search memories
150
- claude-recall failures # View failure memories
151
- claude-recall outcomes # Outcome-aware learning status
152
- claude-recall outcomes --section lessons # Just candidate lessons
153
- claude-recall export backup.json # Export memories to JSON
154
- claude-recall import backup.json # Import memories from JSON
155
- claude-recall --version # Check version
148
+ claude-recall --version # Confirm installed version
149
+ claude-recall status # Installation health: hooks, MCP, DB path, project ID
150
+ claude-recall stats # What's in the DB for this project
151
+ claude-recall stats --global # What's in the DB across ALL projects
152
+ ```
153
+
154
+ ### Inspecting Memories
155
+
156
+ ```bash
157
+ claude-recall search "query" # Search current project's memories
158
+ claude-recall search "query" --global # Search across all projects
159
+ claude-recall search "query" --json # Machine-readable output
160
+ claude-recall search "query" --project <id> # Search a specific project
161
+
162
+ claude-recall failures # View failure memories (current project)
163
+ claude-recall failures --limit 20 # Show more
164
+
165
+ claude-recall outcomes # Outcome-aware learning status
166
+ claude-recall outcomes --section lessons # Just candidate lessons
167
+ claude-recall outcomes --section stats # Retrieval/helpfulness stats per memory
168
+ claude-recall outcomes --limit 20 # More items per section
169
+
170
+ claude-recall monitor # Memory search monitoring stats
171
+ ```
172
+
173
+ ### Managing Memories
174
+
175
+ ```bash
176
+ claude-recall store "content" # Store a memory (default type: preference)
177
+ claude-recall store "content" -t correction # Store with specific type
178
+ claude-recall export backup.json # Export all memories to JSON
179
+ claude-recall import backup.json # Import memories from JSON
180
+ claude-recall clear --force # Delete all memories (irreversible)
181
+ ```
182
+
183
+ ### Troubleshooting
184
+
185
+ ```bash
186
+ # "Are my hooks installed?"
187
+ claude-recall status # Shows hook registration status
188
+ claude-recall hooks check # Verify hook files exist and are valid
189
+
190
+ # "Is the MCP server running?"
191
+ claude-recall mcp status # Current project's server status
192
+ claude-recall mcp ps # List all running servers
193
+
194
+ # "Which project does this directory map to?"
195
+ claude-recall project show # Shows project ID for current directory
196
+ claude-recall project list # All registered projects
197
+
198
+ # "Why do I see memories from other projects?"
199
+ claude-recall search "query" # Scoped to current project (default)
200
+ claude-recall search "query" --global # Explicitly cross-project
201
+
202
+ # "How do I check what the DB actually contains?"
203
+ sqlite3 ~/.claude-recall/claude-recall.db "SELECT type, COUNT(*) FROM memories GROUP BY type"
204
+ sqlite3 ~/.claude-recall/claude-recall.db "SELECT type, COUNT(*) FROM memories WHERE project_id = '<id>' GROUP BY type"
205
+
206
+ # "Hook logs — what did the hooks actually do?"
207
+ tail -20 ~/.claude-recall/hook-logs/tool-outcome-watcher.log
208
+ tail -20 ~/.claude-recall/hook-logs/memory-stop.log
209
+ tail -20 ~/.claude-recall/hook-logs/correction-detector.log
210
+
211
+ # "Something is broken, start fresh"
212
+ claude-recall repair # Clean up old hooks, reinstall skills
213
+ claude-recall setup --install # Reinstall skills + hooks
214
+ claude-recall mcp cleanup --all # Stop all stale MCP servers
156
215
  ```
157
216
 
158
217
  <details>
@@ -164,11 +223,18 @@ claude-recall setup # Show activation instructions
164
223
  claude-recall setup --install # Install skills + hooks
165
224
  claude-recall status # Installation and system status
166
225
  claude-recall repair # Clean up old hooks, install skills
226
+ claude-recall hooks check # Verify hook files exist and are valid
227
+ claude-recall hooks test-enforcement # Test if search enforcer hook works
167
228
 
168
229
  # ── Memory ───────────────────────────────────────────────────────────
169
- claude-recall stats # Memory statistics
170
- claude-recall search "query" # Search memories
230
+ claude-recall stats # Memory statistics (current project)
231
+ claude-recall stats --global # Memory statistics (all projects)
232
+ claude-recall search "query" # Search memories (current project)
233
+ claude-recall search "query" --global # Search memories (all projects)
234
+ claude-recall search "query" --json # Output as JSON
235
+ claude-recall search "query" --project <id> # Search specific project
171
236
  claude-recall store "content" # Store memory directly
237
+ claude-recall store "content" -t <type> # Store with type (preference, correction, failure, devops, project-knowledge)
172
238
  claude-recall export backup.json # Export memories to JSON
173
239
  claude-recall import backup.json # Import memories from JSON
174
240
  claude-recall clear --force # Clear all memories
@@ -176,6 +242,7 @@ claude-recall failures # View failure memories
176
242
  claude-recall failures --limit 20 # Limit results
177
243
  claude-recall outcomes # Outcome-aware learning status
178
244
  claude-recall outcomes --section lessons # Just candidate lessons
245
+ claude-recall outcomes --section stats # Retrieval/helpfulness stats
179
246
  claude-recall outcomes --limit 20 # More items per section
180
247
  claude-recall monitor # Memory search monitoring stats
181
248
 
@@ -199,8 +266,16 @@ claude-recall mcp cleanup --all # Stop all servers
199
266
  claude-recall project show # Current project info
200
267
  claude-recall project list # All registered projects
201
268
  claude-recall project register # Register current project
269
+ claude-recall project unregister [id] # Unregister a project
202
270
  claude-recall project clean # Remove stale registry entries
203
271
 
272
+ # ── Database Migration ──────────────────────────────────────────────
273
+ claude-recall migrate check # Check if migration needed
274
+ claude-recall migrate schema # Show current schema version
275
+ claude-recall migrate export # Export pre-migration backup
276
+ claude-recall migrate import # Import from backup
277
+ claude-recall migrate complete # Run pending migrations
278
+
204
279
  # ── Auto-Capture Hooks (run automatically, registered via setup --install) ──
205
280
  claude-recall hook run correction-detector # UserPromptSubmit hook
206
281
  claude-recall hook run memory-stop # Stop hook
@@ -49,6 +49,7 @@ var __importStar = (this && this.__importStar) || (function () {
49
49
  Object.defineProperty(exports, "__esModule", { value: true });
50
50
  exports.handleBashFailureWatcher = void 0;
51
51
  exports.handleToolOutcomeWatcher = handleToolOutcomeWatcher;
52
+ exports.shouldCaptureFailure = shouldCaptureFailure;
52
53
  exports.handleToolFailure = handleToolFailure;
53
54
  const fs = __importStar(require("fs"));
54
55
  const path = __importStar(require("path"));
@@ -195,6 +196,37 @@ async function handleToolOutcomeWatcher(input) {
195
196
  }
196
197
  // Backward compatibility alias
197
198
  exports.handleBashFailureWatcher = handleToolOutcomeWatcher;
199
+ /**
200
+ * Quality filter: should this bash failure be captured as a memory?
201
+ * Returns false for exploratory probes, command-not-found, and other low-signal failures.
202
+ */
203
+ function shouldCaptureFailure(command, exitCode, output) {
204
+ // Very short commands (ls, cd, etc.) — not worth capturing
205
+ if (command.trim().length < 5)
206
+ return false;
207
+ // Exploratory probes: commands ending in 2>/dev/null are intentionally suppressing errors
208
+ if (/2>\s*\/dev\/null\s*$/.test(command))
209
+ return false;
210
+ // Command-existence checks: which, type, command -v
211
+ const trimmed = command.trim();
212
+ if (/^(which|type)\s+/i.test(trimmed))
213
+ return false;
214
+ if (/^command\s+-v\s+/i.test(trimmed))
215
+ return false;
216
+ // Command not found (exit code 127 or output contains "command not found")
217
+ if (exitCode === '127')
218
+ return false;
219
+ if (/command not found/i.test(output))
220
+ return false;
221
+ // No useful output — nothing to learn from
222
+ const cleanOutput = output.replace(/\(no output\)/gi, '').trim();
223
+ if (cleanOutput.length < 10 && !/^(npm|npx|node|python|pip|cargo|go|make|docker|git)\s/.test(trimmed))
224
+ return false;
225
+ // EISDIR / "Is a directory" — file read probes
226
+ if (/is a directory/i.test(output) || /EISDIR/i.test(output))
227
+ return false;
228
+ return true;
229
+ }
198
230
  // --- Bash handler (original bash-failure-watcher logic) ---
199
231
  async function handleBashOutcome(input) {
200
232
  const command = input.tool_input?.command;
@@ -215,6 +247,11 @@ async function handleBashOutcome(input) {
215
247
  }
216
248
  }
217
249
  async function handleBashFailure(command, exitCode, output, sessionId) {
250
+ // Quality filter — skip low-signal failures
251
+ if (!shouldCaptureFailure(command, exitCode, output)) {
252
+ (0, shared_1.hookLog)(HOOK_NAME, `Skipped low-quality failure: ${truncate(command, 60)} (exit ${exitCode})`);
253
+ return;
254
+ }
218
255
  // Dedup check
219
256
  const existing = (0, shared_1.searchExisting)(command);
220
257
  if ((0, shared_1.isDuplicate)(command, existing, 0.7)) {
@@ -230,6 +230,77 @@ class MemoryStorage {
230
230
  });
231
231
  return crypto.createHash('sha256').update(canonical).digest('hex');
232
232
  }
233
+ /**
234
+ * Find a same-type memory whose text content is a near-duplicate (Jaccard >= 0.85).
235
+ * Returns the key of the matching memory, or null if none found.
236
+ */
237
+ findFuzzyDuplicate(memory) {
238
+ const newText = this.extractText(memory.value).toLowerCase();
239
+ if (newText.length < 40)
240
+ return null; // Too short to fuzzy-match reliably
241
+ // Scope fuzzy dedup to same type AND same project (or both null/universal)
242
+ const projectFilter = memory.project_id
243
+ ? 'AND project_id = ?'
244
+ : 'AND (project_id IS NULL OR project_id = \'\')';
245
+ const params = [memory.type, memory.key];
246
+ if (memory.project_id)
247
+ params.push(memory.project_id);
248
+ const candidates = this.db.prepare(`SELECT key, value FROM memories WHERE type = ? AND key != ? ${projectFilter}`).all(...params);
249
+ for (const candidate of candidates) {
250
+ let candidateText;
251
+ try {
252
+ const parsed = JSON.parse(candidate.value);
253
+ candidateText = this.extractText(parsed).toLowerCase();
254
+ }
255
+ catch {
256
+ candidateText = candidate.value.toLowerCase();
257
+ }
258
+ if (this.jaccardSimilarity(newText, candidateText) >= 0.65) {
259
+ return candidate.key;
260
+ }
261
+ }
262
+ return null;
263
+ }
264
+ extractText(value) {
265
+ if (typeof value === 'string')
266
+ return value;
267
+ if (value && typeof value === 'object') {
268
+ // Collect all leaf string values, ignoring JSON keys
269
+ const leaves = [];
270
+ const collect = (obj) => {
271
+ if (typeof obj === 'string') {
272
+ leaves.push(obj);
273
+ return;
274
+ }
275
+ if (obj && typeof obj === 'object') {
276
+ for (const v of Object.values(obj))
277
+ collect(v);
278
+ }
279
+ };
280
+ collect(value);
281
+ return leaves.join(' ');
282
+ }
283
+ return String(value);
284
+ }
285
+ jaccardSimilarity(a, b) {
286
+ const tokenize = (s) => {
287
+ const words = s.replace(/[^a-z0-9\s]/g, ' ').split(/\s+/).filter(Boolean);
288
+ return new Set(words.filter(w => !MemoryStorage.STOP_WORDS.has(w)));
289
+ };
290
+ const wordsA = tokenize(a);
291
+ const wordsB = tokenize(b);
292
+ if (wordsA.size === 0 && wordsB.size === 0)
293
+ return 1;
294
+ if (wordsA.size === 0 || wordsB.size === 0)
295
+ return 0;
296
+ let intersection = 0;
297
+ for (const w of wordsA) {
298
+ if (wordsB.has(w))
299
+ intersection++;
300
+ }
301
+ const union = wordsA.size + wordsB.size - intersection;
302
+ return union === 0 ? 0 : intersection / union;
303
+ }
233
304
  save(memory) {
234
305
  const contentHash = this.computeContentHash(memory.value, memory.type);
235
306
  // Write-time dedup: check if identical content already exists under a different key
@@ -240,6 +311,13 @@ class MemoryStorage {
240
311
  this.db.pragma('wal_checkpoint(TRUNCATE)');
241
312
  return;
242
313
  }
314
+ // Fuzzy dedup: check if a same-type memory with very similar content exists
315
+ const fuzzyMatch = this.findFuzzyDuplicate(memory);
316
+ if (fuzzyMatch) {
317
+ this.db.prepare('UPDATE memories SET timestamp = ?, access_count = access_count + 1 WHERE key = ?').run(Date.now(), fuzzyMatch);
318
+ this.db.pragma('wal_checkpoint(TRUNCATE)');
319
+ return;
320
+ }
243
321
  const stmt = this.db.prepare(`
244
322
  INSERT OR REPLACE INTO memories
245
323
  (key, value, type, project_id, file_path, timestamp, relevance_score, access_count,
@@ -541,3 +619,9 @@ class MemoryStorage {
541
619
  }
542
620
  }
543
621
  exports.MemoryStorage = MemoryStorage;
622
+ MemoryStorage.STOP_WORDS = new Set([
623
+ 'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'been', 'being',
624
+ 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by', 'from', 'as',
625
+ 'and', 'or', 'but', 'not', 'no', 'do', 'does', 'did', 'this', 'that',
626
+ 'it', 'its', 'via', 'can', 'should', 'will', 'would', 'may', 'might',
627
+ ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-recall",
3
- "version": "0.20.13",
3
+ "version": "0.20.15",
4
4
  "description": "Persistent memory for Claude Code and Pi with native Skills integration, automatic capture, failure learning, and project scoping",
5
5
  "main": "dist/index.js",
6
6
  "bin": {