claude-self-reflect 2.4.0 ā 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/__main__.py +2 -1
- package/mcp-server/src/server.py +66 -7
- package/mcp-server/src/server_v2.py +11 -7
- package/package.json +5 -1
- package/scripts/import-conversations-unified.py +16 -1
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execSync, spawn, spawnSync } from 'child_process';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
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
|
+
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = dirname(__filename);
|
|
13
|
+
const projectRoot = join(__dirname, '..');
|
|
14
|
+
|
|
15
|
+
// Parse command line arguments
|
|
16
|
+
const args = process.argv.slice(2);
|
|
17
|
+
let voyageKey = null;
|
|
18
|
+
let debugMode = false;
|
|
19
|
+
|
|
20
|
+
for (const arg of args) {
|
|
21
|
+
if (arg.startsWith('--voyage-key=')) {
|
|
22
|
+
voyageKey = arg.split('=')[1];
|
|
23
|
+
} else if (arg === '--debug') {
|
|
24
|
+
debugMode = true;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Default to local mode unless Voyage key is provided
|
|
29
|
+
let localMode = !voyageKey;
|
|
30
|
+
|
|
31
|
+
// Helper to safely execute commands
|
|
32
|
+
function safeExec(command, args = [], options = {}) {
|
|
33
|
+
const result = spawnSync(command, args, {
|
|
34
|
+
...options,
|
|
35
|
+
shell: false
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
if (result.error) {
|
|
39
|
+
throw result.error;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (result.status !== 0) {
|
|
43
|
+
const error = new Error(`Command failed: ${command} ${args.join(' ')}`);
|
|
44
|
+
error.stdout = result.stdout;
|
|
45
|
+
error.stderr = result.stderr;
|
|
46
|
+
error.status = result.status;
|
|
47
|
+
throw error;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return result.stdout?.toString() || '';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const isInteractive = process.stdin.isTTY && process.stdout.isTTY;
|
|
54
|
+
|
|
55
|
+
const rl = isInteractive ? readline.createInterface({
|
|
56
|
+
input: process.stdin,
|
|
57
|
+
output: process.stdout
|
|
58
|
+
}) : null;
|
|
59
|
+
|
|
60
|
+
const question = (query) => {
|
|
61
|
+
if (!isInteractive) {
|
|
62
|
+
console.log(`Non-interactive mode detected. ${query} [Defaulting to 'n']`);
|
|
63
|
+
return Promise.resolve('n');
|
|
64
|
+
}
|
|
65
|
+
return new Promise((resolve) => rl.question(query, resolve));
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
async function checkDocker() {
|
|
69
|
+
console.log('\nš³ Checking Docker...');
|
|
70
|
+
try {
|
|
71
|
+
safeExec('docker', ['info'], { stdio: 'ignore' });
|
|
72
|
+
console.log('ā
Docker is installed and running');
|
|
73
|
+
|
|
74
|
+
// Check docker compose
|
|
75
|
+
try {
|
|
76
|
+
safeExec('docker', ['compose', 'version'], { stdio: 'ignore' });
|
|
77
|
+
console.log('ā
Docker Compose v2 is available');
|
|
78
|
+
return true;
|
|
79
|
+
} catch {
|
|
80
|
+
console.log('ā Docker Compose v2 not found');
|
|
81
|
+
console.log(' Please update Docker Desktop to the latest version');
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
console.log('ā Docker is not running or not installed');
|
|
86
|
+
console.log('\nš Please install Docker:');
|
|
87
|
+
console.log(' ⢠macOS/Windows: https://docker.com/products/docker-desktop');
|
|
88
|
+
console.log(' ⢠Linux: https://docs.docker.com/engine/install/');
|
|
89
|
+
console.log('\n After installation, make sure Docker is running and try again.');
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function configureEnvironment() {
|
|
95
|
+
console.log('\nš Configuring environment...');
|
|
96
|
+
|
|
97
|
+
const envPath = join(projectRoot, '.env');
|
|
98
|
+
let envContent = '';
|
|
99
|
+
let hasValidApiKey = false;
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
envContent = await fs.readFile(envPath, 'utf-8');
|
|
103
|
+
} catch {
|
|
104
|
+
// .env doesn't exist, create it
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Check if we have a command line API key
|
|
108
|
+
if (voyageKey) {
|
|
109
|
+
if (voyageKey.startsWith('pa-')) {
|
|
110
|
+
console.log('ā
Using API key from command line');
|
|
111
|
+
envContent = envContent.replace(/VOYAGE_KEY=.*/g, '');
|
|
112
|
+
envContent += `\nVOYAGE_KEY=${voyageKey}\n`;
|
|
113
|
+
hasValidApiKey = true;
|
|
114
|
+
} else {
|
|
115
|
+
console.log('ā Invalid API key format. Voyage keys start with "pa-"');
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
} else if (localMode) {
|
|
119
|
+
console.log('š Running in local mode - no API key required');
|
|
120
|
+
hasValidApiKey = false;
|
|
121
|
+
} else {
|
|
122
|
+
// Check if we already have a valid API key
|
|
123
|
+
const existingKeyMatch = envContent.match(/VOYAGE_KEY=([^\s]+)/);
|
|
124
|
+
if (existingKeyMatch && existingKeyMatch[1] && !existingKeyMatch[1].includes('your-')) {
|
|
125
|
+
console.log('ā
Found existing Voyage API key in .env file');
|
|
126
|
+
hasValidApiKey = true;
|
|
127
|
+
} else if (isInteractive) {
|
|
128
|
+
console.log('\nš Voyage AI API Key Setup (Optional)');
|
|
129
|
+
console.log('āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā');
|
|
130
|
+
console.log('For better search accuracy, you can use Voyage AI embeddings.');
|
|
131
|
+
console.log('Skip this to use local embeddings (recommended for privacy).\n');
|
|
132
|
+
|
|
133
|
+
const inputKey = await question('Paste your Voyage AI key (or press Enter to skip): ');
|
|
134
|
+
|
|
135
|
+
if (inputKey && inputKey.trim() && inputKey.trim().startsWith('pa-')) {
|
|
136
|
+
envContent = envContent.replace(/VOYAGE_KEY=.*/g, '');
|
|
137
|
+
envContent += `\nVOYAGE_KEY=${inputKey.trim()}\n`;
|
|
138
|
+
hasValidApiKey = true;
|
|
139
|
+
console.log('ā
API key saved');
|
|
140
|
+
} else if (inputKey && inputKey.trim()) {
|
|
141
|
+
console.log('ā ļø Invalid key format. Skipping...');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Set default values
|
|
147
|
+
if (!envContent.includes('QDRANT_URL=')) {
|
|
148
|
+
envContent += 'QDRANT_URL=http://localhost:6333\n';
|
|
149
|
+
}
|
|
150
|
+
if (!envContent.includes('ENABLE_MEMORY_DECAY=')) {
|
|
151
|
+
envContent += 'ENABLE_MEMORY_DECAY=false\n';
|
|
152
|
+
}
|
|
153
|
+
if (!envContent.includes('PREFER_LOCAL_EMBEDDINGS=')) {
|
|
154
|
+
envContent += `PREFER_LOCAL_EMBEDDINGS=${localMode ? 'true' : 'false'}\n`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
await fs.writeFile(envPath, envContent.trim() + '\n');
|
|
158
|
+
console.log('ā
Environment configured');
|
|
159
|
+
|
|
160
|
+
return { hasValidApiKey };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function startDockerServices() {
|
|
164
|
+
console.log('\nš Starting Docker services...');
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
// First, ensure any old containers are stopped
|
|
168
|
+
console.log('š§¹ Cleaning up old containers...');
|
|
169
|
+
try {
|
|
170
|
+
safeExec('docker', ['compose', 'down'], {
|
|
171
|
+
cwd: projectRoot,
|
|
172
|
+
stdio: 'pipe'
|
|
173
|
+
});
|
|
174
|
+
} catch {
|
|
175
|
+
// Ignore errors if no containers exist
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Check for existing bind mount data that needs migration
|
|
179
|
+
const bindMountPath = join(projectRoot, 'data', 'qdrant');
|
|
180
|
+
try {
|
|
181
|
+
await fs.access(bindMountPath);
|
|
182
|
+
const files = await fs.readdir(bindMountPath);
|
|
183
|
+
if (files.length > 0) {
|
|
184
|
+
console.log('\nā ļø Found existing Qdrant data in ./data/qdrant');
|
|
185
|
+
console.log('š¦ This will be automatically migrated to Docker volume on first start.');
|
|
186
|
+
|
|
187
|
+
// Create a migration marker
|
|
188
|
+
await fs.writeFile(join(projectRoot, '.needs-migration'), 'true');
|
|
189
|
+
}
|
|
190
|
+
} catch {
|
|
191
|
+
// No existing data, nothing to migrate
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Start Qdrant and MCP server
|
|
195
|
+
console.log('š¦ Starting Qdrant database and MCP server...');
|
|
196
|
+
safeExec('docker', ['compose', '--profile', 'mcp', 'up', '-d'], {
|
|
197
|
+
cwd: projectRoot,
|
|
198
|
+
stdio: 'inherit'
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Wait for services to be ready
|
|
202
|
+
console.log('ā³ Waiting for services to start...');
|
|
203
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
204
|
+
|
|
205
|
+
// Check if we need to migrate data
|
|
206
|
+
try {
|
|
207
|
+
await fs.access(join(projectRoot, '.needs-migration'));
|
|
208
|
+
console.log('\nš Migrating data from bind mount to Docker volume...');
|
|
209
|
+
|
|
210
|
+
// Stop Qdrant to perform migration
|
|
211
|
+
safeExec('docker', ['compose', 'stop', 'qdrant'], {
|
|
212
|
+
cwd: projectRoot,
|
|
213
|
+
stdio: 'pipe'
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Copy data from bind mount to Docker volume
|
|
217
|
+
safeExec('docker', ['run', '--rm',
|
|
218
|
+
'-v', `${projectRoot}/data/qdrant:/source:ro`,
|
|
219
|
+
'-v', 'claude-self-reflect_qdrant_data:/target',
|
|
220
|
+
'alpine', 'sh', '-c', 'cp -R /source/* /target/'
|
|
221
|
+
], {
|
|
222
|
+
cwd: projectRoot,
|
|
223
|
+
stdio: 'inherit'
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
console.log('ā
Data migration completed!');
|
|
227
|
+
|
|
228
|
+
// Remove migration marker
|
|
229
|
+
await fs.unlink(join(projectRoot, '.needs-migration'));
|
|
230
|
+
|
|
231
|
+
// Restart Qdrant
|
|
232
|
+
safeExec('docker', ['compose', '--profile', 'mcp', 'up', '-d', 'qdrant'], {
|
|
233
|
+
cwd: projectRoot,
|
|
234
|
+
stdio: 'pipe'
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
238
|
+
} catch {
|
|
239
|
+
// No migration needed
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Check if services are running
|
|
243
|
+
const psOutput = safeExec('docker', ['compose', 'ps', '--format', 'table'], {
|
|
244
|
+
cwd: projectRoot,
|
|
245
|
+
encoding: 'utf8'
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
console.log('\nš Service Status:');
|
|
249
|
+
console.log(psOutput);
|
|
250
|
+
|
|
251
|
+
return true;
|
|
252
|
+
} catch (error) {
|
|
253
|
+
console.log('ā Failed to start Docker services:', error.message);
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function configureClaude() {
|
|
259
|
+
console.log('\nš¤ Configuring Claude Desktop...');
|
|
260
|
+
|
|
261
|
+
const mcpScript = join(projectRoot, 'mcp-server', 'run-mcp-docker.sh');
|
|
262
|
+
|
|
263
|
+
// Create a script that runs the MCP server in Docker
|
|
264
|
+
const scriptContent = `#!/bin/bash
|
|
265
|
+
docker exec -i claude-reflection-mcp python -m src.server_v2
|
|
266
|
+
`;
|
|
267
|
+
|
|
268
|
+
await fs.writeFile(mcpScript, scriptContent, { mode: 0o755 });
|
|
269
|
+
|
|
270
|
+
// Check if Claude CLI is available
|
|
271
|
+
try {
|
|
272
|
+
safeExec('which', ['claude'], { stdio: 'ignore' });
|
|
273
|
+
|
|
274
|
+
console.log('š§ Adding MCP to Claude Desktop...');
|
|
275
|
+
try {
|
|
276
|
+
const mcpArgs = ['mcp', 'add', 'claude-self-reflect', mcpScript];
|
|
277
|
+
safeExec('claude', mcpArgs, { stdio: 'inherit' });
|
|
278
|
+
console.log('ā
MCP added successfully!');
|
|
279
|
+
console.log('\nā ļø Please restart Claude Desktop for changes to take effect.');
|
|
280
|
+
} catch {
|
|
281
|
+
console.log('ā ļø Could not add MCP automatically');
|
|
282
|
+
showManualConfig(mcpScript);
|
|
283
|
+
}
|
|
284
|
+
} catch {
|
|
285
|
+
console.log('ā ļø Claude CLI not found');
|
|
286
|
+
showManualConfig(mcpScript);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function showManualConfig(mcpScript) {
|
|
291
|
+
console.log('\nAdd this to your Claude Desktop config manually:');
|
|
292
|
+
console.log('```json');
|
|
293
|
+
console.log(JSON.stringify({
|
|
294
|
+
"claude-self-reflect": {
|
|
295
|
+
"command": mcpScript
|
|
296
|
+
}
|
|
297
|
+
}, null, 2));
|
|
298
|
+
console.log('```');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async function importConversations() {
|
|
302
|
+
console.log('\nš Importing conversations...');
|
|
303
|
+
|
|
304
|
+
const answer = await question('Would you like to import your existing Claude conversations? (y/n): ');
|
|
305
|
+
|
|
306
|
+
if (answer.toLowerCase() === 'y') {
|
|
307
|
+
console.log('š Starting import process...');
|
|
308
|
+
console.log(' This may take a few minutes depending on your conversation history');
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
safeExec('docker', ['compose', 'run', '--rm', 'importer'], {
|
|
312
|
+
cwd: projectRoot,
|
|
313
|
+
stdio: 'inherit'
|
|
314
|
+
});
|
|
315
|
+
console.log('\nā
Import completed!');
|
|
316
|
+
} catch {
|
|
317
|
+
console.log('\nā ļø Import had some issues, but you can continue');
|
|
318
|
+
}
|
|
319
|
+
} else {
|
|
320
|
+
console.log('š Skipping import. You can import later with:');
|
|
321
|
+
console.log(' docker compose run --rm importer');
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async function showFinalInstructions() {
|
|
326
|
+
console.log('\nā
Setup complete!');
|
|
327
|
+
|
|
328
|
+
console.log('\nšÆ Your Claude Self-Reflect System:');
|
|
329
|
+
console.log(' ⢠š Qdrant Dashboard: http://localhost:6333/dashboard/');
|
|
330
|
+
console.log(' ⢠š Status: All services running');
|
|
331
|
+
console.log(' ⢠š Search: Semantic search with memory decay enabled');
|
|
332
|
+
console.log(' ⢠š Import: Watcher checking every 60 seconds');
|
|
333
|
+
|
|
334
|
+
console.log('\nš Quick Reference Commands:');
|
|
335
|
+
console.log(' ⢠Check status: docker compose ps');
|
|
336
|
+
console.log(' ⢠View logs: docker compose logs -f');
|
|
337
|
+
console.log(' ⢠Import conversations: docker compose run --rm importer');
|
|
338
|
+
console.log(' ⢠Start watcher: docker compose --profile watch up -d');
|
|
339
|
+
console.log(' ⢠Stop all: docker compose down');
|
|
340
|
+
|
|
341
|
+
console.log('\nšÆ Next Steps:');
|
|
342
|
+
console.log('1. Restart Claude Desktop');
|
|
343
|
+
console.log('2. Look for "claude-self-reflect" in the MCP tools');
|
|
344
|
+
console.log('3. Try: "Search my past conversations about Python"');
|
|
345
|
+
|
|
346
|
+
console.log('\nš Documentation: https://github.com/ramakay/claude-self-reflect');
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
async function checkExistingInstallation() {
|
|
350
|
+
try {
|
|
351
|
+
// Check if services are already running
|
|
352
|
+
const psResult = safeExec('docker', ['compose', '-f', 'docker-compose.yaml', 'ps', '--format', 'json'], {
|
|
353
|
+
cwd: projectRoot,
|
|
354
|
+
encoding: 'utf8'
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
if (psResult && psResult.includes('claude-reflection-')) {
|
|
358
|
+
const services = psResult.split('\n').filter(line => line.trim());
|
|
359
|
+
const runningServices = services.filter(line => line.includes('"State":"running"')).length;
|
|
360
|
+
|
|
361
|
+
if (runningServices >= 2) { // At least Qdrant and MCP should be running
|
|
362
|
+
console.log('ā
Claude Self-Reflect is already installed and running!\n');
|
|
363
|
+
console.log('šÆ Your System Status:');
|
|
364
|
+
console.log(' ⢠š Qdrant Dashboard: http://localhost:6333/dashboard/');
|
|
365
|
+
console.log(' ⢠š Services: ' + runningServices + ' containers running');
|
|
366
|
+
console.log(' ⢠š Mode: ' + (localMode ? 'Local embeddings (privacy mode)' : 'Cloud embeddings (Voyage AI)'));
|
|
367
|
+
console.log(' ⢠┠Memory decay: Enabled (90-day half-life)');
|
|
368
|
+
|
|
369
|
+
console.log('\nš Quick Commands:');
|
|
370
|
+
console.log(' ⢠View status: docker compose ps');
|
|
371
|
+
console.log(' ⢠View logs: docker compose logs -f');
|
|
372
|
+
console.log(' ⢠Restart: docker compose restart');
|
|
373
|
+
console.log(' ⢠Stop: docker compose down');
|
|
374
|
+
|
|
375
|
+
console.log('\nš” To re-run setup, first stop services with: docker compose down');
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
} catch (err) {
|
|
380
|
+
// Services not running, continue with setup
|
|
381
|
+
}
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async function main() {
|
|
386
|
+
console.log('š Claude Self-Reflect Setup (Docker Edition)\n');
|
|
387
|
+
|
|
388
|
+
// Check if already installed
|
|
389
|
+
const alreadyInstalled = await checkExistingInstallation();
|
|
390
|
+
if (alreadyInstalled) {
|
|
391
|
+
if (rl) rl.close();
|
|
392
|
+
process.exit(0);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
console.log('This simplified setup runs everything in Docker.');
|
|
396
|
+
console.log('No Python installation required!\n');
|
|
397
|
+
|
|
398
|
+
// Check Docker
|
|
399
|
+
const dockerOk = await checkDocker();
|
|
400
|
+
if (!dockerOk) {
|
|
401
|
+
if (rl) rl.close();
|
|
402
|
+
process.exit(1);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Configure environment
|
|
406
|
+
await configureEnvironment();
|
|
407
|
+
|
|
408
|
+
// Start services
|
|
409
|
+
const servicesOk = await startDockerServices();
|
|
410
|
+
if (!servicesOk) {
|
|
411
|
+
console.log('\nā Failed to start services');
|
|
412
|
+
console.log(' Check the Docker logs for details');
|
|
413
|
+
if (rl) rl.close();
|
|
414
|
+
process.exit(1);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Configure Claude
|
|
418
|
+
await configureClaude();
|
|
419
|
+
|
|
420
|
+
// Import conversations
|
|
421
|
+
await importConversations();
|
|
422
|
+
|
|
423
|
+
// Show final instructions
|
|
424
|
+
await showFinalInstructions();
|
|
425
|
+
|
|
426
|
+
if (rl) rl.close();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
main().catch(error => {
|
|
430
|
+
console.error('ā Setup failed:', error);
|
|
431
|
+
if (rl) rl.close();
|
|
432
|
+
process.exit(1);
|
|
433
|
+
});
|