claude-self-reflect 6.0.3 → 6.0.5
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/docker-compose.yaml
CHANGED
|
@@ -8,7 +8,7 @@ services:
|
|
|
8
8
|
command: chown -R 1000:1000 /config
|
|
9
9
|
volumes:
|
|
10
10
|
- ${CONFIG_PATH:-~/.claude-self-reflect/config}:/config
|
|
11
|
-
profiles: ["watch", "
|
|
11
|
+
profiles: ["watch", "import", "async", "safe-watch"]
|
|
12
12
|
|
|
13
13
|
# Qdrant vector database - the heart of semantic search
|
|
14
14
|
qdrant:
|
|
@@ -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');
|
|
@@ -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,
|
|
@@ -234,16 +267,30 @@ class UpdateManager {
|
|
|
234
267
|
|
|
235
268
|
async startQdrant() {
|
|
236
269
|
this.log('Starting Qdrant...', 'info');
|
|
270
|
+
|
|
271
|
+
// Try Docker Compose v2 first (docker compose)
|
|
237
272
|
try {
|
|
238
273
|
execSync('docker compose up -d qdrant', {
|
|
239
274
|
cwd: this.packageRoot,
|
|
240
275
|
stdio: 'inherit'
|
|
241
276
|
});
|
|
242
|
-
this.log('Qdrant started', 'success');
|
|
277
|
+
this.log('Qdrant started (Docker Compose v2)', 'success');
|
|
243
278
|
return true;
|
|
244
|
-
} catch (
|
|
245
|
-
this.log(
|
|
246
|
-
|
|
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
|
+
}
|
|
247
294
|
}
|
|
248
295
|
}
|
|
249
296
|
|
|
@@ -257,18 +304,38 @@ class UpdateManager {
|
|
|
257
304
|
this.log('Analyzing installation...', 'info');
|
|
258
305
|
console.log();
|
|
259
306
|
|
|
260
|
-
// 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
|
|
261
309
|
const checks = [
|
|
262
|
-
this.checkDocker(),
|
|
263
|
-
this.checkQdrant(),
|
|
264
|
-
this.checkFastEmbedModel(),
|
|
265
|
-
this.checkDockerComposeConfig(),
|
|
266
|
-
this.checkCCStatusline(),
|
|
267
|
-
this.checkCSRStatusScript(),
|
|
268
|
-
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() }
|
|
269
317
|
];
|
|
270
318
|
|
|
271
|
-
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
|
+
});
|
|
272
339
|
|
|
273
340
|
// Categorize results
|
|
274
341
|
const missing = results.filter(r => !r.installed);
|
|
@@ -300,6 +367,40 @@ class UpdateManager {
|
|
|
300
367
|
if (!success) {
|
|
301
368
|
this.log(`Failed to fix: ${issue.name}`, 'error');
|
|
302
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
|
+
}
|
|
303
404
|
}
|
|
304
405
|
} else {
|
|
305
406
|
// Issue has no fix and no error message - track as unresolved
|