claude-self-reflect 6.0.2 → 6.0.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.
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
import path from 'path';
|
|
9
|
-
import { execSync } from 'child_process';
|
|
9
|
+
import { execSync, spawnSync } from 'child_process';
|
|
10
10
|
import https from 'https';
|
|
11
11
|
import os from 'os';
|
|
12
12
|
|
|
@@ -65,20 +65,30 @@ class FastEmbedFallback {
|
|
|
65
65
|
|
|
66
66
|
try {
|
|
67
67
|
// Download with curl (handles proxies better than Node's https)
|
|
68
|
+
// SECURITY: Use array-based arguments to prevent shell injection
|
|
68
69
|
this.log(`Downloading ${this.modelFile} (79MB)...`, 'info');
|
|
69
|
-
|
|
70
|
+
const curlResult = spawnSync('curl', ['-L', '-o', tarPath, this.gcsUrl], {
|
|
70
71
|
stdio: 'inherit',
|
|
71
72
|
timeout: 300000 // 5 minute timeout
|
|
72
73
|
});
|
|
73
74
|
|
|
75
|
+
if (curlResult.error || curlResult.status !== 0) {
|
|
76
|
+
throw new Error(`curl failed: ${curlResult.error?.message || `exit code ${curlResult.status}`}`);
|
|
77
|
+
}
|
|
78
|
+
|
|
74
79
|
this.log('Download complete. Extracting...', 'success');
|
|
75
80
|
|
|
76
81
|
// Extract (with 2 minute timeout to prevent hanging)
|
|
77
|
-
|
|
82
|
+
// SECURITY: Use array-based arguments to prevent shell injection
|
|
83
|
+
const tarResult = spawnSync('tar', ['-xzf', tarPath, '-C', this.cacheDir], {
|
|
78
84
|
stdio: 'inherit',
|
|
79
85
|
timeout: 120000 // 2 minute timeout
|
|
80
86
|
});
|
|
81
87
|
|
|
88
|
+
if (tarResult.error || tarResult.status !== 0) {
|
|
89
|
+
throw new Error(`tar extraction failed: ${tarResult.error?.message || `exit code ${tarResult.status}`}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
82
92
|
// Verify extraction
|
|
83
93
|
if (this.checkModelExists()) {
|
|
84
94
|
this.log('FastEmbed model installed successfully!', 'success');
|
package/installer/postinstall.js
CHANGED
|
@@ -118,7 +118,7 @@ class StatuslineSetup {
|
|
|
118
118
|
fs.unlinkSync(userLocalBin);
|
|
119
119
|
}
|
|
120
120
|
fs.symlinkSync(this.csrScript, userLocalBin);
|
|
121
|
-
// Note:
|
|
121
|
+
// Note: Symlink permissions don't matter - source script permissions are used
|
|
122
122
|
|
|
123
123
|
this.log('csr-status installed to ~/bin (no sudo required)', 'success');
|
|
124
124
|
this.log('Add ~/bin to PATH: export PATH="$HOME/bin:$PATH"', 'info');
|
|
@@ -42,9 +42,9 @@ class UpdateManager {
|
|
|
42
42
|
async checkCCStatusline() {
|
|
43
43
|
try {
|
|
44
44
|
execSync('npm list -g cc-statusline', { stdio: 'ignore' });
|
|
45
|
-
return { installed: true, name: 'cc-statusline', critical:
|
|
45
|
+
return { installed: true, name: 'cc-statusline', critical: true };
|
|
46
46
|
} catch {
|
|
47
|
-
return { installed: false, name: 'cc-statusline', critical:
|
|
47
|
+
return { installed: false, name: 'cc-statusline', critical: true, fix: () => this.installCCStatusline() };
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -77,18 +77,18 @@ class UpdateManager {
|
|
|
77
77
|
// Check for both 'ast-grep' (brew) and 'sg' (npm) binaries
|
|
78
78
|
try {
|
|
79
79
|
execSync('ast-grep --version', { stdio: 'ignore' });
|
|
80
|
-
return { installed: true, name: 'AST-Grep', critical:
|
|
80
|
+
return { installed: true, name: 'AST-Grep', critical: true };
|
|
81
81
|
} catch {
|
|
82
82
|
// Try 'sg' binary (npm install -g @ast-grep/cli)
|
|
83
83
|
try {
|
|
84
84
|
execSync('sg --version', { stdio: 'ignore' });
|
|
85
|
-
return { installed: true, name: 'AST-Grep (sg)', critical:
|
|
85
|
+
return { installed: true, name: 'AST-Grep (sg)', critical: true };
|
|
86
86
|
} catch {
|
|
87
87
|
return {
|
|
88
88
|
installed: false,
|
|
89
|
-
name: 'AST-Grep
|
|
90
|
-
critical:
|
|
91
|
-
fix: () => this.
|
|
89
|
+
name: 'AST-Grep',
|
|
90
|
+
critical: true,
|
|
91
|
+
fix: () => this.installASTGrep()
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -110,12 +110,45 @@ class UpdateManager {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
async checkQdrant() {
|
|
113
|
+
// Check if fetch is available (Node.js 18+)
|
|
114
|
+
if (typeof fetch === 'undefined') {
|
|
115
|
+
this.log('fetch not available, skipping Qdrant health check', 'warning');
|
|
116
|
+
return {
|
|
117
|
+
installed: false,
|
|
118
|
+
name: 'Qdrant',
|
|
119
|
+
critical: true,
|
|
120
|
+
fix: () => this.startQdrant(),
|
|
121
|
+
error: 'Cannot verify Qdrant (fetch not available)'
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const controller = new AbortController();
|
|
126
|
+
const timeout = setTimeout(() => controller.abort(), 3000); // 3 second timeout
|
|
127
|
+
|
|
113
128
|
try {
|
|
114
|
-
const response = await fetch('http://localhost:6333'
|
|
129
|
+
const response = await fetch('http://localhost:6333', {
|
|
130
|
+
signal: controller.signal
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Clear timeout immediately after successful fetch
|
|
134
|
+
clearTimeout(timeout);
|
|
135
|
+
|
|
115
136
|
if (response.ok) {
|
|
137
|
+
// Timeout already cleared above, safe to return
|
|
116
138
|
return { installed: true, name: 'Qdrant', critical: true };
|
|
139
|
+
} else {
|
|
140
|
+
// Timeout already cleared above, log and fall through
|
|
141
|
+
this.log(`Qdrant responded with status: ${response.status}`, 'warning');
|
|
142
|
+
}
|
|
143
|
+
} catch (error) {
|
|
144
|
+
clearTimeout(timeout);
|
|
145
|
+
|
|
146
|
+
if (error.name === 'AbortError') {
|
|
147
|
+
this.log('Qdrant health check timed out after 3 seconds', 'warning');
|
|
148
|
+
} else {
|
|
149
|
+
this.log(`Qdrant health check failed: ${error.message}`, 'warning');
|
|
117
150
|
}
|
|
118
|
-
}
|
|
151
|
+
}
|
|
119
152
|
|
|
120
153
|
return {
|
|
121
154
|
installed: false,
|
|
@@ -193,25 +226,71 @@ class UpdateManager {
|
|
|
193
226
|
return await fallback.run();
|
|
194
227
|
}
|
|
195
228
|
|
|
196
|
-
async
|
|
197
|
-
|
|
198
|
-
this.log('
|
|
199
|
-
|
|
200
|
-
|
|
229
|
+
async installASTGrep() {
|
|
230
|
+
// Try npm installation first (works on all platforms)
|
|
231
|
+
this.log('Installing AST-Grep via npm...', 'info');
|
|
232
|
+
try {
|
|
233
|
+
execSync('npm install -g @ast-grep/cli', { stdio: 'inherit' });
|
|
234
|
+
this.log('AST-Grep installed successfully', 'success');
|
|
235
|
+
return true;
|
|
236
|
+
} catch (npmError) {
|
|
237
|
+
// Check for permission errors
|
|
238
|
+
const isPermissionError = npmError.code === 'EACCES' ||
|
|
239
|
+
npmError.code === 'EPERM' ||
|
|
240
|
+
(npmError.stderr && npmError.stderr.toString().includes('EACCES')) ||
|
|
241
|
+
(npmError.message && npmError.message.includes('permission'));
|
|
242
|
+
|
|
243
|
+
if (isPermissionError) {
|
|
244
|
+
this.log('Failed to install AST-Grep via npm: Permission denied', 'error');
|
|
245
|
+
this.log('Alternative installation methods:', 'info');
|
|
246
|
+
this.log(' 1. With sudo: sudo npm install -g @ast-grep/cli', 'info');
|
|
247
|
+
this.log(' 2. With brew: brew install ast-grep (macOS/Linux)', 'info');
|
|
248
|
+
this.log(' 3. Use nvm for user-local npm installs', 'info');
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// If npm fails for other reasons, try suggesting brew on macOS
|
|
253
|
+
if (process.platform === 'darwin') {
|
|
254
|
+
this.log('npm installation failed. Checking for Homebrew...', 'warning');
|
|
255
|
+
try {
|
|
256
|
+
execSync('brew --version', { stdio: 'ignore' });
|
|
257
|
+
this.log('Install AST-Grep with: brew install ast-grep', 'info');
|
|
258
|
+
} catch {
|
|
259
|
+
this.log('Homebrew not found. Install from: https://brew.sh', 'info');
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
this.log(`AST-Grep installation failed: ${npmError.message}`, 'error');
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
201
266
|
}
|
|
202
267
|
|
|
203
268
|
async startQdrant() {
|
|
204
269
|
this.log('Starting Qdrant...', 'info');
|
|
270
|
+
|
|
271
|
+
// Try Docker Compose v2 first (docker compose)
|
|
205
272
|
try {
|
|
206
273
|
execSync('docker compose up -d qdrant', {
|
|
207
274
|
cwd: this.packageRoot,
|
|
208
275
|
stdio: 'inherit'
|
|
209
276
|
});
|
|
210
|
-
this.log('Qdrant started', 'success');
|
|
277
|
+
this.log('Qdrant started (Docker Compose v2)', 'success');
|
|
211
278
|
return true;
|
|
212
|
-
} catch (
|
|
213
|
-
this.log(
|
|
214
|
-
|
|
279
|
+
} catch (v2Error) {
|
|
280
|
+
this.log('Docker Compose v2 failed, trying v1...', 'warning');
|
|
281
|
+
|
|
282
|
+
// Fallback to Docker Compose v1 (docker-compose)
|
|
283
|
+
try {
|
|
284
|
+
execSync('docker-compose up -d qdrant', {
|
|
285
|
+
cwd: this.packageRoot,
|
|
286
|
+
stdio: 'inherit'
|
|
287
|
+
});
|
|
288
|
+
this.log('Qdrant started (Docker Compose v1)', 'success');
|
|
289
|
+
return true;
|
|
290
|
+
} catch (v1Error) {
|
|
291
|
+
this.log(`Failed to start Qdrant with both v1 and v2: ${v1Error.message}`, 'error');
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
215
294
|
}
|
|
216
295
|
}
|
|
217
296
|
|
|
@@ -225,18 +304,38 @@ class UpdateManager {
|
|
|
225
304
|
this.log('Analyzing installation...', 'info');
|
|
226
305
|
console.log();
|
|
227
306
|
|
|
228
|
-
// Run all checks
|
|
307
|
+
// Run all checks with Promise.allSettled to prevent throw on first failure
|
|
308
|
+
// Store checks as objects with name property for maintainability
|
|
229
309
|
const checks = [
|
|
230
|
-
this.checkDocker(),
|
|
231
|
-
this.checkQdrant(),
|
|
232
|
-
this.checkFastEmbedModel(),
|
|
233
|
-
this.checkDockerComposeConfig(),
|
|
234
|
-
this.checkCCStatusline(),
|
|
235
|
-
this.checkCSRStatusScript(),
|
|
236
|
-
this.checkASTGrep()
|
|
310
|
+
{ name: 'Docker', fn: () => this.checkDocker() },
|
|
311
|
+
{ name: 'Qdrant', fn: () => this.checkQdrant() },
|
|
312
|
+
{ name: 'FastEmbed', fn: () => this.checkFastEmbedModel() },
|
|
313
|
+
{ name: 'Docker Config', fn: () => this.checkDockerComposeConfig() },
|
|
314
|
+
{ name: 'cc-statusline', fn: () => this.checkCCStatusline() },
|
|
315
|
+
{ name: 'csr-status', fn: () => this.checkCSRStatusScript() },
|
|
316
|
+
{ name: 'AST-Grep', fn: () => this.checkASTGrep() }
|
|
237
317
|
];
|
|
238
318
|
|
|
239
|
-
const
|
|
319
|
+
const settledResults = await Promise.allSettled(checks.map(c => c.fn()));
|
|
320
|
+
|
|
321
|
+
// Convert settled results to standard format, treating rejections as failures
|
|
322
|
+
const results = settledResults.map((result, index) => {
|
|
323
|
+
if (result.status === 'fulfilled') {
|
|
324
|
+
return result.value;
|
|
325
|
+
} else {
|
|
326
|
+
// Rejected check - treat as critical failure
|
|
327
|
+
this.log(
|
|
328
|
+
`Check failed: ${checks[index].name} - ${result.reason?.message || result.reason}`,
|
|
329
|
+
'error'
|
|
330
|
+
);
|
|
331
|
+
return {
|
|
332
|
+
installed: false,
|
|
333
|
+
critical: true,
|
|
334
|
+
name: checks[index].name,
|
|
335
|
+
error: `Check threw error: ${result.reason?.message || result.reason}`
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
});
|
|
240
339
|
|
|
241
340
|
// Categorize results
|
|
242
341
|
const missing = results.filter(r => !r.installed);
|
|
@@ -268,6 +367,40 @@ class UpdateManager {
|
|
|
268
367
|
if (!success) {
|
|
269
368
|
this.log(`Failed to fix: ${issue.name}`, 'error');
|
|
270
369
|
unresolvedCritical.push(issue);
|
|
370
|
+
} else {
|
|
371
|
+
// Re-verify the fix worked by re-running the check
|
|
372
|
+
this.log(`Verifying fix for ${issue.name}...`, 'info');
|
|
373
|
+
const recheckName = issue.name.toLowerCase();
|
|
374
|
+
let recheckResult;
|
|
375
|
+
|
|
376
|
+
if (recheckName.includes('docker') && recheckName.includes('config')) {
|
|
377
|
+
// Docker config specific check
|
|
378
|
+
recheckResult = await this.checkDockerComposeConfig();
|
|
379
|
+
} else if (recheckName.includes('docker') && !recheckName.includes('config')) {
|
|
380
|
+
recheckResult = await this.checkDocker();
|
|
381
|
+
} else if (recheckName.includes('qdrant')) {
|
|
382
|
+
// Give Qdrant a moment to come online before rechecking
|
|
383
|
+
this.log('Waiting 5 seconds for Qdrant to start...', 'info');
|
|
384
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
385
|
+
recheckResult = await this.checkQdrant();
|
|
386
|
+
} else if (recheckName.includes('fastembed')) {
|
|
387
|
+
recheckResult = await this.checkFastEmbedModel();
|
|
388
|
+
} else if (recheckName.includes('cc-statusline')) {
|
|
389
|
+
recheckResult = await this.checkCCStatusline();
|
|
390
|
+
} else if (recheckName.includes('csr-status')) {
|
|
391
|
+
recheckResult = await this.checkCSRStatusScript();
|
|
392
|
+
} else if (recheckName.includes('ast-grep')) {
|
|
393
|
+
recheckResult = await this.checkASTGrep();
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Guard against undefined recheckResult (no matching verifier)
|
|
397
|
+
if (recheckResult === undefined) {
|
|
398
|
+
this.log(`No verifier found for ${issue.name} - cannot verify fix`, 'error');
|
|
399
|
+
unresolvedCritical.push(issue);
|
|
400
|
+
} else if (!recheckResult.installed) {
|
|
401
|
+
this.log(`Fix verification failed for ${issue.name}`, 'error');
|
|
402
|
+
unresolvedCritical.push(issue);
|
|
403
|
+
}
|
|
271
404
|
}
|
|
272
405
|
} else {
|
|
273
406
|
// Issue has no fix and no error message - track as unresolved
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-self-reflect",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.4",
|
|
4
4
|
"description": "Give Claude perfect memory of all your conversations - Installation wizard for Python MCP server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"author": "Claude-Self-Reflect Contributors",
|
|
32
32
|
"type": "module",
|
|
33
33
|
"bin": {
|
|
34
|
-
"claude-self-reflect": "installer/cli.js"
|
|
34
|
+
"claude-self-reflect": "installer/cli.js",
|
|
35
|
+
"csr-status": "scripts/csr-status"
|
|
35
36
|
},
|
|
36
37
|
"files": [
|
|
37
38
|
"installer/**/*.js",
|
|
@@ -43,8 +43,10 @@ class LocalEmbeddingProvider(EmbeddingProvider):
|
|
|
43
43
|
"""Initialize the FastEmbed model."""
|
|
44
44
|
try:
|
|
45
45
|
from fastembed import TextEmbedding
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
# CRITICAL: Use the correct model that matches the rest of the system
|
|
47
|
+
# This must be sentence-transformers/all-MiniLM-L6-v2 (384 dimensions)
|
|
48
|
+
self.model = TextEmbedding(model_name="sentence-transformers/all-MiniLM-L6-v2")
|
|
49
|
+
logger.info("Initialized local FastEmbed model: sentence-transformers/all-MiniLM-L6-v2 (384 dimensions)")
|
|
48
50
|
except ImportError as e:
|
|
49
51
|
logger.error("FastEmbed not installed. Install with: pip install fastembed")
|
|
50
52
|
raise
|