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-
|
|
11
|
+
Auto-generated from 5 memories. Last updated: 2026-04-10.
|
|
12
12
|
|
|
13
13
|
## Rules
|
|
14
14
|
|
|
15
|
-
- Session test preference
|
|
16
|
-
- Test preference
|
|
17
|
-
- Test preference
|
|
18
|
-
- Test preference
|
|
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": "
|
|
3
|
+
"sourceHash": "9b2ae714e4bdd82091b4fcc5347c2b1a8a2a10d3be57ca57877e943e380a5457",
|
|
4
4
|
"memoryCount": 5,
|
|
5
|
-
"generatedAt": "2026-04-
|
|
5
|
+
"generatedAt": "2026-04-10T11:51:32.469Z",
|
|
6
6
|
"memoryKeys": [
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
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
|
-
###
|
|
145
|
+
### Health Check (run these first)
|
|
146
146
|
|
|
147
147
|
```bash
|
|
148
|
-
claude-recall
|
|
149
|
-
claude-recall
|
|
150
|
-
claude-recall
|
|
151
|
-
claude-recall
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
|
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)) {
|
package/dist/memory/storage.js
CHANGED
|
@@ -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