claude-recall 0.37.0 → 0.37.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.
Files changed (42) hide show
  1. package/.claude/hooks/search_enforcer.py +29 -2
  2. package/README.md +1 -1
  3. package/dist/cli/claude-recall-cli.js +7 -2
  4. package/dist/memory/schema.sql +0 -0
  5. package/docs/hooks.md +1 -1
  6. package/package.json +1 -1
  7. package/scripts/uninstall.js +0 -0
  8. package/dist/cli/commands/live-test.js +0 -245
  9. package/dist/cli/commands/migrate.js +0 -317
  10. package/dist/core/patterns.js +0 -56
  11. package/dist/hooks/bash-failure-watcher.js +0 -253
  12. package/dist/mcp/memory-capture-middleware.js +0 -349
  13. package/dist/mcp/queue-tools.js +0 -532
  14. package/dist/mcp/tools/live-testing-tools.js +0 -231
  15. package/dist/mcp/tools/test-tools.js +0 -320
  16. package/dist/memory/database-adapter.js +0 -256
  17. package/dist/memory/pattern-store.js +0 -68
  18. package/dist/services/claude-json-watcher.js +0 -243
  19. package/dist/services/context-enhancer.js +0 -215
  20. package/dist/services/conversation-context-manager.js +0 -254
  21. package/dist/services/embedding-service.js +0 -183
  22. package/dist/services/memory-enhancer.js +0 -148
  23. package/dist/services/memory-evolution.js +0 -249
  24. package/dist/services/memory-usage-tracker.js +0 -227
  25. package/dist/services/preference-analyzer.js +0 -242
  26. package/dist/services/queue-api.js +0 -560
  27. package/dist/services/queue-integration.js +0 -409
  28. package/dist/services/queue-migration.js +0 -415
  29. package/dist/services/queue-system.js +0 -1092
  30. package/dist/services/restart-continuity.js +0 -361
  31. package/dist/services/semantic-preference-extractor.js +0 -432
  32. package/dist/testing/auto-correction-engine.js +0 -338
  33. package/dist/testing/live-testing-manager.js +0 -402
  34. package/dist/testing/mock-claude.js +0 -249
  35. package/dist/testing/observable-database.js +0 -178
  36. package/dist/testing/scenario-runner.js +0 -354
  37. package/dist/testing/test-orchestrator.js +0 -328
  38. package/docs/2026-07-follow-up-article-draft.md +0 -68
  39. package/docs/2026-07-follow-up-article-linkedin-tight.md +0 -46
  40. package/docs/2026-07-positioning-vs-steering-files.md +0 -92
  41. package/docs/design-hybrid-retrieval-fts5.md +0 -185
  42. package/docs/using-claude-code-subscription-instead-of-api-key.md +0 -69
@@ -16,7 +16,29 @@ from datetime import datetime
16
16
 
17
17
  STATE_DIR = Path.home() / '.claude-recall' / 'hook-state'
18
18
  SEARCH_TTL_MS = int(os.environ.get('CLAUDE_RECALL_SEARCH_TTL', 60 * 1000)) # 1 min default (once per task)
19
- ENFORCE_MODE = os.environ.get('CLAUDE_RECALL_ENFORCE_MODE', 'block') # block, warn, off
19
+
20
+
21
+ def _resolve_enforce_mode() -> str:
22
+ """Enforcement mode: env var wins, then ~/.claude-recall/config.json
23
+ ("enforceMode"), else default 'warn'. A file-based switch is reachable
24
+ from inside a running session (an agent can edit config.json to escape a
25
+ stuck gate); an env var set before launch is not. Default is 'warn' — this
26
+ gate is an advisory nudge (see module docstring), so it should never
27
+ hard-block a session by default."""
28
+ env = os.environ.get('CLAUDE_RECALL_ENFORCE_MODE')
29
+ if env:
30
+ return env.strip().lower()
31
+ try:
32
+ cfg = json.load(open(Path.home() / '.claude-recall' / 'config.json'))
33
+ mode = cfg.get('enforceMode')
34
+ if mode:
35
+ return str(mode).strip().lower()
36
+ except Exception:
37
+ pass
38
+ return 'warn'
39
+
40
+
41
+ ENFORCE_MODE = _resolve_enforce_mode() # block, warn, off
20
42
  MAX_BLOCKS = int(os.environ.get('CLAUDE_RECALL_MAX_BLOCKS', 3)) # degrade to warn after N blocks
21
43
 
22
44
  # Tools that count as "search performed"
@@ -182,6 +204,7 @@ STALE RULES — consider reloading before {tool_name}
182
204
 
183
205
  Rules were loaded earlier but TTL expired.
184
206
  Run: mcp__claude-recall__load_rules({{}})
207
+ (If not directly callable, ToolSearch it first — see below.)
185
208
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
186
209
  """
187
210
  print(msg.strip(), file=sys.stderr)
@@ -214,10 +237,14 @@ LOAD RULES REQUIRED before {tool_name} (attempt {block_count}/{MAX_BLOCKS})
214
237
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
215
238
 
216
239
  Run: mcp__claude-recall__load_rules({{}})
240
+ (If that tool is not directly callable, first run
241
+ ToolSearch({{query:"select:mcp__claude-recall__load_rules"}}) —
242
+ some harnesses defer MCP tool schemas until discovered.)
217
243
 
218
244
  This ensures you apply user preferences and avoid past mistakes.
219
245
 
220
- To disable: CLAUDE_RECALL_ENFORCE_MODE=off
246
+ To disable this gate: set "enforceMode":"off" in ~/.claude-recall/config.json
247
+ (or export CLAUDE_RECALL_ENFORCE_MODE=off before launching Claude Code).
221
248
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
222
249
  """
223
250
  print(msg.strip(), file=sys.stderr)
package/README.md CHANGED
@@ -460,7 +460,7 @@ Defaults work out of the box; tune via environment variables as needed.
460
460
  | `CLAUDE_RECALL_AUTO_CLEANUP` | `false` | Auto-kill stale MCP processes on start (otherwise reports and exits). |
461
461
  | `CLAUDE_RECALL_COMPACT_THRESHOLD` | `10MB` | DB size at which automatic compaction kicks in. |
462
462
  | `CLAUDE_RECALL_MAX_MEMORIES` | `10000` | Memory-row soft cap. |
463
- | `CLAUDE_RECALL_ENFORCE_MODE` | `on` | Set to `off` to bypass the search-enforcer hook. |
463
+ | `CLAUDE_RECALL_ENFORCE_MODE` | `warn` | `block` / `warn` / `off` for the search-enforcer hook. Env wins, else `~/.claude-recall/config.json` `"enforceMode"`, else `warn`. |
464
464
  | `CLAUDE_RECALL_LLM_TIMEOUT_MS` | `5000` | Timeout for hook-context LLM calls (classification, hindsight hints). Hooks fall back to regex when it fires. |
465
465
  | `CLAUDE_RECALL_STOP_DEBOUNCE_MS` | `300000` | Debounce for the heavy Stop-hook pipeline (episodes, session extraction, promotion). `0` disables. |
466
466
  | `CLAUDE_RECALL_PROJECT_ID` | *(cwd)* | Pin the project scope to a fixed id, overriding working-directory detection. |
@@ -51,9 +51,10 @@ const hook_commands_1 = require("./commands/hook-commands");
51
51
  const kiro_commands_1 = require("./commands/kiro-commands");
52
52
  const repair_1 = require("./commands/repair");
53
53
  // v14 = add PreToolUse rule-injector + Post resolver for JITRI.
54
+ // v15 = bound the search_enforcer PreToolUse entry with timeout: 5.
54
55
  // Bump when the hook block template changes — setup skips the settings
55
56
  // rewrite when the installed hooksVersion already matches.
56
- const HOOKS_VERSION = '14.0.0';
57
+ const HOOKS_VERSION = '15.0.0';
57
58
  const parse_utils_1 = require("./parse-utils");
58
59
  const program = new commander_1.Command();
59
60
  class ClaudeRecallCLI {
@@ -1375,7 +1376,11 @@ async function main() {
1375
1376
  hooks: [
1376
1377
  {
1377
1378
  type: "command",
1378
- command: `python3 ${hookDest}`
1379
+ command: `python3 ${hookDest}`,
1380
+ // Runs before EVERY tool call (matcher .*). Bound the python3
1381
+ // cold start so it can't become a per-call latency tax or an
1382
+ // unbounded failure mode.
1383
+ timeout: 5
1379
1384
  },
1380
1385
  {
1381
1386
  type: "command",
File without changes
package/docs/hooks.md CHANGED
@@ -58,7 +58,7 @@ Common read-only commands are exempt from enforcement:
58
58
  | Environment Variable | Default | Description |
59
59
  |---|---|---|
60
60
  | `CLAUDE_RECALL_SEARCH_TTL` | `300000` (5 min) | Milliseconds a search remains valid |
61
- | `CLAUDE_RECALL_ENFORCE_MODE` | `block` | `block` (exit 2), `warn` (exit 0 + stderr message), or `off` (disabled) |
61
+ | `CLAUDE_RECALL_ENFORCE_MODE` | `warn` | `block` (exit 2), `warn` (exit 0 + stderr message), or `off` (disabled). Env wins; otherwise read from `~/.claude-recall/config.json` → `"enforceMode"`; else `warn`. The file switch is reachable from inside a running session — an env var set before launch is not. |
62
62
 
63
63
  ### Exit Codes
64
64
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-recall",
3
- "version": "0.37.0",
3
+ "version": "0.37.1",
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": {
File without changes
@@ -1,245 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LiveTestCommand = void 0;
4
- const live_testing_manager_1 = require("../../testing/live-testing-manager");
5
- const restart_continuity_1 = require("../../services/restart-continuity");
6
- const logging_1 = require("../../services/logging");
7
- class LiveTestCommand {
8
- constructor() {
9
- this.liveTestManager = live_testing_manager_1.LiveTestingManager.getInstance();
10
- this.continuityManager = restart_continuity_1.RestartContinuityManager.getInstance();
11
- this.logger = logging_1.LoggingService.getInstance();
12
- }
13
- register(program) {
14
- const liveTestCmd = program
15
- .command('live-test')
16
- .description('Manage live testing with automatic restart capability');
17
- // Start live testing
18
- liveTestCmd
19
- .command('start')
20
- .description('Start a live testing session')
21
- .option('-s, --scenario <scenarios...>', 'Test scenarios to run', ['memory_persistence', 'search_compliance'])
22
- .option('-a, --auto-restart', 'Enable automatic restart on changes', true)
23
- .option('-r, --restart-on-failure', 'Restart on test failures', true)
24
- .option('-m, --max-restarts <number>', 'Maximum restart attempts', '3')
25
- .option('-i, --inject-results', 'Inject test results as memories', true)
26
- .action(async (options) => {
27
- await this.startLiveTest(options);
28
- });
29
- // Check status
30
- liveTestCmd
31
- .command('status')
32
- .description('Check live testing and continuity status')
33
- .action(() => {
34
- this.showStatus();
35
- });
36
- // Stop testing
37
- liveTestCmd
38
- .command('stop')
39
- .description('Stop the current live testing session')
40
- .action(async () => {
41
- await this.stopLiveTest();
42
- });
43
- // Check continuity
44
- liveTestCmd
45
- .command('continuity')
46
- .description('Show continuity state and restart information')
47
- .action(() => {
48
- this.showContinuity();
49
- });
50
- // Add checkpoint
51
- liveTestCmd
52
- .command('checkpoint <name>')
53
- .description('Add a checkpoint for restart recovery')
54
- .action((name) => {
55
- this.addCheckpoint(name);
56
- });
57
- // Simulate restart
58
- liveTestCmd
59
- .command('restart')
60
- .description('Simulate a restart for testing')
61
- .option('-r, --reason <reason>', 'Reason for restart', 'manual_test')
62
- .action((options) => {
63
- this.simulateRestart(options.reason);
64
- });
65
- }
66
- async startLiveTest(options) {
67
- console.log('🚀 Starting Live Testing Session');
68
- console.log('━'.repeat(50));
69
- try {
70
- // Parse scenarios
71
- const scenarios = options.scenario.map((name) => ({
72
- name,
73
- params: {},
74
- sessionId: `cli_${Date.now()}`
75
- }));
76
- // Configure live testing
77
- this.liveTestManager.updateConfig({
78
- autoRestart: options.autoRestart,
79
- restartOnFailure: options.restartOnFailure,
80
- maxRestartAttempts: parseInt(options.maxRestarts),
81
- restartDelay: 2000,
82
- watchFiles: [
83
- 'src/hooks/**/*.sh',
84
- 'src/cli/claude-recall-cli.ts',
85
- 'src/services/**/*.ts'
86
- ],
87
- injectResults: options.injectResults
88
- });
89
- // Start session
90
- const sessionId = await this.liveTestManager.startLiveTestSession(scenarios);
91
- console.log('✅ Live testing session started');
92
- console.log(`Session ID: ${sessionId}`);
93
- console.log(`Scenarios: ${scenarios.map(s => s.name).join(', ')}`);
94
- console.log();
95
- console.log('Configuration:');
96
- console.log(` • Auto-restart: ${options.autoRestart ? 'enabled' : 'disabled'}`);
97
- console.log(` • Restart on failure: ${options.restartOnFailure ? 'enabled' : 'disabled'}`);
98
- console.log(` • Max restarts: ${options.maxRestarts}`);
99
- console.log(` • Inject results: ${options.injectResults ? 'enabled' : 'disabled'}`);
100
- console.log();
101
- console.log('Watching for file changes...');
102
- console.log('Use "claude-recall live-test status" to check progress');
103
- }
104
- catch (error) {
105
- console.error('❌ Failed to start live testing:', error.message);
106
- process.exit(1);
107
- }
108
- }
109
- showStatus() {
110
- console.log('📊 Live Testing Status');
111
- console.log('━'.repeat(50));
112
- // Get live test status
113
- const liveStatus = this.liveTestManager.getSessionStatus();
114
- if (liveStatus.status === 'no_active_session') {
115
- console.log('No active live testing session');
116
- }
117
- else {
118
- console.log(`Session ID: ${liveStatus.sessionId}`);
119
- console.log(`Status: ${this.getStatusEmoji(liveStatus.status)} ${liveStatus.status}`);
120
- console.log(`Progress: ${liveStatus.testsRun}/${liveStatus.totalTests} tests`);
121
- console.log(`Restart attempts: ${liveStatus.restartAttempts}`);
122
- if (liveStatus.results && liveStatus.results.length > 0) {
123
- console.log();
124
- console.log('Test Results:');
125
- liveStatus.results.forEach((result) => {
126
- const emoji = result.status === 'passed' ? '✅' : result.status === 'failed' ? '❌' : '⚠️';
127
- console.log(` ${emoji} ${result.test}: ${result.status}`);
128
- });
129
- }
130
- }
131
- // Get continuity status
132
- const continuityState = this.continuityManager.getCurrentState();
133
- if (continuityState) {
134
- console.log();
135
- console.log('🔄 Continuity State');
136
- console.log('━'.repeat(50));
137
- console.log(`Session: ${continuityState.sessionId}`);
138
- console.log(`Restarts: ${continuityState.restartCount}`);
139
- console.log(`Test in progress: ${continuityState.testInProgress ? 'yes' : 'no'}`);
140
- if (continuityState.currentTest) {
141
- console.log(`Current test: ${continuityState.currentTest.name}`);
142
- console.log(`Checkpoints: ${continuityState.currentTest.checkpoints.length}`);
143
- }
144
- if (continuityState.pendingActions.length > 0) {
145
- console.log();
146
- console.log(`Pending actions: ${continuityState.pendingActions.length}`);
147
- }
148
- }
149
- // Stop monitoring to allow process to exit
150
- this.continuityManager.stopMonitoring();
151
- }
152
- async stopLiveTest() {
153
- console.log('🛑 Stopping Live Testing');
154
- try {
155
- await this.liveTestManager.stopLiveTestSession();
156
- console.log('✅ Live testing session stopped');
157
- }
158
- catch (error) {
159
- console.error('❌ Failed to stop live testing:', error.message);
160
- }
161
- }
162
- showContinuity() {
163
- console.log('🔄 Continuity Information');
164
- console.log('━'.repeat(50));
165
- const state = this.continuityManager.getCurrentState();
166
- if (!state) {
167
- console.log('No continuity state available');
168
- return;
169
- }
170
- console.log('Session Information:');
171
- console.log(` • ID: ${state.sessionId}`);
172
- console.log(` • Restart count: ${state.restartCount}`);
173
- console.log(` • Last activity: ${new Date(state.lastActivity).toLocaleString()}`);
174
- console.log();
175
- if (state.testInProgress && state.currentTest) {
176
- console.log('Current Test:');
177
- console.log(` • Name: ${state.currentTest.name}`);
178
- console.log(` • Started: ${new Date(state.currentTest.startTime).toLocaleString()}`);
179
- console.log(` • Checkpoints: ${state.currentTest.checkpoints.length}`);
180
- if (state.currentTest.checkpoints.length > 0) {
181
- console.log(' • Recent checkpoints:');
182
- state.currentTest.checkpoints.slice(-3).forEach(cp => {
183
- console.log(` - ${cp}`);
184
- });
185
- }
186
- }
187
- else {
188
- console.log('No test currently in progress');
189
- }
190
- if (state.pendingActions.length > 0) {
191
- console.log();
192
- console.log(`Pending Actions (${state.pendingActions.length}):`);
193
- state.pendingActions.forEach(action => {
194
- console.log(` • ${action.type} - ${new Date(action.timestamp).toLocaleString()}`);
195
- });
196
- }
197
- // Stop monitoring to allow process to exit
198
- this.continuityManager.stopMonitoring();
199
- }
200
- addCheckpoint(name) {
201
- console.log('➕ Adding Checkpoint');
202
- try {
203
- this.continuityManager.addCheckpoint(name);
204
- console.log(`✅ Checkpoint added: ${name}`);
205
- const state = this.continuityManager.getCurrentState();
206
- if (state?.currentTest) {
207
- console.log(`Total checkpoints: ${state.currentTest.checkpoints.length}`);
208
- }
209
- }
210
- catch (error) {
211
- console.error('❌ Failed to add checkpoint:', error.message);
212
- }
213
- finally {
214
- // Stop monitoring to allow process to exit
215
- this.continuityManager.stopMonitoring();
216
- }
217
- }
218
- simulateRestart(reason) {
219
- console.log('🔄 Simulating Restart');
220
- console.log('━'.repeat(50));
221
- console.log(`Reason: ${reason}`);
222
- console.log('Note: This simulates a restart for testing purposes');
223
- console.log('In production, Claude Code would actually restart');
224
- // Add restart action to continuity
225
- this.continuityManager.addPendingAction('simulated_restart', {
226
- reason,
227
- timestamp: Date.now()
228
- });
229
- console.log();
230
- console.log('✅ Restart simulation recorded');
231
- console.log('The restart will be detected on next actual restart');
232
- // Stop monitoring to allow process to exit
233
- this.continuityManager.stopMonitoring();
234
- }
235
- getStatusEmoji(status) {
236
- switch (status) {
237
- case 'running': return '🏃';
238
- case 'completed': return '✅';
239
- case 'failed': return '❌';
240
- case 'restarting': return '🔄';
241
- default: return '❓';
242
- }
243
- }
244
- }
245
- exports.LiveTestCommand = LiveTestCommand;
@@ -1,317 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.MigrateCommand = void 0;
37
- const memory_1 = require("../../services/memory");
38
- const fs = __importStar(require("fs"));
39
- const path = __importStar(require("path"));
40
- const os = __importStar(require("os"));
41
- class MigrateCommand {
42
- static register(program) {
43
- const migrateCmd = program
44
- .command('migrate')
45
- .description('Migrate from file-watcher to MCP architecture');
46
- migrateCmd
47
- .command('check')
48
- .description('Check for existing file-watcher installation')
49
- .action(async () => {
50
- const migrator = new MigrateCommand();
51
- await migrator.checkInstallation();
52
- });
53
- migrateCmd
54
- .command('export')
55
- .description('Export memories from current installation')
56
- .option('-o, --output <path>', 'Output file path', 'claude-recall-export.json')
57
- .action(async (options) => {
58
- const migrator = new MigrateCommand();
59
- await migrator.exportMemories(options.output);
60
- });
61
- migrateCmd
62
- .command('import')
63
- .description('Import memories from export file')
64
- .argument('<file>', 'Export file to import')
65
- .action(async (file) => {
66
- const migrator = new MigrateCommand();
67
- await migrator.importMemories(file);
68
- });
69
- migrateCmd
70
- .command('complete')
71
- .description('Complete migration process')
72
- .action(async () => {
73
- const migrator = new MigrateCommand();
74
- await migrator.completeMigration();
75
- });
76
- migrateCmd
77
- .command('schema')
78
- .description('Migrate database schema to latest version (adds missing columns)')
79
- .option('--backup', 'Create backup before migration')
80
- .action(async (options) => {
81
- const migrator = new MigrateCommand();
82
- await migrator.migrateSchema(options.backup);
83
- });
84
- }
85
- async checkInstallation() {
86
- console.log('🔍 Checking for existing Claude Recall installation...\n');
87
- const checks = {
88
- database: this.checkDatabase(),
89
- hooks: this.checkHooks(),
90
- config: this.checkConfig()
91
- };
92
- if (checks.database) {
93
- console.log('✅ Found existing database');
94
- try {
95
- const memoryService = memory_1.MemoryService.getInstance();
96
- const stats = memoryService.getStats();
97
- console.log(` Contains ${stats.total} memories`);
98
- }
99
- catch (error) {
100
- console.log(' (Unable to read memory count)');
101
- }
102
- }
103
- else {
104
- console.log('❌ No existing database found');
105
- }
106
- if (checks.hooks) {
107
- console.log('✅ Found hook configuration in settings.json');
108
- }
109
- else {
110
- console.log('ℹ️ No hooks configured (or using MCP already)');
111
- }
112
- if (checks.database || checks.hooks) {
113
- console.log('\n📋 Next steps:');
114
- console.log('1. Run: claude-recall migrate export');
115
- console.log('2. Install MCP server: claude mcp add claude-recall claude-recall mcp start');
116
- console.log('3. Run: claude-recall migrate import claude-recall-export.json');
117
- console.log('4. Run: claude-recall migrate complete');
118
- }
119
- else {
120
- console.log('\n✨ No migration needed! Set up MCP directly:');
121
- console.log(' claude mcp add claude-recall claude-recall mcp start');
122
- }
123
- }
124
- async exportMemories(outputPath) {
125
- console.log('📦 Exporting memories...\n');
126
- try {
127
- // Access database directly for export
128
- const dbPath = path.join(os.homedir(), '.claude-recall', 'claude-recall.db');
129
- const { MemoryStorage } = require('../../memory/storage');
130
- const storage = new MemoryStorage(dbPath);
131
- const db = storage.getDatabase();
132
- // Get all memories from database
133
- const allMemories = db.prepare('SELECT * FROM memories').all();
134
- const stats = storage.getStats();
135
- const exportData = {
136
- version: '0.2.0',
137
- exportDate: new Date().toISOString(),
138
- memories: allMemories,
139
- stats: stats
140
- };
141
- fs.writeFileSync(outputPath, JSON.stringify(exportData, null, 2));
142
- console.log(`✅ Exported ${allMemories.length} memories to ${outputPath}`);
143
- }
144
- catch (error) {
145
- console.error('❌ Export failed:', error);
146
- process.exit(1);
147
- }
148
- }
149
- async importMemories(filePath) {
150
- console.log('📥 Importing memories...\n');
151
- try {
152
- if (!fs.existsSync(filePath)) {
153
- throw new Error(`File not found: ${filePath}`);
154
- }
155
- const exportData = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
156
- const memoryService = memory_1.MemoryService.getInstance();
157
- let imported = 0;
158
- for (const memory of exportData.memories) {
159
- try {
160
- memoryService.store({
161
- key: memory.key || `imported_${Date.now()}_${Math.random()}`,
162
- value: memory.value,
163
- type: memory.type || 'imported',
164
- context: memory.context || {}
165
- });
166
- imported++;
167
- }
168
- catch (err) {
169
- console.warn(`⚠️ Failed to import memory: ${err}`);
170
- }
171
- }
172
- console.log(`✅ Imported ${imported}/${exportData.memories.length} memories`);
173
- }
174
- catch (error) {
175
- console.error('❌ Import failed:', error);
176
- process.exit(1);
177
- }
178
- }
179
- async completeMigration() {
180
- console.log('🎯 Completing migration to MCP architecture...\n');
181
- // Remove old hooks from settings.json
182
- console.log('1. Cleaning up old hooks...');
183
- const removed = await this.removeOldHooks();
184
- if (removed) {
185
- console.log(' ✅ Removed file-watcher hooks');
186
- }
187
- else {
188
- console.log(' ℹ️ No hooks to remove');
189
- }
190
- // Verify MCP is working
191
- console.log('\n2. Verifying MCP server...');
192
- try {
193
- const { execSync } = require('child_process');
194
- const result = execSync('echo \'{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}\' | claude-recall mcp start', { timeout: 5000 }).toString();
195
- if (result.includes('claude-recall')) {
196
- console.log(' ✅ MCP server is working');
197
- }
198
- else {
199
- throw new Error('MCP server not responding correctly');
200
- }
201
- }
202
- catch (error) {
203
- console.log(' ❌ MCP server test failed');
204
- console.log(' Please ensure: claude mcp add claude-recall claude-recall mcp start');
205
- }
206
- console.log('\n✨ Migration complete!');
207
- console.log('Claude Recall is now using the MCP architecture.');
208
- }
209
- checkDatabase() {
210
- const dbPath = path.join(os.homedir(), '.claude-recall', 'claude-recall.db');
211
- return fs.existsSync(dbPath);
212
- }
213
- checkHooks() {
214
- // Check if settings.json has our hooks
215
- const settingsPath = path.join(os.homedir(), '.claude-code', 'settings.json');
216
- if (fs.existsSync(settingsPath)) {
217
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
218
- return settings.hooks && Object.keys(settings.hooks).some(k => settings.hooks[k] && settings.hooks[k].includes('claude-recall'));
219
- }
220
- return false;
221
- }
222
- checkConfig() {
223
- const configPath = path.join(os.homedir(), '.claude-recall', 'config.json');
224
- return fs.existsSync(configPath);
225
- }
226
- async removeOldHooks() {
227
- const settingsPath = path.join(os.homedir(), '.claude-code', 'settings.json');
228
- if (!fs.existsSync(settingsPath)) {
229
- return false;
230
- }
231
- try {
232
- const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
233
- let modified = false;
234
- if (settings.hooks) {
235
- for (const hookName of Object.keys(settings.hooks)) {
236
- if (settings.hooks[hookName] && settings.hooks[hookName].includes('claude-recall')) {
237
- delete settings.hooks[hookName];
238
- modified = true;
239
- }
240
- }
241
- }
242
- if (modified) {
243
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
244
- return true;
245
- }
246
- }
247
- catch (error) {
248
- console.error('Failed to modify settings.json:', error);
249
- }
250
- return false;
251
- }
252
- async migrateSchema(createBackup = false) {
253
- console.log('📋 Migrating database schema...\n');
254
- try {
255
- const dbPath = path.join(os.homedir(), '.claude-recall', 'claude-recall.db');
256
- if (!fs.existsSync(dbPath)) {
257
- console.log('❌ Database not found. Nothing to migrate.');
258
- return;
259
- }
260
- // Create backup if requested
261
- if (createBackup) {
262
- const backupPath = `${dbPath}.backup.${Date.now()}`;
263
- console.log(`📦 Creating backup: ${backupPath}`);
264
- fs.copyFileSync(dbPath, backupPath);
265
- console.log('✅ Backup created\n');
266
- }
267
- // Access storage to trigger migration
268
- const { MemoryStorage } = require('../../memory/storage');
269
- const storage = new MemoryStorage(dbPath);
270
- // Check what columns exist
271
- const db = storage.getDatabase();
272
- const columns = db.prepare("PRAGMA table_info(memories)").all();
273
- const columnNames = columns.map((c) => c.name);
274
- console.log('Current schema columns:');
275
- console.log(' ' + columnNames.join(', '));
276
- const hasSophistication = columnNames.includes('sophistication_level');
277
- const hasScope = columnNames.includes('scope');
278
- if (hasSophistication && hasScope) {
279
- console.log('\n✅ Schema is already up to date!');
280
- console.log(' All required columns are present.');
281
- }
282
- else {
283
- console.log('\n📋 Schema update needed:');
284
- if (!hasSophistication) {
285
- console.log(' - Missing: sophistication_level (added in v0.7.0)');
286
- }
287
- if (!hasScope) {
288
- console.log(' - Missing: scope (added in v0.7.2)');
289
- }
290
- console.log('\n⚙️ Running migration...');
291
- // The migration happens automatically when MemoryStorage initializes
292
- // Just force a re-init by creating a new instance
293
- const { MemoryStorage: Storage2 } = require('../../memory/storage');
294
- new Storage2(dbPath);
295
- console.log('\n✅ Migration complete!');
296
- console.log(' Database schema is now up to date.');
297
- }
298
- // Display stats
299
- console.log('\n📊 Database stats:');
300
- const stats = storage.getStats();
301
- console.log(` Total memories: ${stats.total}`);
302
- if (stats.byType) {
303
- console.log(' By type:');
304
- for (const [type, count] of Object.entries(stats.byType)) {
305
- console.log(` ${type}: ${count}`);
306
- }
307
- }
308
- }
309
- catch (error) {
310
- console.error('\n❌ Migration failed:', error);
311
- console.log('\nIf you created a backup, you can restore it manually:');
312
- console.log(` cp ~/.claude-recall/claude-recall.db.backup.* ~/.claude-recall/claude-recall.db`);
313
- process.exit(1);
314
- }
315
- }
316
- }
317
- exports.MigrateCommand = MigrateCommand;