claude-self-reflect 2.4.1 → 2.4.2

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.
@@ -1,1492 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { execSync, spawn, spawnSync } from 'child_process';
3
+ // This is the new Docker-based setup wizard
4
+ // It runs everything in Docker to avoid Python environment issues
4
5
  import { fileURLToPath } from 'url';
5
6
  import { dirname, join } from 'path';
6
- import fs from 'fs/promises';
7
- import fsSync from 'fs';
8
- import readline from 'readline';
9
- import path from 'path';
10
7
 
11
8
  const __filename = fileURLToPath(import.meta.url);
12
9
  const __dirname = dirname(__filename);
13
- const projectRoot = join(__dirname, '..');
14
10
 
15
- // Safe command execution helper
16
- function safeExec(command, args = [], options = {}) {
17
- const result = spawnSync(command, args, {
18
- ...options,
19
- shell: false // Never use shell to prevent injection
20
- });
21
-
22
- if (result.error) {
23
- throw result.error;
24
- }
25
-
26
- if (result.status !== 0) {
27
- const error = new Error(`Command failed: ${command} ${args.join(' ')}`);
28
- error.stdout = result.stdout;
29
- error.stderr = result.stderr;
30
- error.status = result.status;
31
- throw error;
32
- }
33
-
34
- return result.stdout?.toString() || '';
35
- }
36
-
37
- // Parse command line arguments
38
- const args = process.argv.slice(2);
39
- let voyageKey = null;
40
- let mcpConfigured = false;
41
- let debugMode = false;
42
-
43
- for (const arg of args) {
44
- if (arg.startsWith('--voyage-key=')) {
45
- voyageKey = arg.split('=')[1];
46
- } else if (arg === '--debug') {
47
- debugMode = true;
48
- }
49
- }
50
-
51
- // Default to local mode unless Voyage key is provided
52
- let localMode = !voyageKey;
53
-
54
- // WSL Detection
55
- function isWSL() {
56
- try {
57
- const osRelease = fsSync.readFileSync('/proc/version', 'utf8');
58
- return osRelease.toLowerCase().includes('microsoft') || osRelease.toLowerCase().includes('wsl');
59
- } catch {
60
- return false;
61
- }
62
- }
63
-
64
- // Debug logging helper
65
- function debugLog(...args) {
66
- if (debugMode) {
67
- console.log('[DEBUG]', ...args);
68
- }
69
- }
70
-
71
- const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
72
-
73
- const rl = isInteractive ? readline.createInterface({
74
- input: process.stdin,
75
- output: process.stdout
76
- }) : null;
77
-
78
- const question = (query) => {
79
- if (!isInteractive) {
80
- console.log(`Non-interactive mode detected. ${query} [Defaulting to 'n']`);
81
- return Promise.resolve('n');
82
- }
83
- return new Promise((resolve) => rl.question(query, resolve));
84
- };
85
-
86
- async function checkPython() {
87
- console.log('\nšŸ“¦ Checking Python installation...');
88
- try {
89
- const version = safeExec('python3', ['--version']).trim();
90
- console.log(`āœ… Found ${version}`);
91
-
92
- // Check if SSL module works
93
- try {
94
- safeExec('python3', ['-c', 'import ssl'], { stdio: 'pipe' });
95
- return true;
96
- } catch (sslError) {
97
- console.log('āš ļø Python SSL module not working');
98
-
99
- // Check if we're using pyenv
100
- const whichPython = safeExec('which', ['python3']).trim();
101
- if (whichPython.includes('pyenv')) {
102
- console.log('šŸ” Detected pyenv Python with broken SSL');
103
-
104
- // Check if brew Python is available
105
- try {
106
- let brewPrefix = '';
107
- for (const pythonVersion of ['python@3.11', 'python@3.10', 'python@3.12']) {
108
- try {
109
- brewPrefix = safeExec('brew', ['--prefix', pythonVersion]).trim();
110
- if (brewPrefix) break;
111
- } catch {}
112
- }
113
- if (brewPrefix) {
114
- // Find the actual python executable
115
- let pythonPath = null;
116
- for (const exe of ['python3.11', 'python3.10', 'python3.12', 'python3']) {
117
- try {
118
- const fullPath = `${brewPrefix}/bin/${exe}`;
119
- // Use fs.existsSync instead of shell test command
120
- const { existsSync } = await import('fs');
121
- if (!existsSync(fullPath)) throw new Error('File not found');
122
- pythonPath = fullPath;
123
- break;
124
- } catch {}
125
- }
126
-
127
- if (pythonPath) {
128
- console.log(`āœ… Found brew Python at ${pythonPath}`);
129
- // Test if SSL works with brew Python
130
- try {
131
- safeExec(pythonPath, ['-c', 'import ssl'], { stdio: 'pipe' });
132
- process.env.PYTHON_PATH = pythonPath;
133
- return true;
134
- } catch {
135
- console.log('āš ļø Brew Python also has SSL issues');
136
- }
137
- }
138
- }
139
- } catch {}
140
-
141
- console.log('\nšŸ”§ Attempting to install Python with brew...');
142
- try {
143
- safeExec('brew', ['install', 'python@3.11'], { stdio: 'inherit' });
144
- const brewPython = safeExec('brew', ['--prefix', 'python@3.11']).trim();
145
- process.env.PYTHON_PATH = `${brewPython}/bin/python3`;
146
- console.log('āœ… Installed Python 3.11 with brew');
147
- return true;
148
- } catch {
149
- console.log('āŒ Failed to install Python with brew');
150
- }
151
- }
152
-
153
- return false;
154
- }
155
- } catch {
156
- console.log('āŒ Python 3.10+ not found');
157
- console.log(' Please install Python from https://python.org');
158
- return false;
159
- }
160
- }
161
-
162
- async function checkDocker() {
163
- try {
164
- safeExec('docker', ['info'], { stdio: 'ignore' });
165
- return true;
166
- } catch {
167
- return false;
168
- }
169
- }
170
-
171
- async function checkQdrant() {
172
- console.log('\n🐳 Checking Qdrant...');
173
- try {
174
- const response = await fetch('http://localhost:6333');
175
- const data = await response.json();
176
- if (data.title && data.title.includes('qdrant')) {
177
- console.log('āœ… Qdrant is already running');
178
- return true;
179
- }
180
- } catch {}
181
-
182
- console.log('āŒ Qdrant not found');
183
-
184
- // Check if Docker is available and running
185
- const dockerAvailable = await checkDocker();
186
- if (!dockerAvailable) {
187
- console.log('āŒ Docker is not running or not installed');
188
- console.log(' Please install Docker from https://docker.com and ensure the Docker daemon is running');
189
- console.log(' Then run this setup again');
190
- return false;
191
- }
192
-
193
- // In non-interactive mode, skip standalone Qdrant - docker-compose will handle it
194
- let start = 'n';
195
- if (isInteractive) {
196
- start = await question('Would you like to start Qdrant with Docker? (y/n): ');
197
- } else {
198
- console.log('šŸ¤– Non-interactive mode detected. Qdrant will be started with Docker Compose later...');
199
- return 'pending'; // Special value to indicate we'll handle it later
200
- }
201
-
202
- if (start.toLowerCase() === 'y') {
203
- try {
204
- // Check if a container named 'qdrant' already exists
205
- try {
206
- safeExec('docker', ['container', 'inspect', 'qdrant'], { stdio: 'ignore' });
207
- console.log('Removing existing Qdrant container...');
208
- safeExec('docker', ['rm', '-f', 'qdrant'], { stdio: 'ignore' });
209
- } catch {
210
- // Container doesn't exist, which is fine
211
- }
212
-
213
- console.log('Starting Qdrant...');
214
- safeExec('docker', ['run', '-d', '--name', 'qdrant', '-p', '6333:6333', '-v', 'qdrant_storage:/qdrant/storage', 'qdrant/qdrant:latest'], { stdio: 'inherit' });
215
-
216
- // Wait for Qdrant to be ready
217
- console.log('Waiting for Qdrant to start...');
218
- await new Promise(resolve => setTimeout(resolve, 3000)); // Initial wait for container to start
219
-
220
- let retries = 60; // Increase to 60 seconds
221
- while (retries > 0) {
222
- try {
223
- const response = await fetch('http://localhost:6333');
224
- const data = await response.json();
225
- if (data.title && data.title.includes('qdrant')) {
226
- console.log('āœ… Qdrant started successfully');
227
- return true;
228
- }
229
- } catch (e) {
230
- // Show progress every 10 attempts
231
- if (retries % 10 === 0) {
232
- console.log(` Still waiting... (${retries} seconds left)`);
233
- // Check if container is still running
234
- try {
235
- // Check if container is running without using shell pipes
236
- const psOutput = safeExec('docker', ['ps', '--filter', 'name=qdrant', '--format', '{{.Names}}'], { stdio: 'pipe' });
237
- if (!psOutput.includes('qdrant')) throw new Error('Container not running');
238
- } catch {
239
- console.log('āŒ Qdrant container stopped unexpectedly');
240
- return false;
241
- }
242
- }
243
- }
244
- await new Promise(resolve => setTimeout(resolve, 1000));
245
- retries--;
246
- }
247
-
248
- console.log('āŒ Qdrant failed to start properly');
249
- return false;
250
- } catch (error) {
251
- console.log('āŒ Failed to start Qdrant:', error.message);
252
- return false;
253
- }
254
- }
255
-
256
- return false;
257
- }
258
-
259
- async function setupPythonEnvironment() {
260
- console.log('\nšŸ Setting up Python MCP server...');
261
-
262
- // Detect WSL environment
263
- if (isWSL()) {
264
- console.log('🐧 WSL environment detected');
265
- debugLog('Running on WSL, may need special handling');
266
- }
267
-
268
- const mcpPath = join(projectRoot, 'mcp-server');
269
- const scriptsPath = join(projectRoot, 'scripts');
270
-
271
- try {
272
- // Check if venv already exists
273
- const venvPath = join(mcpPath, 'venv');
274
- let venvExists = false;
275
- let venvHealthy = false;
276
- let needsInstall = false;
277
-
278
- try {
279
- await fs.access(venvPath);
280
- venvExists = true;
281
-
282
- // Check if existing venv is healthy
283
- const venvPython = process.platform === 'win32'
284
- ? join(venvPath, 'Scripts', 'python.exe')
285
- : join(venvPath, 'bin', 'python');
286
-
287
- try {
288
- // Try to run python --version to verify venv is functional
289
- safeExec(venvPython, ['--version'], { stdio: 'pipe' });
290
-
291
- // Also check if MCP dependencies are installed
292
- try {
293
- safeExec(venvPython, ['-c', 'import fastmcp, qdrant_client'], { stdio: 'pipe' });
294
- venvHealthy = true;
295
- console.log('āœ… Virtual environment already exists and is healthy');
296
- } catch {
297
- // Dependencies not installed, need to install them
298
- console.log('āš ļø Virtual environment exists but missing dependencies');
299
- needsInstall = true;
300
- venvHealthy = true; // venv itself is healthy, just needs deps
301
- }
302
- } catch (healthError) {
303
- console.log('āš ļø Existing virtual environment is corrupted');
304
- console.log(' Removing and recreating...');
305
-
306
- // Remove broken venv
307
- fsSync.rmSync(venvPath, { recursive: true, force: true });
308
- venvExists = false;
309
- venvHealthy = false;
310
- }
311
- } catch {
312
- // venv doesn't exist, create it
313
- }
314
-
315
- if (!venvExists || !venvHealthy) {
316
- // Pre-flight checks before creating venv
317
- console.log('Checking Python environment...');
318
- const pythonCmd = process.env.PYTHON_PATH || 'python3';
319
-
320
- // Check if Python is available and get version
321
- try {
322
- const pythonVersion = safeExec(pythonCmd, ['--version'], { stdio: 'pipe' });
323
- debugLog(`Python version: ${pythonVersion.trim()}`);
324
- } catch (e) {
325
- console.log('āŒ Python not found');
326
- console.log('šŸ“š Fix: Install Python 3.10 or higher');
327
- return false;
328
- }
329
-
330
- // Check if venv module is available
331
- debugLog('Checking if venv module is available...');
332
- try {
333
- const venvCheck = spawnSync(pythonCmd, ['-c', 'import venv; print("venv module OK")'], {
334
- cwd: mcpPath,
335
- encoding: 'utf8'
336
- });
337
-
338
- if (venvCheck.status !== 0) {
339
- debugLog(`venv module check failed: ${venvCheck.stderr}`);
340
-
341
- // WSL-specific guidance
342
- if (isWSL()) {
343
- console.log('\nāš ļø WSL-specific issue detected');
344
- console.log('Common WSL fixes:');
345
- console.log('1. Install python3-venv: sudo apt install python3.10-venv');
346
- console.log('2. Use specific Python version: export PYTHON_PATH=python3.10');
347
- console.log('3. Check WSL Python paths: which python3');
348
- }
349
-
350
- throw new Error('venv module not available');
351
- }
352
- debugLog('venv module is available');
353
- } catch (moduleError) {
354
- console.log('āŒ Python venv module not found');
355
- console.log('šŸ“š Fix: Install python3-venv package');
356
- console.log(' Ubuntu/Debian: sudo apt install python3-venv');
357
- console.log(' WSL Ubuntu: sudo apt install python3.10-venv');
358
- console.log(' macOS: Should be included with Python');
359
- return false;
360
- }
361
-
362
- // Create virtual environment
363
- console.log('Creating virtual environment...');
364
- try {
365
- debugLog(`Running: ${pythonCmd} -m venv venv in ${mcpPath}`);
366
-
367
- const result = spawnSync(pythonCmd, ['-m', 'venv', 'venv'], {
368
- cwd: mcpPath,
369
- encoding: 'utf8'
370
- });
371
-
372
- if (result.error) {
373
- debugLog(`spawnSync error: ${result.error.message}`);
374
- throw result.error;
375
- }
376
-
377
- if (result.status !== 0) {
378
- debugLog(`venv creation failed with status ${result.status}`);
379
- debugLog(`stdout: ${result.stdout}`);
380
- debugLog(`stderr: ${result.stderr}`);
381
-
382
- // Check for common WSL issues
383
- if (isWSL() && result.stderr && result.stderr.includes('ensurepip')) {
384
- console.log('\nāš ļø WSL pip installation issue detected');
385
- console.log('Try: sudo apt install python3-pip python3.10-distutils');
386
- }
387
-
388
- throw new Error(`venv creation failed: ${result.stderr}`);
389
- }
390
-
391
- console.log('āœ… Virtual environment created');
392
- needsInstall = true; // Mark that we need to install dependencies
393
- } catch (venvError) {
394
- console.log('āš ļø Failed to create venv with python3, trying python...');
395
- try {
396
- const result = spawnSync('python', ['-m', 'venv', 'venv'], {
397
- cwd: mcpPath,
398
- encoding: 'utf8'
399
- });
400
-
401
- if (result.error) throw result.error;
402
- if (result.status !== 0) {
403
- debugLog(`Python venv creation failed: ${result.stderr}`);
404
- throw new Error(result.stderr);
405
- }
406
-
407
- needsInstall = true; // Mark that we need to install dependencies
408
- } catch (fallbackError) {
409
- console.log('āŒ Failed to create virtual environment');
410
-
411
- if (debugMode) {
412
- console.log('\nšŸ” Debug Information:');
413
- console.log(`Error: ${fallbackError.message}`);
414
- console.log(`Working directory: ${mcpPath}`);
415
- console.log(`Python command: ${pythonCmd}`);
416
- }
417
-
418
- console.log('\nšŸ“š Troubleshooting:');
419
- console.log('1. Check Python installation: python3 --version');
420
- console.log('2. Install venv package:');
421
- console.log(' Ubuntu/Debian: sudo apt install python3-venv');
422
- console.log(' WSL: sudo apt install python3.10-venv python3-pip');
423
- console.log('3. Try with specific Python: PYTHON_PATH=python3.10 npm run setup');
424
-
425
- return false;
426
- }
427
- }
428
- }
429
-
430
- // Setup paths for virtual environment
431
- const venvPython = process.platform === 'win32'
432
- ? join(mcpPath, 'venv', 'Scripts', 'python.exe')
433
- : join(mcpPath, 'venv', 'bin', 'python');
434
- const venvPip = process.platform === 'win32'
435
- ? join(mcpPath, 'venv', 'Scripts', 'pip.exe')
436
- : join(mcpPath, 'venv', 'bin', 'pip');
437
-
438
- // Only install dependencies if we just created a new venv
439
- if (needsInstall) {
440
- console.log('Setting up pip in virtual environment...');
441
-
442
- // First, try to install certifi to help with SSL issues
443
- console.log('Installing certificate handler...');
444
- try {
445
- safeExec(venvPip, [
446
- 'install', '--trusted-host', 'pypi.org', '--trusted-host', 'files.pythonhosted.org', 'certifi'
447
- ], { cwd: mcpPath, stdio: 'pipe' });
448
- } catch {
449
- // Continue even if certifi fails
450
- }
451
-
452
- // Upgrade pip and install wheel first
453
- try {
454
- // Use --no-cache-dir and --timeout to fail faster
455
- safeExec(venvPython, [
456
- '-m', 'pip', 'install', '--no-cache-dir', '--timeout', '5', '--retries', '1', '--upgrade', 'pip', 'wheel', 'setuptools'
457
- ], { cwd: mcpPath, stdio: 'pipe' });
458
- console.log('āœ… Pip upgraded successfully');
459
- } catch {
460
- // If upgrade fails due to SSL, skip it and continue
461
- console.log('āš ļø Pip upgrade failed (likely SSL issue), continuing with existing pip...');
462
- }
463
-
464
- // Now install dependencies
465
- console.log('Installing MCP server dependencies...');
466
- try {
467
- safeExec(venvPip, [
468
- 'install', '--no-cache-dir', '--timeout', '10', '--retries', '1', '-e', '.'
469
- ], { cwd: mcpPath, stdio: 'pipe' });
470
- console.log('āœ… MCP server dependencies installed');
471
- } catch (error) {
472
- // Check for SSL errors
473
- const errorStr = error.toString();
474
- if (errorStr.includes('SSL') || errorStr.includes('HTTPS') || errorStr.includes('ssl')) {
475
- console.log('āš ļø SSL error detected. Attempting automatic fix...');
476
-
477
- // Try different approaches to fix SSL
478
- const sslFixes = [
479
- {
480
- name: 'Using trusted host flags',
481
- install: () => safeExec(venvPip, [
482
- 'install', '--trusted-host', 'pypi.org', '--trusted-host', 'files.pythonhosted.org',
483
- '--no-cache-dir', '-e', '.'
484
- ], { cwd: mcpPath, stdio: 'pipe' })
485
- },
486
- {
487
- name: 'Using index-url without SSL',
488
- install: () => {
489
- safeExec(venvPip, ['config', 'set', 'global.index-url', 'https://pypi.org/simple/'],
490
- { cwd: mcpPath, stdio: 'pipe' });
491
- safeExec(venvPip, ['config', 'set', 'global.trusted-host', 'pypi.org files.pythonhosted.org'],
492
- { cwd: mcpPath, stdio: 'pipe' });
493
- return safeExec(venvPip, ['install', '--no-cache-dir', '-e', '.'],
494
- { cwd: mcpPath, stdio: 'pipe' });
495
- }
496
- }
497
- ];
498
-
499
- for (const fix of sslFixes) {
500
- console.log(`\n Trying: ${fix.name}...`);
501
- try {
502
- fix.install();
503
- console.log(' āœ… Success! Dependencies installed using workaround');
504
- return true;
505
- } catch (e) {
506
- console.log(' āŒ Failed');
507
- }
508
- }
509
-
510
- console.log('\nāŒ All automatic fixes failed');
511
- return false;
512
- } else {
513
- console.log('āŒ Failed to install dependencies');
514
- return false;
515
- }
516
- }
517
-
518
- // Install script dependencies
519
- console.log('Installing import script dependencies...');
520
- try {
521
- safeExec(venvPip, [
522
- 'install', '-r', join(scriptsPath, 'requirements.txt')
523
- ], { cwd: mcpPath, stdio: 'inherit' });
524
- } catch (error) {
525
- // Try with trusted host if SSL error
526
- try {
527
- safeExec(venvPip, [
528
- 'install', '--trusted-host', 'pypi.org', '--trusted-host', 'files.pythonhosted.org',
529
- '-r', join(scriptsPath, 'requirements.txt')
530
- ], { cwd: mcpPath, stdio: 'inherit' });
531
- } catch {
532
- console.log('āš ļø Could not install script dependencies automatically');
533
- console.log(' You may need to install them manually later');
534
- }
535
- }
536
- } // End of needsInstall block
537
-
538
- console.log('āœ… Python environment setup complete');
539
- return true;
540
- } catch (error) {
541
- console.log('āŒ Failed to setup Python environment:', error.message);
542
- return false;
543
- }
544
- }
545
-
546
- async function configureEnvironment() {
547
- console.log('\nšŸ” Configuring environment variables...');
548
-
549
- const envPath = join(projectRoot, '.env');
550
- let envContent = '';
551
- let hasValidApiKey = false;
552
-
553
- try {
554
- envContent = await fs.readFile(envPath, 'utf-8');
555
- } catch {
556
- // .env doesn't exist, create it
557
- }
558
-
559
- // Check if we have a command line API key
560
- if (voyageKey) {
561
- if (voyageKey.startsWith('pa-')) {
562
- console.log('āœ… Using API key from command line');
563
- envContent = envContent.replace(/VOYAGE_KEY=.*/g, '');
564
- envContent += `\nVOYAGE_KEY=${voyageKey}\n`;
565
- hasValidApiKey = true;
566
- } else {
567
- console.log('āŒ Invalid API key format. Voyage keys start with "pa-"');
568
- process.exit(1);
569
- }
570
- } else if (localMode) {
571
- console.log('šŸ  Running in local mode - API key not required');
572
- console.log(' Note: Semantic search will be disabled');
573
- hasValidApiKey = false; // Mark as false but don't fail
574
- } else {
575
- // Check if we already have a valid API key
576
- const existingKeyMatch = envContent.match(/VOYAGE_KEY=([^\s]+)/);
577
- if (existingKeyMatch && existingKeyMatch[1] && !existingKeyMatch[1].includes('your-')) {
578
- console.log('āœ… Found existing Voyage API key in .env file');
579
- hasValidApiKey = true;
580
- } else {
581
- // Need to get API key
582
- console.log('\nšŸ”‘ Voyage AI API Key Setup');
583
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━');
584
- console.log('Claude Self-Reflect uses Voyage AI for semantic search.');
585
- console.log('You\'ll need a free API key to continue.\n');
586
- console.log('šŸ“ Steps to get your API key:');
587
- console.log(' 1. Visit https://www.voyageai.com/');
588
- console.log(' 2. Click "Sign Up" (free account)');
589
- console.log(' 3. Go to API Keys section');
590
- console.log(' 4. Create a new API key');
591
- console.log(' 5. Copy the key (starts with "pa-")\n');
592
-
593
- if (isInteractive) {
594
- const inputKey = await question('Paste your Voyage AI key here (or press Enter to skip): ');
595
-
596
- if (inputKey && inputKey.trim() && inputKey !== 'n') {
597
- // Validate key format
598
- if (inputKey.trim().startsWith('pa-')) {
599
- envContent = envContent.replace(/VOYAGE_KEY=.*/g, '');
600
- envContent += `\nVOYAGE_KEY=${inputKey.trim()}\n`;
601
- hasValidApiKey = true;
602
- console.log('āœ… API key saved to .env file');
603
- } else {
604
- console.log('āš ļø Invalid key format. Voyage keys start with "pa-"');
605
- console.log(' You can add it manually to .env file later');
606
- }
607
- } else {
608
- console.log('\nāš ļø No API key provided');
609
- console.log(' Setup will continue, but you\'ll need to add it to .env file:');
610
- console.log(' VOYAGE_KEY=your-api-key-here');
611
- }
612
- } else {
613
- console.log('\nāš ļø Non-interactive mode: Cannot prompt for API key');
614
- console.log(' Please add your Voyage API key to the .env file:');
615
- console.log(' VOYAGE_KEY=your-api-key-here');
616
-
617
- // Create placeholder
618
- if (!envContent.includes('VOYAGE_KEY=')) {
619
- envContent += '\n# Get your free API key at https://www.voyageai.com/\nVOYAGE_KEY=your-voyage-api-key-here\n';
620
- }
621
- }
622
- }
623
- }
624
-
625
- // Set default Qdrant URL if not present
626
- if (!envContent.includes('QDRANT_URL=')) {
627
- envContent += 'QDRANT_URL=http://localhost:6333\n';
628
- }
629
-
630
- // Add other default settings if not present
631
- if (!envContent.includes('ENABLE_MEMORY_DECAY=')) {
632
- envContent += 'ENABLE_MEMORY_DECAY=false\n';
633
- }
634
- if (!envContent.includes('DECAY_WEIGHT=')) {
635
- envContent += 'DECAY_WEIGHT=0.3\n';
636
- }
637
- if (!envContent.includes('DECAY_SCALE_DAYS=')) {
638
- envContent += 'DECAY_SCALE_DAYS=90\n';
639
- }
640
- if (!envContent.includes('PREFER_LOCAL_EMBEDDINGS=')) {
641
- envContent += `PREFER_LOCAL_EMBEDDINGS=${localMode ? 'true' : 'false'}\n`;
642
- }
643
-
644
- await fs.writeFile(envPath, envContent.trim() + '\n');
645
- console.log('āœ… Environment file created/updated');
646
-
647
- return { apiKey: hasValidApiKey };
648
- }
649
-
650
- async function setupClaude() {
651
- console.log('\nšŸ¤– Claude Code MCP Configuration...');
652
-
653
- const runScript = join(projectRoot, 'mcp-server', 'run-mcp.sh');
654
-
655
- // Check if Claude CLI is available
656
- try {
657
- safeExec('which', ['claude'], { stdio: 'ignore' });
658
-
659
- // Try to add the MCP automatically
660
- try {
661
- const voyageKeyValue = voyageKey || process.env.VOYAGE_KEY || '';
662
- if (!voyageKeyValue && !localMode) {
663
- console.log('āš ļø No Voyage API key available for MCP configuration');
664
- console.log('\nAdd this to your Claude Code settings manually:');
665
- console.log('```bash');
666
- console.log(`claude mcp add claude-self-reflect "${runScript}" -e VOYAGE_KEY="<your-key>" -e QDRANT_URL="http://localhost:6333"`);
667
- console.log('```');
668
- return;
669
- }
670
-
671
- console.log('šŸ”§ Adding MCP to Claude Code...');
672
- const mcpCommand = localMode
673
- ? `claude mcp add claude-self-reflect "${runScript}" -e QDRANT_URL="http://localhost:6333"`
674
- : `claude mcp add claude-self-reflect "${runScript}" -e VOYAGE_KEY="${voyageKeyValue}" -e QDRANT_URL="http://localhost:6333"`;
675
-
676
- // Parse the MCP command properly
677
- const mcpArgs = ['mcp', 'add', 'claude-self-reflect', runScript];
678
- if (voyageKeyValue) {
679
- mcpArgs.push('-e', `VOYAGE_KEY=${voyageKeyValue}`);
680
- }
681
- mcpArgs.push('-e', 'QDRANT_URL=http://localhost:6333');
682
- if (localMode) {
683
- mcpArgs.push('-e', 'PREFER_LOCAL_EMBEDDINGS=true');
684
- }
685
- safeExec('claude', mcpArgs, { stdio: 'inherit' });
686
- console.log('āœ… MCP added successfully!');
687
- console.log('\nāš ļø You may need to restart Claude Code for the changes to take effect.');
688
-
689
- // Store that we've configured MCP
690
- mcpConfigured = true;
691
- } catch (error) {
692
- console.log('āš ļø Could not add MCP automatically');
693
- console.log('\nAdd this to your Claude Code settings manually:');
694
- console.log('```bash');
695
- console.log(`claude mcp add claude-self-reflect "${runScript}" -e VOYAGE_KEY="${voyageKey || '<your-key>'}" -e QDRANT_URL="http://localhost:6333"`);
696
- console.log('```');
697
- }
698
- } catch {
699
- // Claude CLI not installed
700
- console.log('āš ļø Claude CLI not found. Please install Claude Code first.');
701
- console.log('\nOnce installed, add this MCP:');
702
- console.log('```bash');
703
- console.log(`claude mcp add claude-self-reflect "${runScript}" -e VOYAGE_KEY="${voyageKey || '<your-key>'}" -e QDRANT_URL="http://localhost:6333"`);
704
- console.log('```');
705
- }
706
-
707
- console.log('\nThen restart Claude Code for the changes to take effect.');
708
- }
709
-
710
- async function installAgents() {
711
- console.log('\nšŸ¤– Installing Claude agents...');
712
-
713
- const agentsSource = join(projectRoot, '.claude', 'agents');
714
- const agentsDest = join(process.cwd(), '.claude', 'agents');
715
-
716
- if (agentsSource === agentsDest) {
717
- console.log('šŸ“¦ Skipping agent installation in package directory');
718
- return;
719
- }
720
-
721
- try {
722
- await fs.mkdir(path.dirname(agentsDest), { recursive: true });
723
-
724
- // Check if already exists
725
- try {
726
- await fs.access(agentsDest);
727
- console.log('āœ… Agents already installed');
728
- return;
729
- } catch {
730
- // Copy agents
731
- await fs.cp(agentsSource, agentsDest, { recursive: true });
732
- console.log('āœ… Agents installed to .claude/agents/');
733
- }
734
- } catch (error) {
735
- console.log('āš ļø Could not install agents:', error.message);
736
- }
737
- }
738
-
739
- async function showPreSetupInstructions() {
740
- console.log('šŸš€ Welcome to Claude Self-Reflect Setup!\n');
741
- console.log('This wizard will help you set up conversation memory for Claude.\n');
742
-
743
- console.log('šŸ“‹ Before we begin, you\'ll need:');
744
- console.log(' 1. Docker Desktop installed and running');
745
- console.log(' 2. Python 3.10 or higher');
746
-
747
- console.log('\nāš ļø IMPORTANT: Embedding Mode Choice');
748
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
749
- console.log('You must choose between Local or Cloud embeddings:');
750
- console.log('\nšŸ”’ Local Mode (Default):');
751
- console.log(' • Privacy: All processing on your machine');
752
- console.log(' • No API costs or internet required');
753
- console.log(' • Good accuracy for most use cases');
754
- console.log('\nā˜ļø Cloud Mode (Voyage AI):');
755
- console.log(' • Better search accuracy');
756
- console.log(' • Requires API key and internet');
757
- console.log(' • Conversations sent to Voyage for processing');
758
- console.log('\nāš ļø This choice is SEMI-PERMANENT. Switching later requires:');
759
- console.log(' • Re-importing all conversations (30+ minutes)');
760
- console.log(' • Separate storage for each mode');
761
- console.log(' • Cannot search across modes\n');
762
- console.log('For Cloud mode, run with: --voyage-key=<your-key>');
763
- console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
764
-
765
- if (isInteractive) {
766
- await question('Press Enter to continue...');
767
- }
768
- }
769
-
770
- async function importConversations() {
771
- console.log('\nšŸ“š Import Claude Conversations...');
772
- console.log(localMode ? 'šŸ  Using local embeddings for import' : '🌐 Using Voyage AI embeddings');
773
-
774
- // Check if Claude logs directory exists
775
- const logsDir = join(process.env.HOME || process.env.USERPROFILE, '.claude', 'projects');
776
- let hasConversations = false;
777
- let totalProjects = 0;
778
-
779
- try {
780
- await fs.access(logsDir);
781
- const projects = await fs.readdir(logsDir);
782
- const validProjects = projects.filter(p => !p.startsWith('.'));
783
- totalProjects = validProjects.length;
784
- if (totalProjects > 0) {
785
- hasConversations = true;
786
- console.log(`āœ… Found ${totalProjects} Claude projects`);
787
- }
788
- } catch {
789
- console.log('šŸ“­ No Claude conversations found yet');
790
- console.log(' Conversations will be imported automatically once you start using Claude Code');
791
- return;
792
- }
793
-
794
- if (!hasConversations) {
795
- console.log('šŸ“­ No Claude conversations found yet');
796
- console.log(' Conversations will be imported automatically once you start using Claude Code');
797
- return;
798
- }
799
-
800
- // Check if already imported or partially imported
801
- const stateFile = join(process.env.HOME || process.env.USERPROFILE, '.claude-self-reflect', 'imported-files.json');
802
- let importedProjects = 0;
803
- try {
804
- const stateData = await fs.readFile(stateFile, 'utf-8');
805
- const state = JSON.parse(stateData);
806
- if (state.projects && Object.keys(state.projects).length > 0) {
807
- importedProjects = Object.keys(state.projects).length;
808
-
809
- // Check if all projects are imported
810
- if (importedProjects >= totalProjects) {
811
- console.log('āœ… All conversations already imported');
812
- console.log(` ${importedProjects} projects in database`);
813
- return;
814
- } else {
815
- console.log(`šŸ“Š Partially imported: ${importedProjects}/${totalProjects} projects`);
816
- console.log(' Continuing import for remaining projects...');
817
- }
818
- }
819
- } catch {
820
- // State file doesn't exist, proceed with import
821
- }
822
-
823
- // Run import
824
- console.log('\nšŸ”„ Importing conversations...');
825
- console.log(' This may take a few minutes depending on your conversation history');
826
-
827
- try {
828
- const pythonCmd = process.env.PYTHON_PATH || 'python3';
829
- const importScript = join(projectRoot, 'scripts', 'import-conversations-unified.py');
830
-
831
- // Use the venv Python directly - platform specific
832
- let venvPython;
833
- if (process.platform === 'win32') {
834
- venvPython = join(projectRoot, 'mcp-server', 'venv', 'Scripts', 'python.exe');
835
- } else if (process.platform === 'darwin') {
836
- // macOS
837
- venvPython = join(projectRoot, 'mcp-server', 'venv', 'bin', 'python');
838
- } else {
839
- // Linux
840
- venvPython = join(projectRoot, 'mcp-server', 'venv', 'bin', 'python');
841
- }
842
-
843
- // Verify venv Python exists
844
- try {
845
- await fs.access(venvPython);
846
- } catch {
847
- console.log('āš ļø Virtual environment Python not found');
848
- console.log(' Please run the import manually:');
849
- console.log(' cd claude-self-reflect');
850
- console.log(' source mcp-server/venv/bin/activate');
851
- console.log(' python scripts/import-conversations-voyage.py');
852
- return;
853
- }
854
-
855
- const importProcess = spawn(venvPython, [importScript], {
856
- cwd: projectRoot,
857
- env: {
858
- ...process.env,
859
- VOYAGE_KEY: voyageKey || process.env.VOYAGE_KEY,
860
- QDRANT_URL: 'http://localhost:6333',
861
- LOGS_DIR: logsDir,
862
- STATE_FILE: stateFile
863
- },
864
- stdio: 'inherit'
865
- });
866
-
867
- await new Promise((resolve, reject) => {
868
- importProcess.on('exit', (code) => {
869
- if (code === 0) {
870
- console.log('\nāœ… Conversations imported successfully!');
871
- resolve();
872
- } else {
873
- console.log('\nāš ļø Import completed with warnings');
874
- console.log(' Some conversations may not have been imported');
875
- console.log(' You can run the import again later if needed');
876
- resolve(); // Don't fail setup if import has issues
877
- }
878
- });
879
-
880
- importProcess.on('error', (err) => {
881
- console.log('\nāš ļø Could not run import automatically');
882
- console.log(' You can import manually later');
883
- resolve(); // Don't fail setup
884
- });
885
- });
886
-
887
- } catch (error) {
888
- console.log('\nāš ļø Could not run import automatically:', error.message);
889
- console.log(' You can import conversations manually later');
890
- }
891
- }
892
-
893
- async function showSystemDashboard() {
894
- console.log('\nšŸ“Š System Health Dashboard');
895
- console.log('═══════════════════════════════════════════════════════════════\n');
896
-
897
- // Check system components
898
- const status = {
899
- docker: false,
900
- qdrant: false,
901
- python: false,
902
- venv: false,
903
- apiKey: false,
904
- imported: 0,
905
- total: 0,
906
- watcherInstalled: false,
907
- watcherRunning: false
908
- };
909
-
910
- // Docker status
911
- try {
912
- safeExec('docker', ['info'], { stdio: 'ignore' });
913
- status.docker = true;
914
- } catch {}
915
-
916
- // Qdrant status
917
- try {
918
- const response = await fetch('http://localhost:6333');
919
- const data = await response.json();
920
- if (data.title && data.title.includes('qdrant')) {
921
- status.qdrant = true;
922
- }
923
- } catch {}
924
-
925
- // Python status
926
- try {
927
- const pythonCmd = process.env.PYTHON_PATH || 'python3';
928
- safeExec(pythonCmd, ['--version'], { stdio: 'ignore' });
929
- status.python = true;
930
- } catch {}
931
-
932
- // Virtual environment status
933
- const venvPath = join(projectRoot, 'mcp-server', 'venv');
934
- try {
935
- await fs.access(venvPath);
936
- status.venv = true;
937
- } catch {}
938
-
939
- // API key status
940
- const envPath = join(projectRoot, '.env');
941
- try {
942
- const envContent = await fs.readFile(envPath, 'utf-8');
943
- const keyMatch = envContent.match(/VOYAGE_KEY=([^\s]+)/);
944
- if (keyMatch && keyMatch[1] && !keyMatch[1].includes('your-')) {
945
- status.apiKey = true;
946
- }
947
- } catch {}
948
-
949
- // Import status
950
- const logsDir = join(process.env.HOME || process.env.USERPROFILE, '.claude', 'projects');
951
- try {
952
- const projects = await fs.readdir(logsDir);
953
- status.total = projects.filter(p => !p.startsWith('.')).length;
954
- } catch {}
955
-
956
- const stateFile = join(projectRoot, 'config', 'imported-files.json');
957
- try {
958
- const stateData = await fs.readFile(stateFile, 'utf-8');
959
- const state = JSON.parse(stateData);
960
- if (state.projects) {
961
- status.imported = Object.keys(state.projects).length;
962
- }
963
- } catch {}
964
-
965
- // Get Qdrant collection statistics
966
- status.collections = 0;
967
- status.totalDocuments = 0;
968
- try {
969
- const collectionsResponse = await fetch('http://localhost:6333/collections');
970
- const collectionsData = await collectionsResponse.json();
971
- if (collectionsData.result && collectionsData.result.collections) {
972
- status.collections = collectionsData.result.collections.length;
973
-
974
- // Get document count from each collection
975
- for (const collection of collectionsData.result.collections) {
976
- try {
977
- const countResponse = await fetch(`http://localhost:6333/collections/${collection.name}`);
978
- const countData = await countResponse.json();
979
- if (countData.result && countData.result.points_count) {
980
- status.totalDocuments += countData.result.points_count;
981
- }
982
- } catch {}
983
- }
984
- }
985
- } catch {}
986
-
987
- // Check watcher logs for recent activity
988
- status.watcherErrors = [];
989
- status.lastImportTime = null;
990
- try {
991
- const watcherLogs = safeExec('docker', [
992
- 'logs', 'claude-reflection-watcher', '--tail', '50'
993
- ], { encoding: 'utf-8' });
994
-
995
- // Check for recent errors
996
- const errorMatches = watcherLogs.match(/ERROR.*Import failed.*/g);
997
- if (errorMatches) {
998
- status.watcherErrors = errorMatches.slice(-3); // Last 3 errors
999
- }
1000
-
1001
- // Check for successful imports
1002
- const successMatch = watcherLogs.match(/Successfully imported project: ([^\s]+)/g);
1003
- if (successMatch && successMatch.length > 0) {
1004
- const lastSuccess = successMatch[successMatch.length - 1];
1005
- status.lastImportTime = 'Recently';
1006
- }
1007
- } catch {}
1008
-
1009
- // Watcher status
1010
- try {
1011
- // Check if watcher script exists
1012
- const watcherScript = join(projectRoot, 'scripts', 'import-watcher.py');
1013
- await fs.access(watcherScript);
1014
- status.watcherInstalled = true;
1015
-
1016
- // Check if watcher is running via Docker
1017
- try {
1018
- const dockerStatus = safeExec('docker', [
1019
- 'ps', '--filter', 'name=claude-reflection-watcher', '--format', '{{.Names}}'
1020
- ], { cwd: projectRoot, encoding: 'utf-8' }).trim();
1021
-
1022
- if (dockerStatus.includes('watcher')) {
1023
- status.watcherRunning = true;
1024
- }
1025
- } catch {
1026
- // Docker compose not available or watcher not running
1027
- }
1028
- } catch {
1029
- status.watcherInstalled = false;
1030
- }
1031
-
1032
- // Display dashboard
1033
- console.log('šŸ”§ System Components:');
1034
- console.log(` Docker: ${status.docker ? 'āœ… Running' : 'āŒ Not running'}`);
1035
- console.log(` Qdrant: ${status.qdrant ? 'āœ… Running on port 6333' : 'āŒ Not accessible'}`);
1036
- console.log(` Python: ${status.python ? 'āœ… Installed' : 'āŒ Not found'}`);
1037
- console.log(` Virtual Env: ${status.venv ? 'āœ… Created' : 'āŒ Not created'}`);
1038
- console.log(` API Key: ${status.apiKey ? 'āœ… Configured' : localMode ? 'šŸ  Local mode' : 'āŒ Not configured'}`);
1039
-
1040
- console.log('\nšŸ“š Import Status:');
1041
- if (status.total === 0) {
1042
- console.log(' No Claude conversations found yet');
1043
- } else if (status.imported === 0 && status.collections === 0) {
1044
- console.log(` šŸ“­ Not started (${status.total} projects available)`);
1045
- } else if (status.imported < status.total || status.collections > 0) {
1046
- const percent = status.total > 0 ? Math.round((status.imported / status.total) * 100) : 0;
1047
- if (status.collections > 0) {
1048
- console.log(` šŸ”„ Active: ${status.collections} collections, ${status.totalDocuments} conversation chunks`);
1049
- }
1050
- if (status.total > 0) {
1051
- console.log(` šŸ“Š Projects: ${status.imported}/${status.total} (${percent}%)`);
1052
- console.log(` ā–“${'ā–“'.repeat(Math.floor(percent/5))}${'ā–‘'.repeat(20-Math.floor(percent/5))} ${percent}%`);
1053
- }
1054
- if (status.lastImportTime) {
1055
- console.log(` ā° Last import: ${status.lastImportTime}`);
1056
- }
1057
- } else {
1058
- console.log(` āœ… Complete: ${status.imported} projects, ${status.totalDocuments} chunks indexed`);
1059
- }
1060
-
1061
- console.log('\nšŸ”„ Continuous Import (Watcher):');
1062
- if (status.watcherInstalled) {
1063
- if (status.watcherRunning) {
1064
- console.log(' Status: āœ… Running in Docker');
1065
- } else {
1066
- console.log(' Status: ⚪ Available but not running');
1067
- console.log(' • Manual: python scripts/import-watcher.py');
1068
- console.log(' • Docker: docker compose --profile watch up -d');
1069
- }
1070
-
1071
- if (status.watcherErrors.length > 0) {
1072
- console.log(' āš ļø Recent errors:');
1073
- status.watcherErrors.forEach(err => {
1074
- const shortErr = err.replace(/.*ERROR.*?: /, '').substring(0, 60) + '...';
1075
- console.log(` • ${shortErr}`);
1076
- });
1077
- }
1078
- } else {
1079
- console.log(' Status: āŒ Not available');
1080
- }
1081
-
1082
- // Check for issues
1083
- const issues = [];
1084
- if (!status.docker) issues.push('Docker is not running');
1085
- if (!status.qdrant && status.docker) issues.push('Qdrant is not running');
1086
- if (!status.python) issues.push('Python is not installed');
1087
- if (!status.apiKey && !localMode) issues.push('Voyage API key not configured');
1088
-
1089
- if (issues.length > 0) {
1090
- console.log('\nāš ļø Issues Found:');
1091
- issues.forEach(issue => console.log(` • ${issue}`));
1092
- console.log('\nšŸ’” Run setup again to fix these issues');
1093
- } else if (status.imported < status.total) {
1094
- console.log('\nšŸ’” Setup will continue with import process...');
1095
- } else {
1096
- console.log('\nāœ… System is fully configured and healthy!');
1097
- }
1098
-
1099
- console.log('\n═══════════════════════════════════════════════════════════════\n');
1100
-
1101
- return {
1102
- healthy: issues.length === 0,
1103
- needsImport: status.imported < status.total,
1104
- issues
1105
- };
1106
- }
1107
-
1108
- async function setupWatcher() {
1109
- console.log('\nāš™ļø Setting up Continuous Import (Watcher)...');
1110
-
1111
- // Check if Docker compose file exists
1112
- const dockerComposeFile = join(projectRoot, 'docker-compose.yaml');
1113
-
1114
- // Check if Docker watcher is available
1115
- try {
1116
- await fs.access(dockerComposeFile);
1117
- console.log('āœ… Docker-based watcher available');
1118
-
1119
- // Watcher works with both local and cloud embeddings
1120
- console.log(localMode ? 'šŸ  Watcher will use local embeddings' : '🌐 Watcher will use Voyage AI embeddings');
1121
-
1122
- // Ask if user wants to enable watcher
1123
- let enableWatcher = 'y';
1124
- if (isInteractive) {
1125
- console.log('\nšŸ’” The watcher monitors for new conversations and imports them automatically.');
1126
- enableWatcher = await question('Enable continuous import watcher? (y/n): ');
1127
- }
1128
-
1129
- if (enableWatcher.toLowerCase() === 'y') {
1130
- // Check if docker-compose.yaml exists
1131
- const dockerComposeFile = join(projectRoot, 'docker-compose.yaml');
1132
- try {
1133
- await fs.access(dockerComposeFile);
1134
-
1135
- console.log('\n🐳 Starting watcher with Docker Compose...');
1136
- try {
1137
- // First ensure .env has VOYAGE_KEY to avoid warnings
1138
- const envPath = join(projectRoot, '.env');
1139
- const envContent = await fs.readFile(envPath, 'utf-8');
1140
- if (!envContent.includes('VOYAGE_KEY=') && voyageKey) {
1141
- await fs.appendFile(envPath, `\nVOYAGE_KEY=${voyageKey}\n`);
1142
- }
1143
-
1144
- // Clean up all existing containers first
1145
- console.log('🧹 Cleaning up existing containers...');
1146
- try {
1147
- // Stop all claude-reflection containers
1148
- try {
1149
- safeExec('docker', ['compose', 'down'], {
1150
- cwd: projectRoot,
1151
- stdio: 'pipe'
1152
- });
1153
- } catch {}
1154
-
1155
- // Also stop any standalone containers
1156
- try {
1157
- safeExec('docker', ['stop', 'claude-reflection-watcher', 'claude-reflection-qdrant', 'qdrant'], {
1158
- stdio: 'pipe'
1159
- });
1160
- } catch {}
1161
-
1162
- // Remove them
1163
- try {
1164
- safeExec('docker', ['rm', 'claude-reflection-watcher', 'claude-reflection-qdrant', 'qdrant'], {
1165
- stdio: 'pipe'
1166
- });
1167
- } catch {}
1168
-
1169
- // Wait a moment for cleanup
1170
- await new Promise(resolve => setTimeout(resolve, 2000));
1171
- } catch {}
1172
-
1173
- // Start both services with compose
1174
- console.log('šŸš€ Starting Qdrant and Watcher services...');
1175
- safeExec('docker', ['compose', '--profile', 'watch', 'up', '-d'], {
1176
- cwd: projectRoot,
1177
- stdio: 'pipe' // Use pipe to capture output
1178
- });
1179
-
1180
- console.log('ā³ Waiting for containers to start...');
1181
- await new Promise(resolve => setTimeout(resolve, 8000)); // Give more time
1182
-
1183
- // Check container status
1184
- try {
1185
- const psOutput = safeExec('docker', [
1186
- 'ps', '--filter', 'name=claude-reflection', '--format', 'table {{.Names}}\t{{.Status}}'
1187
- ], { cwd: projectRoot, encoding: 'utf8' });
1188
-
1189
- const qdrantReady = psOutput.includes('claude-reflection-qdrant') && psOutput.includes('Up');
1190
- const watcherReady = psOutput.includes('claude-reflection-watcher') && psOutput.includes('Up');
1191
-
1192
- if (qdrantReady && watcherReady) {
1193
- console.log('āœ… All services started successfully!');
1194
- console.log(' • Qdrant is ready for storing conversations');
1195
- console.log(' • Watcher will check for new conversations every 60 seconds');
1196
- console.log('\nšŸ“Š Container Status:');
1197
- console.log(psOutput);
1198
- console.log('\nšŸ“ Useful commands:');
1199
- console.log(' Check status: docker compose ps');
1200
- console.log(' View logs: docker compose logs -f watcher');
1201
- console.log(' Stop services: docker compose --profile watch down');
1202
- } else if (qdrantReady) {
1203
- console.log('āœ… Qdrant started successfully');
1204
- console.log('ā³ Watcher is still starting...');
1205
- console.log('\nšŸ“ Check full status with:');
1206
- console.log(' docker compose ps');
1207
- console.log(' docker compose logs watcher');
1208
- } else {
1209
- console.log('ā³ Services are still starting...');
1210
- console.log('\nšŸ“ Check status with:');
1211
- console.log(' docker compose ps');
1212
- console.log(' docker compose logs');
1213
- }
1214
- } catch (statusError) {
1215
- console.log('āœ… Services deployment initiated');
1216
- console.log('\nšŸ“ Check status with:');
1217
- console.log(' docker compose ps');
1218
- }
1219
- } catch (error) {
1220
- console.log('āš ļø Could not start watcher automatically');
1221
- console.log('Error:', error.message);
1222
- console.log('\nšŸ“ To start manually, run:');
1223
- console.log(' cd claude-self-reflect');
1224
- console.log(' docker compose --profile watch up -d');
1225
- }
1226
- } catch {
1227
- // Fallback to manual Python execution
1228
- console.log('\nšŸ“ To enable the watcher, run:');
1229
- console.log(' cd claude-self-reflect');
1230
- console.log(' docker compose --profile watch up -d');
1231
- }
1232
- } else {
1233
- console.log('\nšŸ“ You can enable the watcher later by running:');
1234
- console.log(' docker compose --profile watch up -d');
1235
- }
1236
- } catch {
1237
- console.log('āš ļø Docker compose not found');
1238
- console.log(' The watcher requires Docker to run continuously');
1239
- }
1240
- }
1241
-
1242
- async function verifyMCP() {
1243
- console.log('\nšŸ” Verifying MCP Installation...');
1244
-
1245
- // Skip verification if MCP wasn't configured
1246
- if (!mcpConfigured) {
1247
- console.log('āš ļø MCP was not automatically configured. Please add it manually and verify.');
1248
- return;
1249
- }
1250
-
1251
- try {
1252
- // Check if MCP is listed
1253
- const mcpList = safeExec('claude', ['mcp', 'list'], { encoding: 'utf8' });
1254
- if (!mcpList.includes('claude-self-reflect')) {
1255
- console.log('āŒ MCP not found in Claude Code');
1256
- return;
1257
- }
1258
-
1259
- console.log('āœ… MCP is installed in Claude Code');
1260
-
1261
- // Create a test verification script
1262
- const testScript = `#!/usr/bin/env python3
1263
- import asyncio
1264
- import sys
1265
- import os
1266
-
1267
- # Add the mcp-server src to the path
1268
- sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'mcp-server', 'src'))
1269
-
1270
- async def test_mcp():
1271
- try:
1272
- # Import the server module
1273
- from server_v2 import mcp, get_voyage_collections
1274
-
1275
- # Check that MCP is loaded
1276
- print(f"āœ… MCP server loaded successfully!")
1277
- print(f" - Server name: {mcp.name}")
1278
-
1279
- # Check for our specific tools by trying to access them
1280
- tool_names = ['reflect_on_past', 'store_reflection']
1281
- found_tools = []
1282
-
1283
- # FastMCP doesn't expose tools list directly, so we'll check if imports worked
1284
- try:
1285
- from server_v2 import reflect_on_past, store_reflection
1286
- found_tools = tool_names
1287
- except ImportError:
1288
- pass
1289
- if 'reflect_on_past' in found_tools:
1290
- print("āœ… Tool 'reflect_on_past' is available")
1291
- else:
1292
- print("āŒ Tool 'reflect_on_past' not found")
1293
-
1294
- if 'store_reflection' in found_tools:
1295
- print("āœ… Tool 'store_reflection' is available")
1296
- else:
1297
- print("āŒ Tool 'store_reflection' not found")
1298
-
1299
- # Test that we can connect to Qdrant
1300
- try:
1301
- collections = await get_voyage_collections()
1302
- print(f"āœ… Connected to Qdrant: {len(collections)} collections found")
1303
- if len(collections) == 0:
1304
- print(" (This is normal if no conversations have been imported yet)")
1305
- except Exception as e:
1306
- print(f"āš ļø Qdrant connection: {e}")
1307
-
1308
- except Exception as e:
1309
- print(f"āŒ Failed to load MCP server: {e}")
1310
- sys.exit(1)
1311
-
1312
- # Run the async test
1313
- asyncio.run(test_mcp())
1314
- `;
1315
-
1316
- // Write test script
1317
- const testPath = join(projectRoot, 'test-mcp.py');
1318
- await fs.writeFile(testPath, testScript, { mode: 0o755 });
1319
-
1320
- // Run the test
1321
- console.log('\n🧪 Testing MCP functionality...');
1322
- try {
1323
- // Create test script that activates venv and runs test
1324
- const testScriptPath = join(projectRoot, 'run-test.sh');
1325
- const testScript = `#!/bin/bash\nsource mcp-server/venv/bin/activate\npython test-mcp.py`;
1326
- await fs.writeFile(testScriptPath, testScript, { mode: 0o755 });
1327
- const testResult = safeExec('bash', [testScriptPath], {
1328
- cwd: projectRoot,
1329
- encoding: 'utf8'
1330
- });
1331
- await fs.unlink(testScriptPath);
1332
- console.log(testResult);
1333
-
1334
- // Clean up test script
1335
- await fs.unlink(testPath);
1336
-
1337
- console.log('\nāœ… MCP verification complete! The reflection tools are working.');
1338
- } catch (error) {
1339
- console.log('āŒ MCP test failed:', error.message);
1340
- console.log('\nāš ļø The MCP may need to be restarted in Claude Code.');
1341
-
1342
- // Clean up test script
1343
- try { await fs.unlink(testPath); } catch {}
1344
- }
1345
-
1346
- } catch (error) {
1347
- console.log('āš ļø Could not verify MCP:', error.message);
1348
- console.log('\nPlease verify manually by:');
1349
- console.log('1. Restarting Claude Code');
1350
- console.log('2. Checking that the reflection tools appear in Claude');
1351
- }
1352
- }
1353
-
1354
- async function main() {
1355
- // Show dashboard first if system is partially configured
1356
- const venvExists = await fs.access(join(projectRoot, 'mcp-server', 'venv')).then(() => true).catch(() => false);
1357
- const envExists = await fs.access(join(projectRoot, '.env')).then(() => true).catch(() => false);
1358
-
1359
- if (venvExists || envExists) {
1360
- // System has been partially configured, show dashboard
1361
- const dashboardStatus = await showSystemDashboard();
1362
-
1363
- if (dashboardStatus.healthy && !dashboardStatus.needsImport) {
1364
- // Everything is already set up
1365
- if (isInteractive) {
1366
- const proceed = await question('System is already configured. Continue with setup anyway? (y/n): ');
1367
- if (proceed.toLowerCase() !== 'y') {
1368
- console.log('\nšŸ‘‹ Setup cancelled. Your system is already configured!');
1369
- if (rl) rl.close();
1370
- process.exit(0);
1371
- }
1372
- }
1373
- }
1374
- }
1375
-
1376
- // In non-interactive mode, just use defaults (local mode unless key provided)
1377
- if (!isInteractive) {
1378
- console.log(voyageKey ? '🌐 Using Voyage AI embeddings' : 'šŸ”’ Using local embeddings for privacy');
1379
- } else if (!voyageKey) {
1380
- // In interactive mode without a key, confirm local mode choice
1381
- await showPreSetupInstructions();
1382
- const confirmLocal = await question('Continue with Local embeddings (privacy mode)? (y/n): ');
1383
- if (confirmLocal.toLowerCase() !== 'y') {
1384
- console.log('\nTo use Cloud mode, restart with: claude-self-reflect setup --voyage-key=<your-key>');
1385
- console.log('Get your free API key at: https://www.voyageai.com/');
1386
- if (rl) rl.close();
1387
- process.exit(0);
1388
- }
1389
- } else {
1390
- await showPreSetupInstructions();
1391
- }
1392
-
1393
- // Check prerequisites
1394
- const pythonOk = await checkPython();
1395
- if (!pythonOk) {
1396
- console.log('\nāŒ Setup cannot continue without Python');
1397
- console.log('\nšŸ“‹ Fix Required:');
1398
- console.log(' 1. Install Python 3.10+ from https://python.org');
1399
- console.log(' 2. Ensure python3 is in your PATH');
1400
- console.log('\nšŸ”„ After fixing, run again:');
1401
- console.log(' claude-self-reflect setup');
1402
- process.exit(1);
1403
- }
1404
-
1405
- // Check Docker and Qdrant
1406
- const qdrantOk = await checkQdrant();
1407
- if (qdrantOk === false) {
1408
- console.log('\nāŒ Setup cannot continue without Qdrant');
1409
- console.log('\nšŸ“‹ Fix Required - Choose one:');
1410
- console.log('\n Option 1: Start Docker Desktop');
1411
- console.log(' - Open Docker Desktop application');
1412
- console.log(' - Wait for it to fully start (green icon)');
1413
- console.log('\n Option 2: Manually start Qdrant');
1414
- console.log(' - docker run -d --name qdrant -p 6333:6333 qdrant/qdrant:latest');
1415
- console.log('\nšŸ”„ After fixing, run again:');
1416
- console.log(' claude-self-reflect setup');
1417
-
1418
- if (rl) rl.close();
1419
- process.exit(1);
1420
- }
1421
- // If qdrantOk is 'pending', we'll start it with docker-compose later
1422
-
1423
- // Setup Python environment
1424
- const pythonEnvOk = await setupPythonEnvironment();
1425
- if (!pythonEnvOk) {
1426
- console.log('\nāŒ Python environment setup failed');
1427
- console.log('\nšŸ“‹ Fix Required:');
1428
- console.log('\n For SSL/HTTPS errors:');
1429
- console.log(' - macOS: brew reinstall python@3.10');
1430
- console.log(' - Ubuntu: sudo apt-get install python3-dev libssl-dev');
1431
- console.log(' - Or use a different Python installation');
1432
- console.log('\n For venv errors:');
1433
- console.log(' - Ubuntu: sudo apt install python3-venv');
1434
- console.log(' - macOS: Should be included with Python');
1435
- console.log('\nšŸ”„ After fixing, run again:');
1436
- console.log(' claude-self-reflect setup');
1437
-
1438
- if (rl) rl.close();
1439
- process.exit(1);
1440
- }
1441
-
1442
- // Configure environment
1443
- const envOk = await configureEnvironment();
1444
- if (!localMode && (!envOk || !envOk.apiKey)) {
1445
- console.log('\nāš ļø No Voyage API key configured');
1446
- console.log('\nšŸ“‹ Next Steps:');
1447
- console.log(' 1. Get your free API key from https://www.voyageai.com/');
1448
- console.log(' 2. Add it to the .env file:');
1449
- console.log(' VOYAGE_KEY=your-api-key-here');
1450
- console.log('\nšŸ”„ After adding the key, run again:');
1451
- console.log(' claude-self-reflect setup');
1452
- console.log('\nšŸ’” Or run in local mode:');
1453
- console.log(' claude-self-reflect setup --local');
1454
-
1455
- if (rl) rl.close();
1456
- process.exit(1);
1457
- }
1458
-
1459
- // Install agents
1460
- await installAgents();
1461
-
1462
- // Show Claude configuration
1463
- await setupClaude();
1464
-
1465
- // Import conversations
1466
- await importConversations();
1467
-
1468
- // Setup watcher for continuous import
1469
- await setupWatcher();
1470
-
1471
- // Verify MCP installation
1472
- await verifyMCP();
1473
-
1474
- console.log('\nāœ… Setup complete!');
1475
- console.log('\nNext steps:');
1476
- if (!mcpConfigured) {
1477
- console.log('1. Add the MCP to Claude Code manually (see instructions above)');
1478
- console.log('2. Restart Claude Code');
1479
- console.log('3. Start using the reflection tools!');
1480
- } else {
1481
- console.log('1. Restart Claude Code if needed');
1482
- console.log('2. Start using the reflection tools!');
1483
- console.log(' - Ask about past conversations');
1484
- console.log(' - Store important insights');
1485
- }
1486
- console.log('\nFor more info: https://github.com/ramakay/claude-self-reflect');
1487
-
1488
- if (rl) rl.close();
1489
- process.exit(0);
1490
- }
1491
-
1492
- main().catch(console.error);
11
+ // Simply forward to the Docker-based wizard
12
+ import(join(__dirname, 'setup-wizard-docker.js'));