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