diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 8191847d..84015344 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,7 +10,7 @@ "plugins": [ { "name": "claude-mem", - "version": "7.1.1", + "version": "7.1.2", "source": "./plugin", "description": "Persistent memory system for Claude Code - context compression across sessions" } diff --git a/CLAUDE.md b/CLAUDE.md index 9fb2215b..9b034175 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ Claude-mem is a Claude Code plugin providing persistent memory across sessions. It captures tool usage, compresses observations using the Claude Agent SDK, and injects relevant context into future sessions. -**Current Version**: 7.1.1 +**Current Version**: 7.1.2 ## Architecture diff --git a/package.json b/package.json index 0f9056d1..90ec87cf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "claude-mem", - "version": "7.1.1", + "version": "7.1.2", "description": "Memory compression system for Claude Code - persist context across sessions", "keywords": [ "claude", diff --git a/plugin/.claude-plugin/plugin.json b/plugin/.claude-plugin/plugin.json index d280d41a..3728f529 100644 --- a/plugin/.claude-plugin/plugin.json +++ b/plugin/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "claude-mem", - "version": "7.1.1", + "version": "7.1.2", "description": "Persistent memory system for Claude Code - seamlessly preserve context across sessions", "author": { "name": "Alex Newman" diff --git a/scripts/smart-install.js b/scripts/smart-install.js index a64a3a3d..1a04fd24 100644 --- a/scripts/smart-install.js +++ b/scripts/smart-install.js @@ -24,18 +24,56 @@ function isBunInstalled() { stdio: ['pipe', 'pipe', 'pipe'], shell: IS_WINDOWS }); - return result.status === 0; + if (result.status === 0) return true; } catch { - return false; + // PATH check failed, try common installation paths } + + // Check common installation paths (handles fresh installs before PATH reload) + const bunPaths = IS_WINDOWS + ? [join(homedir(), '.bun', 'bin', 'bun.exe')] + : [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun']; + + return bunPaths.some(existsSync); +} + +/** + * Get the Bun executable path (from PATH or common install locations) + */ +function getBunPath() { + // Try PATH first + try { + const result = spawnSync('bun', ['--version'], { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + shell: IS_WINDOWS + }); + if (result.status === 0) return 'bun'; + } catch { + // Not in PATH + } + + // Check common installation paths + const bunPaths = IS_WINDOWS + ? [join(homedir(), '.bun', 'bin', 'bun.exe')] + : [join(homedir(), '.bun', 'bin', 'bun'), '/usr/local/bin/bun']; + + for (const bunPath of bunPaths) { + if (existsSync(bunPath)) return bunPath; + } + + return null; } /** * Get Bun version if installed */ function getBunVersion() { + const bunPath = getBunPath(); + if (!bunPath) return null; + try { - const result = spawnSync('bun', ['--version'], { + const result = spawnSync(bunPath, ['--version'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], shell: IS_WINDOWS @@ -56,10 +94,17 @@ function isUvInstalled() { stdio: ['pipe', 'pipe', 'pipe'], shell: IS_WINDOWS }); - return result.status === 0; + if (result.status === 0) return true; } catch { - return false; + // PATH check failed, try common installation paths } + + // Check common installation paths (handles fresh installs before PATH reload) + const uvPaths = IS_WINDOWS + ? [join(homedir(), '.local', 'bin', 'uv.exe'), join(homedir(), '.cargo', 'bin', 'uv.exe')] + : [join(homedir(), '.local', 'bin', 'uv'), join(homedir(), '.cargo', 'bin', 'uv'), '/usr/local/bin/uv']; + + return uvPaths.some(existsSync); } /** @@ -226,12 +271,21 @@ function needsInstall() { * Install dependencies using Bun */ function installDeps() { + const bunPath = getBunPath(); + if (!bunPath) { + throw new Error('Bun executable not found'); + } + console.error('📦 Installing dependencies with Bun...'); + + // Quote path for Windows paths with spaces + const bunCmd = IS_WINDOWS && bunPath.includes(' ') ? `"${bunPath}"` : bunPath; + try { - execSync('bun install', { cwd: ROOT, stdio: 'inherit', shell: IS_WINDOWS }); + execSync(`${bunCmd} install`, { cwd: ROOT, stdio: 'inherit', shell: IS_WINDOWS }); } catch { // Retry with force flag - execSync('bun install --force', { cwd: ROOT, stdio: 'inherit', shell: IS_WINDOWS }); + execSync(`${bunCmd} install --force`, { cwd: ROOT, stdio: 'inherit', shell: IS_WINDOWS }); } // Write version marker diff --git a/src/shared/hook-error-handler.ts b/src/shared/hook-error-handler.ts index 30e9b7bf..4711d28c 100644 --- a/src/shared/hook-error-handler.ts +++ b/src/shared/hook-error-handler.ts @@ -4,8 +4,10 @@ */ export function handleWorkerError(error: any): never { if (error.cause?.code === 'ECONNREFUSED' || + error.code === 'ConnectionRefused' || // Bun-specific error format error.name === 'TimeoutError' || - error.message?.includes('fetch failed')) { + error.message?.includes('fetch failed') || + error.message?.includes('Unable to connect')) { throw new Error( "There's a problem with the worker. Try: npm run worker:restart" ); diff --git a/src/shared/worker-utils.ts b/src/shared/worker-utils.ts index 60238ed1..27d800f7 100644 --- a/src/shared/worker-utils.ts +++ b/src/shared/worker-utils.ts @@ -194,13 +194,6 @@ export async function ensureWorkerRunning(): Promise { // Try to start the worker const started = await startWorker(); - // Final health check before throwing error - // Worker might be already running but was temporarily unresponsive - if (!started && await isWorkerHealthy()) { - await ensureWorkerVersionMatches(); - return; - } - if (!started) { const port = getWorkerPort(); throw new Error( @@ -209,4 +202,22 @@ export async function ensureWorkerRunning(): Promise { `If already running, try: npm run worker:restart` ); } + + // Wait for worker to become responsive after starting + // Try up to 5 times with 500ms delays (2.5 seconds total) + for (let i = 0; i < 5; i++) { + await new Promise(resolve => setTimeout(resolve, 500)); + if (await isWorkerHealthy()) { + await ensureWorkerVersionMatches(); + return; + } + } + + // Worker started but isn't responding + const port = getWorkerPort(); + logger.error('SYSTEM', 'Worker started but not responding to health checks'); + throw new Error( + `Worker service started but is not responding on port ${port}.\n\n` + + `Try: npm run worker:restart` + ); }