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.
- package/.claude/agents/README.md +20 -3
- package/.claude/agents/open-source-maintainer.md +287 -11
- package/.claude/agents/reflect-tester.md +278 -0
- package/.env.example +29 -0
- package/Dockerfile.importer +13 -0
- package/Dockerfile.importer-isolated +20 -0
- package/Dockerfile.mcp-server +17 -0
- package/Dockerfile.streaming-importer +30 -0
- package/Dockerfile.watcher +53 -0
- package/README.md +15 -2
- package/docker-compose.yaml +98 -0
- package/installer/setup-wizard-docker.js +433 -0
- package/installer/setup-wizard.js +4 -1484
- package/mcp-server/run-mcp-docker.sh +5 -0
- package/mcp-server/src/server_v2.py +11 -7
- package/package.json +5 -1
- package/scripts/import-conversations-unified.py +16 -1
|
@@ -1,1492 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
//
|
|
16
|
-
|
|
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'));
|