claude-self-reflect 2.3.2 โ 2.3.4
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/reflection-specialist.md +56 -40
- package/README.md +34 -10
- package/installer/setup-wizard.js +187 -108
- package/mcp-server/pyproject.toml +6 -5
- package/mcp-server/src/server.py +112 -25
- package/package.json +1 -1
- package/scripts/import-conversations-unified.py +269 -0
- package/scripts/import-recent-only.py +5 -1
- package/scripts/import-watcher.py +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { execSync, spawn } from 'child_process';
|
|
3
|
+
import { execSync, spawn, spawnSync } from 'child_process';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import { dirname, join } from 'path';
|
|
6
6
|
import fs from 'fs/promises';
|
|
@@ -11,20 +11,42 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
11
11
|
const __dirname = dirname(__filename);
|
|
12
12
|
const projectRoot = join(__dirname, '..');
|
|
13
13
|
|
|
14
|
+
// Safe command execution helper
|
|
15
|
+
function safeExec(command, args = [], options = {}) {
|
|
16
|
+
const result = spawnSync(command, args, {
|
|
17
|
+
...options,
|
|
18
|
+
shell: false // Never use shell to prevent injection
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
if (result.error) {
|
|
22
|
+
throw result.error;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (result.status !== 0) {
|
|
26
|
+
const error = new Error(`Command failed: ${command} ${args.join(' ')}`);
|
|
27
|
+
error.stdout = result.stdout;
|
|
28
|
+
error.stderr = result.stderr;
|
|
29
|
+
error.status = result.status;
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return result.stdout?.toString() || '';
|
|
34
|
+
}
|
|
35
|
+
|
|
14
36
|
// Parse command line arguments
|
|
15
37
|
const args = process.argv.slice(2);
|
|
16
38
|
let voyageKey = null;
|
|
17
|
-
let localMode = false;
|
|
18
39
|
let mcpConfigured = false;
|
|
19
40
|
|
|
20
41
|
for (const arg of args) {
|
|
21
42
|
if (arg.startsWith('--voyage-key=')) {
|
|
22
43
|
voyageKey = arg.split('=')[1];
|
|
23
|
-
} else if (arg === '--local') {
|
|
24
|
-
localMode = true;
|
|
25
44
|
}
|
|
26
45
|
}
|
|
27
46
|
|
|
47
|
+
// Default to local mode unless Voyage key is provided
|
|
48
|
+
let localMode = !voyageKey;
|
|
49
|
+
|
|
28
50
|
const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
|
|
29
51
|
|
|
30
52
|
const rl = isInteractive ? readline.createInterface({
|
|
@@ -43,31 +65,39 @@ const question = (query) => {
|
|
|
43
65
|
async function checkPython() {
|
|
44
66
|
console.log('\n๐ฆ Checking Python installation...');
|
|
45
67
|
try {
|
|
46
|
-
const version =
|
|
68
|
+
const version = safeExec('python3', ['--version']).trim();
|
|
47
69
|
console.log(`โ
Found ${version}`);
|
|
48
70
|
|
|
49
71
|
// Check if SSL module works
|
|
50
72
|
try {
|
|
51
|
-
|
|
73
|
+
safeExec('python3', ['-c', 'import ssl'], { stdio: 'pipe' });
|
|
52
74
|
return true;
|
|
53
75
|
} catch (sslError) {
|
|
54
76
|
console.log('โ ๏ธ Python SSL module not working');
|
|
55
77
|
|
|
56
78
|
// Check if we're using pyenv
|
|
57
|
-
const whichPython =
|
|
79
|
+
const whichPython = safeExec('which', ['python3']).trim();
|
|
58
80
|
if (whichPython.includes('pyenv')) {
|
|
59
81
|
console.log('๐ Detected pyenv Python with broken SSL');
|
|
60
82
|
|
|
61
83
|
// Check if brew Python is available
|
|
62
84
|
try {
|
|
63
|
-
|
|
85
|
+
let brewPrefix = '';
|
|
86
|
+
for (const pythonVersion of ['python@3.11', 'python@3.10', 'python@3.12']) {
|
|
87
|
+
try {
|
|
88
|
+
brewPrefix = safeExec('brew', ['--prefix', pythonVersion]).trim();
|
|
89
|
+
if (brewPrefix) break;
|
|
90
|
+
} catch {}
|
|
91
|
+
}
|
|
64
92
|
if (brewPrefix) {
|
|
65
93
|
// Find the actual python executable
|
|
66
94
|
let pythonPath = null;
|
|
67
95
|
for (const exe of ['python3.11', 'python3.10', 'python3.12', 'python3']) {
|
|
68
96
|
try {
|
|
69
97
|
const fullPath = `${brewPrefix}/bin/${exe}`;
|
|
70
|
-
|
|
98
|
+
// Use fs.existsSync instead of shell test command
|
|
99
|
+
const { existsSync } = await import('fs');
|
|
100
|
+
if (!existsSync(fullPath)) throw new Error('File not found');
|
|
71
101
|
pythonPath = fullPath;
|
|
72
102
|
break;
|
|
73
103
|
} catch {}
|
|
@@ -77,7 +107,7 @@ async function checkPython() {
|
|
|
77
107
|
console.log(`โ
Found brew Python at ${pythonPath}`);
|
|
78
108
|
// Test if SSL works with brew Python
|
|
79
109
|
try {
|
|
80
|
-
|
|
110
|
+
safeExec(pythonPath, ['-c', 'import ssl'], { stdio: 'pipe' });
|
|
81
111
|
process.env.PYTHON_PATH = pythonPath;
|
|
82
112
|
return true;
|
|
83
113
|
} catch {
|
|
@@ -89,8 +119,8 @@ async function checkPython() {
|
|
|
89
119
|
|
|
90
120
|
console.log('\n๐ง Attempting to install Python with brew...');
|
|
91
121
|
try {
|
|
92
|
-
|
|
93
|
-
const brewPython =
|
|
122
|
+
safeExec('brew', ['install', 'python@3.11'], { stdio: 'inherit' });
|
|
123
|
+
const brewPython = safeExec('brew', ['--prefix', 'python@3.11']).trim();
|
|
94
124
|
process.env.PYTHON_PATH = `${brewPython}/bin/python3`;
|
|
95
125
|
console.log('โ
Installed Python 3.11 with brew');
|
|
96
126
|
return true;
|
|
@@ -110,7 +140,7 @@ async function checkPython() {
|
|
|
110
140
|
|
|
111
141
|
async function checkDocker() {
|
|
112
142
|
try {
|
|
113
|
-
|
|
143
|
+
safeExec('docker', ['info'], { stdio: 'ignore' });
|
|
114
144
|
return true;
|
|
115
145
|
} catch {
|
|
116
146
|
return false;
|
|
@@ -152,15 +182,15 @@ async function checkQdrant() {
|
|
|
152
182
|
try {
|
|
153
183
|
// Check if a container named 'qdrant' already exists
|
|
154
184
|
try {
|
|
155
|
-
|
|
185
|
+
safeExec('docker', ['container', 'inspect', 'qdrant'], { stdio: 'ignore' });
|
|
156
186
|
console.log('Removing existing Qdrant container...');
|
|
157
|
-
|
|
187
|
+
safeExec('docker', ['rm', '-f', 'qdrant'], { stdio: 'ignore' });
|
|
158
188
|
} catch {
|
|
159
189
|
// Container doesn't exist, which is fine
|
|
160
190
|
}
|
|
161
191
|
|
|
162
192
|
console.log('Starting Qdrant...');
|
|
163
|
-
|
|
193
|
+
safeExec('docker', ['run', '-d', '--name', 'qdrant', '-p', '6333:6333', '-v', 'qdrant_storage:/qdrant/storage', 'qdrant/qdrant:latest'], { stdio: 'inherit' });
|
|
164
194
|
|
|
165
195
|
// Wait for Qdrant to be ready
|
|
166
196
|
console.log('Waiting for Qdrant to start...');
|
|
@@ -181,7 +211,9 @@ async function checkQdrant() {
|
|
|
181
211
|
console.log(` Still waiting... (${retries} seconds left)`);
|
|
182
212
|
// Check if container is still running
|
|
183
213
|
try {
|
|
184
|
-
|
|
214
|
+
// Check if container is running without using shell pipes
|
|
215
|
+
const psOutput = safeExec('docker', ['ps', '--filter', 'name=qdrant', '--format', '{{.Names}}'], { stdio: 'pipe' });
|
|
216
|
+
if (!psOutput.includes('qdrant')) throw new Error('Container not running');
|
|
185
217
|
} catch {
|
|
186
218
|
console.log('โ Qdrant container stopped unexpectedly');
|
|
187
219
|
return false;
|
|
@@ -226,11 +258,22 @@ async function setupPythonEnvironment() {
|
|
|
226
258
|
console.log('Creating virtual environment...');
|
|
227
259
|
const pythonCmd = process.env.PYTHON_PATH || 'python3';
|
|
228
260
|
try {
|
|
229
|
-
|
|
261
|
+
// Use spawn with proper path handling instead of shell execution
|
|
262
|
+
const { spawnSync } = require('child_process');
|
|
263
|
+
const result = spawnSync(pythonCmd, ['-m', 'venv', 'venv'], {
|
|
264
|
+
cwd: mcpPath,
|
|
265
|
+
stdio: 'inherit'
|
|
266
|
+
});
|
|
267
|
+
if (result.error) throw result.error;
|
|
230
268
|
} catch (venvError) {
|
|
231
269
|
console.log('โ ๏ธ Failed to create venv with python3, trying python...');
|
|
232
270
|
try {
|
|
233
|
-
|
|
271
|
+
const { spawnSync } = require('child_process');
|
|
272
|
+
const result = spawnSync('python', ['-m', 'venv', 'venv'], {
|
|
273
|
+
cwd: mcpPath,
|
|
274
|
+
stdio: 'inherit'
|
|
275
|
+
});
|
|
276
|
+
if (result.error) throw result.error;
|
|
234
277
|
} catch {
|
|
235
278
|
console.log('โ Failed to create virtual environment');
|
|
236
279
|
console.log('๐ Fix: Install python3-venv package');
|
|
@@ -241,19 +284,21 @@ async function setupPythonEnvironment() {
|
|
|
241
284
|
}
|
|
242
285
|
}
|
|
243
286
|
|
|
244
|
-
//
|
|
287
|
+
// Setup paths for virtual environment
|
|
245
288
|
console.log('Setting up pip in virtual environment...');
|
|
246
|
-
const
|
|
247
|
-
? 'venv
|
|
248
|
-
: '
|
|
289
|
+
const venvPython = process.platform === 'win32'
|
|
290
|
+
? join(mcpPath, 'venv', 'Scripts', 'python.exe')
|
|
291
|
+
: join(mcpPath, 'venv', 'bin', 'python');
|
|
292
|
+
const venvPip = process.platform === 'win32'
|
|
293
|
+
? join(mcpPath, 'venv', 'Scripts', 'pip.exe')
|
|
294
|
+
: join(mcpPath, 'venv', 'bin', 'pip');
|
|
249
295
|
|
|
250
296
|
// First, try to install certifi to help with SSL issues
|
|
251
297
|
console.log('Installing certificate handler...');
|
|
252
298
|
try {
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
});
|
|
299
|
+
safeExec(venvPip, [
|
|
300
|
+
'install', '--trusted-host', 'pypi.org', '--trusted-host', 'files.pythonhosted.org', 'certifi'
|
|
301
|
+
], { cwd: mcpPath, stdio: 'pipe' });
|
|
257
302
|
} catch {
|
|
258
303
|
// Continue even if certifi fails
|
|
259
304
|
}
|
|
@@ -261,10 +306,9 @@ async function setupPythonEnvironment() {
|
|
|
261
306
|
// Upgrade pip and install wheel first
|
|
262
307
|
try {
|
|
263
308
|
// Use --no-cache-dir and --timeout to fail faster
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
});
|
|
309
|
+
safeExec(venvPython, [
|
|
310
|
+
'-m', 'pip', 'install', '--no-cache-dir', '--timeout', '5', '--retries', '1', '--upgrade', 'pip', 'wheel', 'setuptools'
|
|
311
|
+
], { cwd: mcpPath, stdio: 'pipe' });
|
|
268
312
|
console.log('โ
Pip upgraded successfully');
|
|
269
313
|
} catch {
|
|
270
314
|
// If upgrade fails due to SSL, skip it and continue
|
|
@@ -274,10 +318,9 @@ async function setupPythonEnvironment() {
|
|
|
274
318
|
// Now install dependencies
|
|
275
319
|
console.log('Installing MCP server dependencies...');
|
|
276
320
|
try {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
});
|
|
321
|
+
safeExec(venvPip, [
|
|
322
|
+
'install', '--no-cache-dir', '--timeout', '10', '--retries', '1', '-e', '.'
|
|
323
|
+
], { cwd: mcpPath, stdio: 'pipe' });
|
|
281
324
|
console.log('โ
MCP server dependencies installed');
|
|
282
325
|
} catch (error) {
|
|
283
326
|
// Check for SSL errors
|
|
@@ -286,29 +329,31 @@ async function setupPythonEnvironment() {
|
|
|
286
329
|
console.log('โ ๏ธ SSL error detected. Attempting automatic fix...');
|
|
287
330
|
|
|
288
331
|
// Try different approaches to fix SSL
|
|
289
|
-
const
|
|
332
|
+
const sslFixes = [
|
|
290
333
|
{
|
|
291
334
|
name: 'Using trusted host flags',
|
|
292
|
-
|
|
335
|
+
install: () => safeExec(venvPip, [
|
|
336
|
+
'install', '--trusted-host', 'pypi.org', '--trusted-host', 'files.pythonhosted.org',
|
|
337
|
+
'--no-cache-dir', '-e', '.'
|
|
338
|
+
], { cwd: mcpPath, stdio: 'pipe' })
|
|
293
339
|
},
|
|
294
340
|
{
|
|
295
341
|
name: 'Using index-url without SSL',
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
342
|
+
install: () => {
|
|
343
|
+
safeExec(venvPip, ['config', 'set', 'global.index-url', 'https://pypi.org/simple/'],
|
|
344
|
+
{ cwd: mcpPath, stdio: 'pipe' });
|
|
345
|
+
safeExec(venvPip, ['config', 'set', 'global.trusted-host', 'pypi.org files.pythonhosted.org'],
|
|
346
|
+
{ cwd: mcpPath, stdio: 'pipe' });
|
|
347
|
+
return safeExec(venvPip, ['install', '--no-cache-dir', '-e', '.'],
|
|
348
|
+
{ cwd: mcpPath, stdio: 'pipe' });
|
|
349
|
+
}
|
|
301
350
|
}
|
|
302
351
|
];
|
|
303
352
|
|
|
304
|
-
for (const fix of
|
|
353
|
+
for (const fix of sslFixes) {
|
|
305
354
|
console.log(`\n Trying: ${fix.name}...`);
|
|
306
355
|
try {
|
|
307
|
-
|
|
308
|
-
stdio: 'pipe',
|
|
309
|
-
shell: true,
|
|
310
|
-
env: { ...process.env, PYTHONWARNINGS: 'ignore:Unverified HTTPS request' }
|
|
311
|
-
});
|
|
356
|
+
fix.install();
|
|
312
357
|
console.log(' โ
Success! Dependencies installed using workaround');
|
|
313
358
|
return true;
|
|
314
359
|
} catch (e) {
|
|
@@ -327,17 +372,16 @@ async function setupPythonEnvironment() {
|
|
|
327
372
|
// Install script dependencies
|
|
328
373
|
console.log('Installing import script dependencies...');
|
|
329
374
|
try {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
});
|
|
375
|
+
safeExec(venvPip, [
|
|
376
|
+
'install', '-r', join(scriptsPath, 'requirements.txt')
|
|
377
|
+
], { cwd: mcpPath, stdio: 'inherit' });
|
|
334
378
|
} catch (error) {
|
|
335
379
|
// Try with trusted host if SSL error
|
|
336
380
|
try {
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
});
|
|
381
|
+
safeExec(venvPip, [
|
|
382
|
+
'install', '--trusted-host', 'pypi.org', '--trusted-host', 'files.pythonhosted.org',
|
|
383
|
+
'-r', join(scriptsPath, 'requirements.txt')
|
|
384
|
+
], { cwd: mcpPath, stdio: 'inherit' });
|
|
341
385
|
} catch {
|
|
342
386
|
console.log('โ ๏ธ Could not install script dependencies automatically');
|
|
343
387
|
console.log(' You may need to install them manually later');
|
|
@@ -446,6 +490,9 @@ async function configureEnvironment() {
|
|
|
446
490
|
if (!envContent.includes('DECAY_SCALE_DAYS=')) {
|
|
447
491
|
envContent += 'DECAY_SCALE_DAYS=90\n';
|
|
448
492
|
}
|
|
493
|
+
if (!envContent.includes('PREFER_LOCAL_EMBEDDINGS=')) {
|
|
494
|
+
envContent += `PREFER_LOCAL_EMBEDDINGS=${localMode ? 'true' : 'false'}\n`;
|
|
495
|
+
}
|
|
449
496
|
|
|
450
497
|
await fs.writeFile(envPath, envContent.trim() + '\n');
|
|
451
498
|
console.log('โ
Environment file created/updated');
|
|
@@ -460,7 +507,7 @@ async function setupClaude() {
|
|
|
460
507
|
|
|
461
508
|
// Check if Claude CLI is available
|
|
462
509
|
try {
|
|
463
|
-
|
|
510
|
+
safeExec('which', ['claude'], { stdio: 'ignore' });
|
|
464
511
|
|
|
465
512
|
// Try to add the MCP automatically
|
|
466
513
|
try {
|
|
@@ -479,7 +526,16 @@ async function setupClaude() {
|
|
|
479
526
|
? `claude mcp add claude-self-reflect "${runScript}" -e QDRANT_URL="http://localhost:6333"`
|
|
480
527
|
: `claude mcp add claude-self-reflect "${runScript}" -e VOYAGE_KEY="${voyageKeyValue}" -e QDRANT_URL="http://localhost:6333"`;
|
|
481
528
|
|
|
482
|
-
|
|
529
|
+
// Parse the MCP command properly
|
|
530
|
+
const mcpArgs = ['mcp', 'add', 'claude-self-reflect', runScript];
|
|
531
|
+
if (voyageKeyValue) {
|
|
532
|
+
mcpArgs.push('-e', `VOYAGE_KEY=${voyageKeyValue}`);
|
|
533
|
+
}
|
|
534
|
+
mcpArgs.push('-e', 'QDRANT_URL=http://localhost:6333');
|
|
535
|
+
if (localMode) {
|
|
536
|
+
mcpArgs.push('-e', 'PREFER_LOCAL_EMBEDDINGS=true');
|
|
537
|
+
}
|
|
538
|
+
safeExec('claude', mcpArgs, { stdio: 'inherit' });
|
|
483
539
|
console.log('โ
MCP added successfully!');
|
|
484
540
|
console.log('\nโ ๏ธ You may need to restart Claude Code for the changes to take effect.');
|
|
485
541
|
|
|
@@ -540,7 +596,24 @@ async function showPreSetupInstructions() {
|
|
|
540
596
|
console.log('๐ Before we begin, you\'ll need:');
|
|
541
597
|
console.log(' 1. Docker Desktop installed and running');
|
|
542
598
|
console.log(' 2. Python 3.10 or higher');
|
|
543
|
-
|
|
599
|
+
|
|
600
|
+
console.log('\nโ ๏ธ IMPORTANT: Embedding Mode Choice');
|
|
601
|
+
console.log('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ');
|
|
602
|
+
console.log('You must choose between Local or Cloud embeddings:');
|
|
603
|
+
console.log('\n๐ Local Mode (Default):');
|
|
604
|
+
console.log(' โข Privacy: All processing on your machine');
|
|
605
|
+
console.log(' โข No API costs or internet required');
|
|
606
|
+
console.log(' โข Good accuracy for most use cases');
|
|
607
|
+
console.log('\nโ๏ธ Cloud Mode (Voyage AI):');
|
|
608
|
+
console.log(' โข Better search accuracy');
|
|
609
|
+
console.log(' โข Requires API key and internet');
|
|
610
|
+
console.log(' โข Conversations sent to Voyage for processing');
|
|
611
|
+
console.log('\nโ ๏ธ This choice is SEMI-PERMANENT. Switching later requires:');
|
|
612
|
+
console.log(' โข Re-importing all conversations (30+ minutes)');
|
|
613
|
+
console.log(' โข Separate storage for each mode');
|
|
614
|
+
console.log(' โข Cannot search across modes\n');
|
|
615
|
+
console.log('For Cloud mode, run with: --voyage-key=<your-key>');
|
|
616
|
+
console.log('โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n');
|
|
544
617
|
|
|
545
618
|
if (isInteractive) {
|
|
546
619
|
await question('Press Enter to continue...');
|
|
@@ -549,12 +622,7 @@ async function showPreSetupInstructions() {
|
|
|
549
622
|
|
|
550
623
|
async function importConversations() {
|
|
551
624
|
console.log('\n๐ Import Claude Conversations...');
|
|
552
|
-
|
|
553
|
-
// Skip import in local mode
|
|
554
|
-
if (localMode) {
|
|
555
|
-
console.log('๐ Skipping import in local mode (no API key for embeddings)');
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
625
|
+
console.log(localMode ? '๐ Using local embeddings for import' : '๐ Using Voyage AI embeddings');
|
|
558
626
|
|
|
559
627
|
// Check if Claude logs directory exists
|
|
560
628
|
const logsDir = join(process.env.HOME || process.env.USERPROFILE, '.claude', 'projects');
|
|
@@ -694,7 +762,7 @@ async function showSystemDashboard() {
|
|
|
694
762
|
|
|
695
763
|
// Docker status
|
|
696
764
|
try {
|
|
697
|
-
|
|
765
|
+
safeExec('docker', ['info'], { stdio: 'ignore' });
|
|
698
766
|
status.docker = true;
|
|
699
767
|
} catch {}
|
|
700
768
|
|
|
@@ -710,7 +778,7 @@ async function showSystemDashboard() {
|
|
|
710
778
|
// Python status
|
|
711
779
|
try {
|
|
712
780
|
const pythonCmd = process.env.PYTHON_PATH || 'python3';
|
|
713
|
-
|
|
781
|
+
safeExec(pythonCmd, ['--version'], { stdio: 'ignore' });
|
|
714
782
|
status.python = true;
|
|
715
783
|
} catch {}
|
|
716
784
|
|
|
@@ -773,9 +841,9 @@ async function showSystemDashboard() {
|
|
|
773
841
|
status.watcherErrors = [];
|
|
774
842
|
status.lastImportTime = null;
|
|
775
843
|
try {
|
|
776
|
-
const watcherLogs =
|
|
777
|
-
|
|
778
|
-
})
|
|
844
|
+
const watcherLogs = safeExec('docker', [
|
|
845
|
+
'logs', 'claude-reflection-watcher', '--tail', '50'
|
|
846
|
+
], { encoding: 'utf-8' });
|
|
779
847
|
|
|
780
848
|
// Check for recent errors
|
|
781
849
|
const errorMatches = watcherLogs.match(/ERROR.*Import failed.*/g);
|
|
@@ -800,10 +868,9 @@ async function showSystemDashboard() {
|
|
|
800
868
|
|
|
801
869
|
// Check if watcher is running via Docker
|
|
802
870
|
try {
|
|
803
|
-
const dockerStatus =
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
}).toString().trim();
|
|
871
|
+
const dockerStatus = safeExec('docker', [
|
|
872
|
+
'ps', '--filter', 'name=claude-reflection-watcher', '--format', '{{.Names}}'
|
|
873
|
+
], { cwd: projectRoot, encoding: 'utf-8' }).trim();
|
|
807
874
|
|
|
808
875
|
if (dockerStatus.includes('watcher')) {
|
|
809
876
|
status.watcherRunning = true;
|
|
@@ -901,11 +968,8 @@ async function setupWatcher() {
|
|
|
901
968
|
await fs.access(watcherScript);
|
|
902
969
|
console.log('โ
Watcher script found');
|
|
903
970
|
|
|
904
|
-
//
|
|
905
|
-
|
|
906
|
-
console.log('๐ Skipping watcher in local mode');
|
|
907
|
-
return;
|
|
908
|
-
}
|
|
971
|
+
// Watcher works with both local and cloud embeddings
|
|
972
|
+
console.log(localMode ? '๐ Watcher will use local embeddings' : '๐ Watcher will use Voyage AI embeddings');
|
|
909
973
|
|
|
910
974
|
// Ask if user wants to enable watcher
|
|
911
975
|
let enableWatcher = 'y';
|
|
@@ -933,20 +997,26 @@ async function setupWatcher() {
|
|
|
933
997
|
console.log('๐งน Cleaning up existing containers...');
|
|
934
998
|
try {
|
|
935
999
|
// Stop all claude-reflection containers
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1000
|
+
try {
|
|
1001
|
+
safeExec('docker', ['compose', 'down'], {
|
|
1002
|
+
cwd: projectRoot,
|
|
1003
|
+
stdio: 'pipe'
|
|
1004
|
+
});
|
|
1005
|
+
} catch {}
|
|
940
1006
|
|
|
941
1007
|
// Also stop any standalone containers
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1008
|
+
try {
|
|
1009
|
+
safeExec('docker', ['stop', 'claude-reflection-watcher', 'claude-reflection-qdrant', 'qdrant'], {
|
|
1010
|
+
stdio: 'pipe'
|
|
1011
|
+
});
|
|
1012
|
+
} catch {}
|
|
945
1013
|
|
|
946
1014
|
// Remove them
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1015
|
+
try {
|
|
1016
|
+
safeExec('docker', ['rm', 'claude-reflection-watcher', 'claude-reflection-qdrant', 'qdrant'], {
|
|
1017
|
+
stdio: 'pipe'
|
|
1018
|
+
});
|
|
1019
|
+
} catch {}
|
|
950
1020
|
|
|
951
1021
|
// Wait a moment for cleanup
|
|
952
1022
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
@@ -954,7 +1024,7 @@ async function setupWatcher() {
|
|
|
954
1024
|
|
|
955
1025
|
// Start both services with compose
|
|
956
1026
|
console.log('๐ Starting Qdrant and Watcher services...');
|
|
957
|
-
|
|
1027
|
+
safeExec('docker', ['compose', '--profile', 'watch', 'up', '-d'], {
|
|
958
1028
|
cwd: projectRoot,
|
|
959
1029
|
stdio: 'pipe' // Use pipe to capture output
|
|
960
1030
|
});
|
|
@@ -964,10 +1034,9 @@ async function setupWatcher() {
|
|
|
964
1034
|
|
|
965
1035
|
// Check container status
|
|
966
1036
|
try {
|
|
967
|
-
const psOutput =
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
});
|
|
1037
|
+
const psOutput = safeExec('docker', [
|
|
1038
|
+
'ps', '--filter', 'name=claude-reflection', '--format', 'table {{.Names}}\t{{.Status}}'
|
|
1039
|
+
], { cwd: projectRoot, encoding: 'utf8' });
|
|
971
1040
|
|
|
972
1041
|
const qdrantReady = psOutput.includes('claude-reflection-qdrant') && psOutput.includes('Up');
|
|
973
1042
|
const watcherReady = psOutput.includes('claude-reflection-watcher') && psOutput.includes('Up');
|
|
@@ -1033,7 +1102,7 @@ async function verifyMCP() {
|
|
|
1033
1102
|
|
|
1034
1103
|
try {
|
|
1035
1104
|
// Check if MCP is listed
|
|
1036
|
-
const mcpList =
|
|
1105
|
+
const mcpList = safeExec('claude', ['mcp', 'list'], { encoding: 'utf8' });
|
|
1037
1106
|
if (!mcpList.includes('claude-self-reflect')) {
|
|
1038
1107
|
console.log('โ MCP not found in Claude Code');
|
|
1039
1108
|
return;
|
|
@@ -1103,10 +1172,15 @@ asyncio.run(test_mcp())
|
|
|
1103
1172
|
// Run the test
|
|
1104
1173
|
console.log('\n๐งช Testing MCP functionality...');
|
|
1105
1174
|
try {
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1175
|
+
// Create test script that activates venv and runs test
|
|
1176
|
+
const testScriptPath = join(projectRoot, 'run-test.sh');
|
|
1177
|
+
const testScript = `#!/bin/bash\nsource mcp-server/venv/bin/activate\npython test-mcp.py`;
|
|
1178
|
+
await fs.writeFile(testScriptPath, testScript, { mode: 0o755 });
|
|
1179
|
+
const testResult = safeExec('bash', [testScriptPath], {
|
|
1180
|
+
cwd: projectRoot,
|
|
1181
|
+
encoding: 'utf8'
|
|
1109
1182
|
});
|
|
1183
|
+
await fs.unlink(testScriptPath);
|
|
1110
1184
|
console.log(testResult);
|
|
1111
1185
|
|
|
1112
1186
|
// Clean up test script
|
|
@@ -1151,18 +1225,23 @@ async function main() {
|
|
|
1151
1225
|
}
|
|
1152
1226
|
}
|
|
1153
1227
|
|
|
1154
|
-
//
|
|
1155
|
-
if (!isInteractive
|
|
1156
|
-
console.log('
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1228
|
+
// In non-interactive mode, just use defaults (local mode unless key provided)
|
|
1229
|
+
if (!isInteractive) {
|
|
1230
|
+
console.log(voyageKey ? '๐ Using Voyage AI embeddings' : '๐ Using local embeddings for privacy');
|
|
1231
|
+
} else if (!voyageKey) {
|
|
1232
|
+
// In interactive mode without a key, confirm local mode choice
|
|
1233
|
+
await showPreSetupInstructions();
|
|
1234
|
+
const confirmLocal = await question('Continue with Local embeddings (privacy mode)? (y/n): ');
|
|
1235
|
+
if (confirmLocal.toLowerCase() !== 'y') {
|
|
1236
|
+
console.log('\nTo use Cloud mode, restart with: claude-self-reflect setup --voyage-key=<your-key>');
|
|
1237
|
+
console.log('Get your free API key at: https://www.voyageai.com/');
|
|
1238
|
+
if (rl) rl.close();
|
|
1239
|
+
process.exit(0);
|
|
1240
|
+
}
|
|
1241
|
+
} else {
|
|
1242
|
+
await showPreSetupInstructions();
|
|
1162
1243
|
}
|
|
1163
1244
|
|
|
1164
|
-
await showPreSetupInstructions();
|
|
1165
|
-
|
|
1166
1245
|
// Check prerequisites
|
|
1167
1246
|
const pythonOk = await checkPython();
|
|
1168
1247
|
if (!pythonOk) {
|
|
@@ -9,11 +9,12 @@ authors = [
|
|
|
9
9
|
]
|
|
10
10
|
dependencies = [
|
|
11
11
|
"fastmcp>=0.0.7",
|
|
12
|
-
"qdrant-client>=1.7.0",
|
|
13
|
-
"voyageai>=0.1.0",
|
|
14
|
-
"python-dotenv>=1.0.0",
|
|
15
|
-
"pydantic>=2.0.0",
|
|
16
|
-
"pydantic-settings>=2.0.0",
|
|
12
|
+
"qdrant-client>=1.7.0,<2.0.0",
|
|
13
|
+
"voyageai>=0.1.0,<1.0.0",
|
|
14
|
+
"python-dotenv>=1.0.0,<2.0.0",
|
|
15
|
+
"pydantic>=2.9.2,<3.0.0", # Pin to avoid CVE-2024-3772
|
|
16
|
+
"pydantic-settings>=2.0.0,<3.0.0",
|
|
17
|
+
"fastembed>=0.4.0,<1.0.0",
|
|
17
18
|
]
|
|
18
19
|
|
|
19
20
|
[project.scripts]
|