claude-recall 0.16.1 → 0.17.1

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,7 +8,7 @@ source: claude-recall
8
8
 
9
9
  # Corrections
10
10
 
11
- Auto-generated from 20 memories. Last updated: 2026-03-16.
11
+ Auto-generated from 21 memories. Last updated: 2026-03-17.
12
12
 
13
13
  ## Rules
14
14
 
@@ -25,6 +25,7 @@ Auto-generated from 20 memories. Last updated: 2026-03-16.
25
25
  - CORRECTION: Memory with complex metadata
26
26
  - CORRECTION: Memory with complex metadata
27
27
  - CORRECTION: Memory with complex metadata
28
+ - CORRECTION: Memory with complex metadata
28
29
  - CORRECTION: Replace expired access token with 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
29
30
  - CORRECTION: use spaces not tabs for indentation
30
31
  - CORRECTION: cited (loaded 5+ times): 19
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "topicId": "corrections",
3
- "sourceHash": "aaf622f0c245a33d23d1c690cd82108fdf2b43e141882838c7b12923bde2aea4",
4
- "memoryCount": 20,
5
- "generatedAt": "2026-03-16T11:15:23.327Z",
3
+ "sourceHash": "b717cebe14486cb8c7d1681f494e3e31136c32b0fe8a51112fe1c1a07bfe47f4",
4
+ "memoryCount": 21,
5
+ "generatedAt": "2026-03-17T09:43:28.791Z",
6
6
  "memoryKeys": [
7
+ "memory_1773740608781_ae2bn7zos",
7
8
  "memory_1773659723317_o738xfbyw",
8
9
  "memory_1773656489899_l75d50pxo",
9
10
  "memory_1773654956776_7mffm1tp2",
@@ -8,11 +8,12 @@ source: claude-recall
8
8
 
9
9
  # Failure Lessons
10
10
 
11
- Auto-generated from 23 memories. Last updated: 2026-03-16.
11
+ Auto-generated from 24 memories. Last updated: 2026-03-17.
12
12
 
13
13
  ## Rules
14
14
 
15
15
  - SQLite query syntax error: LIKE clause requires single quotes around string literal, not double quotes
16
+ - Avoid: Command failed: npx jest tests/unit/memory-sync-hook.test.ts 2>&1 → Instead: Check command syntax, file paths, and prerequisites before running
16
17
  - Avoid: Command failed: npm whoami 2>&1 && npm config get registry 2>&1 → Instead: Check command syntax, file paths, and prerequisites before running
17
18
  - npm install -g claude-recall@0.15.36 failed with notarget error
18
19
  - claude-recall@0.15.36 does not exist on npm registry (ETARGET error)
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "topicId": "failure-lessons",
3
- "sourceHash": "6c18a22368601e246196934bc675b7cc11619a20758e5a8067886375b499dd00",
4
- "memoryCount": 23,
5
- "generatedAt": "2026-03-16T09:55:56.753Z",
3
+ "sourceHash": "e099bbcfc9aa57afd1fb83ab4082a08ff820dd4f1dc31af6d4fbcfa3ad0af3d5",
4
+ "memoryCount": 24,
5
+ "generatedAt": "2026-03-17T09:43:28.755Z",
6
6
  "memoryKeys": [
7
7
  "hook_failure_1772637584921_0tj4rrxnt",
8
+ "hook_failure_non-zero-exit_1773669669860_9mcdbx51y",
8
9
  "hook_failure_non-zero-exit_1773409269877_ful451241",
9
10
  "hook_failure_1773410916808_5k1r6zo4u",
10
11
  "hook_failure_1773410916789_xtrb8j9nw",
@@ -8,10 +8,15 @@ source: claude-recall
8
8
 
9
9
  # Preferences
10
10
 
11
- Auto-generated from 86 memories. Last updated: 2026-03-16.
11
+ Auto-generated from 91 memories. Last updated: 2026-03-17.
12
12
 
13
13
  ## Rules
14
14
 
15
+ - Session test preference 1773740608856
16
+ - Test preference 1773740608797-2
17
+ - Test preference 1773740608797-1
18
+ - Test preference 1773740608797-0
19
+ - Test memory content
15
20
  - Session test preference 1773659723404
16
21
  - Test preference 1773659723333-2
17
22
  - Test preference 1773659723333-1
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "topicId": "preferences",
3
- "sourceHash": "4ae92aa7a2e0c881c89cc72d6645bc7bbe2c9c848db8618031bdf6ba30caecbc",
4
- "memoryCount": 86,
5
- "generatedAt": "2026-03-16T11:15:23.417Z",
3
+ "sourceHash": "e2c0f6c21a82d06422afe3b9bddeca1a865d50a9472e3868459612589dd9f5e9",
4
+ "memoryCount": 91,
5
+ "generatedAt": "2026-03-17T09:43:28.867Z",
6
6
  "memoryKeys": [
7
+ "memory_1773740608857_7954nxhke",
8
+ "memory_1773740608828_pkci7qhtt",
9
+ "memory_1773740608815_k3tj4psf1",
10
+ "memory_1773740608798_287hs9q7h",
11
+ "memory_1773740608717_3wy6inv8q",
7
12
  "memory_1773659723405_v31rp5h3e",
8
13
  "memory_1773659723371_xhh4zlbuh",
9
14
  "memory_1773659723354_y2ohkszjc",
package/README.md CHANGED
@@ -28,29 +28,34 @@ Your preferences, project structure, workflows, corrections, and coding style ar
28
28
  | Node.js | **20+** | required for better-sqlite3 |
29
29
  | OS | macOS / Linux / Windows | WSL supported |
30
30
 
31
- ### One-Time Setup
31
+ ### Install / Reinstall
32
32
 
33
- Run these once per machine:
33
+ Run these from your project directory:
34
34
 
35
35
  ```bash
36
- npm install -g claude-recall
37
- claude mcp remove claude-recall 2>/dev/null; claude mcp add claude-recall -s user -- claude-recall mcp start
38
- ```
36
+ # 1. Remove MCP server registration (if exists)
37
+ claude mcp remove claude-recall
39
38
 
40
- This installs the global binary and registers the MCP server at user scope — shared across all your projects.
39
+ # 2. Clear npm cache
40
+ npm cache clean --force
41
41
 
42
- ### Per-Project Setup
42
+ # 3. Uninstall global claude-recall
43
+ npm uninstall -g claude-recall
43
44
 
44
- Run this in each project directory:
45
+ # 4. Install global claude-recall
46
+ npm install -g claude-recall
45
47
 
46
- ```bash
47
- cd your-project && claude-recall setup --install
48
- ```
48
+ # 5. Install in local project folder
49
+ claude-recall setup --install
49
50
 
50
- This installs hooks and skills into `your-project/.claude/`. Repeat for each project you want Claude Recall active in. Memories are automatically scoped per project in a shared database (`~/.claude-recall/claude-recall.db`).
51
+ # 6. Re-register MCP server
52
+ claude mcp add claude-recall -- claude-recall mcp start
53
+ ```
51
54
 
52
55
  Then restart your Claude Code session.
53
56
 
57
+ Repeat step 5 (`claude-recall setup --install`) in each project you want Claude Recall active in. Memories are automatically scoped per project in a shared database (`~/.claude-recall/claude-recall.db`).
58
+
54
59
  ### Verify
55
60
 
56
61
  In Claude Code, ask: *"Load my rules"*
@@ -59,10 +64,7 @@ Claude should call `mcp__claude-recall__load_rules`. If it works, you're ready.
59
64
 
60
65
  ### Upgrading
61
66
 
62
- ```bash
63
- npm install -g claude-recall@latest
64
- cd your-project && claude-recall setup --install # Re-register hooks in each project
65
- ```
67
+ Follow the same install steps above — they handle both fresh installs and upgrades.
66
68
 
67
69
  ---
68
70
 
@@ -119,11 +121,6 @@ claude-recall --version # Check version
119
121
  <summary>All commands</summary>
120
122
 
121
123
  ```bash
122
- # ── Upgrade (global binary, then per-project hooks) ─────────────────
123
- npm install -g claude-recall@latest
124
- cd your-project && claude-recall setup --install # Repeat per project
125
- claude-recall --version # Verify
126
-
127
124
  # ── Setup & Diagnostics ─────────────────────────────────────────────
128
125
  claude-recall setup # Show activation instructions
129
126
  claude-recall setup --install # Install skills + hooks
@@ -608,8 +608,20 @@ async function main() {
608
608
  // This avoids registry lookups on every hook invocation.
609
609
  const cliScript = path.join(packageDir, 'dist', 'cli', 'claude-recall-cli.js');
610
610
  const hookCmd = `node ${cliScript} hook run`;
611
- settings.hooksVersion = '7.0.0'; // v7 = add memory-sync hook for auto-memory export
611
+ settings.hooksVersion = '8.0.0'; // v8 = add bash-failure-watcher PostToolUse hook
612
612
  settings.hooks = {
613
+ PostToolUse: [
614
+ {
615
+ matcher: "Bash",
616
+ hooks: [
617
+ {
618
+ type: "command",
619
+ command: `${hookCmd} bash-failure-watcher`,
620
+ timeout: 3
621
+ }
622
+ ]
623
+ }
624
+ ],
613
625
  PreToolUse: [
614
626
  {
615
627
  matcher: ".*",
@@ -75,9 +75,14 @@ class HookCommands {
75
75
  await handleMemorySync(input);
76
76
  break;
77
77
  }
78
+ case 'bash-failure-watcher': {
79
+ const { handleBashFailureWatcher } = await Promise.resolve().then(() => __importStar(require('../../hooks/bash-failure-watcher')));
80
+ await handleBashFailureWatcher(input);
81
+ break;
82
+ }
78
83
  default:
79
84
  console.error(`Unknown hook: ${name}`);
80
- console.error('Available: correction-detector, memory-stop, precompact-preserve, memory-sync');
85
+ console.error('Available: correction-detector, memory-stop, precompact-preserve, memory-sync, bash-failure-watcher');
81
86
  }
82
87
  }
83
88
  catch {
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ /**
3
+ * Bash Failure Watcher — PostToolUse hook (v0.17.0)
4
+ *
5
+ * Captures bash command failures immediately after they happen (not at session end).
6
+ * When a previously failed command succeeds, pairs the fix with the failure record.
7
+ *
8
+ * Input (PostToolUse stdin from Claude Code):
9
+ * {
10
+ * "tool_name": "Bash",
11
+ * "tool_input": { "command": "npm test" },
12
+ * "tool_output": "... Exit code 1 ...",
13
+ * "session_id": "abc-123"
14
+ * }
15
+ */
16
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ var desc = Object.getOwnPropertyDescriptor(m, k);
19
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
20
+ desc = { enumerable: true, get: function() { return m[k]; } };
21
+ }
22
+ Object.defineProperty(o, k2, desc);
23
+ }) : (function(o, m, k, k2) {
24
+ if (k2 === undefined) k2 = k;
25
+ o[k2] = m[k];
26
+ }));
27
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
28
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
29
+ }) : function(o, v) {
30
+ o["default"] = v;
31
+ });
32
+ var __importStar = (this && this.__importStar) || (function () {
33
+ var ownKeys = function(o) {
34
+ ownKeys = Object.getOwnPropertyNames || function (o) {
35
+ var ar = [];
36
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
37
+ return ar;
38
+ };
39
+ return ownKeys(o);
40
+ };
41
+ return function (mod) {
42
+ if (mod && mod.__esModule) return mod;
43
+ var result = {};
44
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
45
+ __setModuleDefault(result, mod);
46
+ return result;
47
+ };
48
+ })();
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ exports.handleBashFailureWatcher = handleBashFailureWatcher;
51
+ const fs = __importStar(require("fs"));
52
+ const path = __importStar(require("path"));
53
+ const os = __importStar(require("os"));
54
+ const shared_1 = require("./shared");
55
+ const memory_1 = require("../services/memory");
56
+ const HOOK_NAME = 'bash-failure-watcher';
57
+ const EXIT_CODE_REGEX = /Exit code (\d+)/;
58
+ const MAX_PENDING = 5;
59
+ const FIX_WINDOW_MS = 5 * 60 * 1000; // 5 minutes
60
+ const FIX_JACCARD_THRESHOLD = 0.3;
61
+ function getStateDir() {
62
+ return path.join(os.homedir(), '.claude-recall', 'hook-state');
63
+ }
64
+ function getStatePath(sessionId) {
65
+ return path.join(getStateDir(), `${sessionId}-failures.json`);
66
+ }
67
+ function readPendingFailures(sessionId) {
68
+ try {
69
+ const statePath = getStatePath(sessionId);
70
+ if (!fs.existsSync(statePath))
71
+ return [];
72
+ const data = fs.readFileSync(statePath, 'utf-8');
73
+ return JSON.parse(data);
74
+ }
75
+ catch {
76
+ return [];
77
+ }
78
+ }
79
+ function writePendingFailures(sessionId, pending) {
80
+ try {
81
+ const stateDir = getStateDir();
82
+ if (!fs.existsSync(stateDir)) {
83
+ fs.mkdirSync(stateDir, { recursive: true });
84
+ }
85
+ fs.writeFileSync(getStatePath(sessionId), JSON.stringify(pending));
86
+ }
87
+ catch {
88
+ // Never fail on state file writes
89
+ }
90
+ }
91
+ function truncate(s, maxLen) {
92
+ if (s.length <= maxLen)
93
+ return s;
94
+ return s.substring(0, maxLen - 3) + '...';
95
+ }
96
+ function firstLine(s) {
97
+ const idx = s.indexOf('\n');
98
+ return idx === -1 ? s : s.substring(0, idx);
99
+ }
100
+ async function handleBashFailureWatcher(input) {
101
+ try {
102
+ if (!input || typeof input !== 'object')
103
+ return;
104
+ const toolName = input.tool_name;
105
+ if (toolName !== 'Bash')
106
+ return;
107
+ const command = input.tool_input?.command;
108
+ if (!command || typeof command !== 'string' || command.length < 3)
109
+ return;
110
+ const output = input.tool_output ?? '';
111
+ const sessionId = input.session_id ?? 'unknown';
112
+ // Skip hook infrastructure errors
113
+ if (output.includes('PreToolUse') || output.includes('PostToolUse'))
114
+ return;
115
+ const exitMatch = output.match(EXIT_CODE_REGEX);
116
+ const isFailure = exitMatch && exitMatch[1] !== '0';
117
+ if (isFailure) {
118
+ await handleFailure(command, exitMatch[1], output, sessionId);
119
+ }
120
+ else {
121
+ await handleSuccess(command, sessionId);
122
+ }
123
+ }
124
+ catch (err) {
125
+ (0, shared_1.hookLog)(HOOK_NAME, `Error: ${err}`);
126
+ // Never throw — hooks must not block Claude
127
+ }
128
+ }
129
+ async function handleFailure(command, exitCode, output, sessionId) {
130
+ // Dedup check
131
+ const existing = (0, shared_1.searchExisting)(command);
132
+ if ((0, shared_1.isDuplicate)(command, existing, 0.7)) {
133
+ (0, shared_1.hookLog)(HOOK_NAME, `Skipped duplicate: ${truncate(command, 60)}`);
134
+ return;
135
+ }
136
+ const failureContent = {
137
+ what_failed: `Command failed: ${truncate(command, 100)}`,
138
+ why_failed: `Exit code ${exitCode}: ${truncate(firstLine(output), 150)}`,
139
+ what_should_do: 'Check command syntax, file paths, and prerequisites before running',
140
+ context: `Bash command returned non-zero exit code ${exitCode}`,
141
+ preventative_checks: [
142
+ 'Verify command arguments and paths exist',
143
+ 'Check required tools are installed',
144
+ ],
145
+ };
146
+ const key = `hook_failure_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
147
+ const memoryService = memory_1.MemoryService.getInstance();
148
+ memoryService.store({
149
+ key,
150
+ value: failureContent,
151
+ type: 'failure',
152
+ context: { timestamp: Date.now() },
153
+ relevanceScore: 0.85,
154
+ });
155
+ (0, shared_1.hookLog)(HOOK_NAME, `Stored failure: ${truncate(command, 60)} (exit ${exitCode})`);
156
+ // Track pending failure for fix pairing
157
+ const pending = readPendingFailures(sessionId);
158
+ pending.push({ command, memoryKey: key, timestamp: Date.now() });
159
+ // Keep max 5, evict oldest
160
+ while (pending.length > MAX_PENDING) {
161
+ pending.shift();
162
+ }
163
+ writePendingFailures(sessionId, pending);
164
+ // Notify Claude
165
+ console.log(`⚠️ Failure stored: "${truncate(command, 60)}" (exit ${exitCode}). Use search_memory for similar past failures.`);
166
+ }
167
+ async function handleSuccess(command, sessionId) {
168
+ const pending = readPendingFailures(sessionId);
169
+ if (pending.length === 0)
170
+ return;
171
+ const now = Date.now();
172
+ let matched = false;
173
+ // Filter expired and find matches
174
+ const remaining = [];
175
+ for (const pf of pending) {
176
+ if (now - pf.timestamp > FIX_WINDOW_MS) {
177
+ // Expired — evict
178
+ continue;
179
+ }
180
+ if (!matched && (0, shared_1.jaccardSimilarity)(pf.command, command) >= FIX_JACCARD_THRESHOLD) {
181
+ // Match found — update the failure memory with the fix
182
+ try {
183
+ const memoryService = memory_1.MemoryService.getInstance();
184
+ memoryService.update(pf.memoryKey, {
185
+ value: {
186
+ what_should_do: `Fix: ${truncate(command, 200)}`,
187
+ },
188
+ });
189
+ (0, shared_1.hookLog)(HOOK_NAME, `Paired fix: "${truncate(command, 60)}" → ${pf.memoryKey}`);
190
+ matched = true;
191
+ // Don't add to remaining — consumed
192
+ }
193
+ catch (err) {
194
+ (0, shared_1.hookLog)(HOOK_NAME, `Fix pairing error: ${err}`);
195
+ remaining.push(pf);
196
+ }
197
+ }
198
+ else {
199
+ remaining.push(pf);
200
+ }
201
+ }
202
+ writePendingFailures(sessionId, remaining);
203
+ }
@@ -496,6 +496,12 @@ class MemoryService {
496
496
  // Default: unscoped (null) for backward compatibility
497
497
  return null;
498
498
  }
499
+ /**
500
+ * Update a memory record by key (used for fix pairing in hooks)
501
+ */
502
+ update(key, updates) {
503
+ this.storage.update(key, updates);
504
+ }
499
505
  /**
500
506
  * Close database connection
501
507
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-recall",
3
- "version": "0.16.1",
3
+ "version": "0.17.1",
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": {