diff --git a/CLAUDE.md b/CLAUDE.md index d6007ade..5421d646 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -77,6 +77,7 @@ Settings are managed in `~/.claude-mem/settings.json`. The file is auto-created ## Requirements - **Bun** >= 1.0 (all platforms - auto-installed if missing) +- **uv** (all platforms - auto-installed if missing, provides Python for Chroma) - Node.js >= 18 (build tools only) ## Quick Reference diff --git a/README.md b/README.md index 5f6705b6..be8126b2 100644 --- a/README.md +++ b/README.md @@ -264,6 +264,7 @@ See [CHANGELOG.md](CHANGELOG.md) for complete version history. - **Node.js**: 18.0.0 or higher - **Claude Code**: Latest version with plugin support - **Bun**: JavaScript runtime and process manager (auto-installed if missing) +- **uv**: Python package manager for vector search (auto-installed if missing) - **SQLite 3**: For persistent storage (bundled) --- diff --git a/docs/PM2-TO-BUN-MIGRATION.md b/docs/PM2-TO-BUN-MIGRATION.md index fa38a049..8875d6e1 100644 --- a/docs/PM2-TO-BUN-MIGRATION.md +++ b/docs/PM2-TO-BUN-MIGRATION.md @@ -179,7 +179,7 @@ npm run worker:logs # View logs The migration system uses a marker-based approach to perform PM2 cleanup exactly once. -**Implementation**: `src/shared/worker-utils.ts:72-86` +**Implementation**: `src/shared/worker-utils.ts:73-86` ```typescript // Clean up legacy PM2 (one-time migration) @@ -352,7 +352,6 @@ npm run sync-marketplace 4. **Migration Check**: ``` startWorker() checks: - - Platform: Mac/Linux/Windows? - Marker: ~/.claude-mem/.pm2-migrated exists? NO ``` @@ -462,15 +461,7 @@ npm run worker:logs # tail -f logs/worker-YYYY-MM-DD.log Exists: NO ``` -2. **Platform Check**: - ``` - Platform: darwin - Condition: process.platform !== 'win32' - Result: TRUE (not Windows) - Action: Proceed to PM2 cleanup - ``` - -3. **PM2 Cleanup**: +2. **PM2 Cleanup**: ```bash Command: pm2 delete claude-mem-worker @@ -487,14 +478,14 @@ npm run worker:logs # tail -f logs/worker-YYYY-MM-DD.log → Error ignored (catch block) ``` -4. **Marker Creation**: +3. **Marker Creation**: ``` File: ~/.claude-mem/.pm2-migrated Content: 2025-12-13T00:18:39.673Z Created: Regardless of PM2 cleanup success/failure ``` -5. **New Worker**: +4. **New Worker**: ```bash Spawn: bun plugin/scripts/worker-cli.js start 37777 Detached: true (process runs independently) @@ -517,7 +508,6 @@ npm run worker:logs # tail -f logs/worker-YYYY-MM-DD.log **First Session**: - Marker check → Missing -- Platform check → Pass (not Windows) - PM2 cleanup → Attempted - Marker created → `~/.claude-mem/.pm2-migrated` @@ -1187,39 +1177,7 @@ if (isRunning) { // Prevents double-start ``` -### Scenario 5: Windows Platform Detection Fails - -**Symptoms**: -- Windows system attempts PM2 cleanup -- Errors in logs about PM2 not found -- Migration marker created on Windows - -**Diagnosis**: -```bash -# Check platform detection -node -e "console.log(process.platform)" -# Should output: win32 - -# Check marker file (shouldn't exist on Windows) -dir %USERPROFILE%\.claude-mem\.pm2-migrated -``` - -**Cause**: -- `process.platform` returns unexpected value -- Code running in WSL (reports 'linux' not 'win32') -- Environment misconfiguration - -**Resolution**: -```bash -# If running in WSL, this is expected -# WSL reports 'linux' → PM2 cleanup runs -# This is correct behavior (treat WSL as Linux) - -# If native Windows reporting wrong platform: -# File bug report (platform detection broken) -``` - -### Scenario 6: Health Check Fails (Worker Running but Unhealthy) +### Scenario 5: Health Check Fails (Worker Running but Unhealthy) **Symptoms**: - Worker process exists @@ -1271,7 +1229,7 @@ curl http://localhost:37777/health # Should return: {"status":"healthy"} ``` -### Scenario 7: Fresh Install (Never Had PM2) +### Scenario 6: Fresh Install (Never Had PM2) **Symptoms**: - User installs claude-mem 7.0.10+ for first time @@ -1293,19 +1251,18 @@ cat ~/.claude-mem/.pm2-migrated ``` First hook trigger: 1. Marker check: Missing -2. Platform check: Mac/Linux -3. PM2 cleanup: Attempted -4. Error: "command not found: pm2" -5. Catch block: Error ignored -6. Marker creation: Success -7. Worker start: Success +2. PM2 cleanup: Attempted +3. Error: "command not found: pm2" +4. Catch block: Error ignored +5. Marker creation: Success +6. Worker start: Success Result: Normal startup, marker created, no issues ``` **No Action Needed**: This is expected and correct behavior. -### Scenario 8: Manual Marker Deletion +### Scenario 7: Manual Marker Deletion **Symptoms**: - User deletes `.pm2-migrated` file @@ -1322,13 +1279,12 @@ ls ~/.claude-mem/.pm2-migrated ``` Next hook trigger: 1. Marker check: Missing -2. Platform check: Mac/Linux -3. PM2 cleanup: Attempted -4. Result: No PM2 worker exists (already cleaned) -5. Error: "process claude-mem-worker not found" -6. Catch block: Ignored -7. Marker recreation: Success -8. Worker start: Normal +2. PM2 cleanup: Attempted +3. Result: No PM2 worker exists (already cleaned) +4. Error: "process claude-mem-worker not found" +5. Catch block: Ignored +6. Marker recreation: Success +7. Worker start: Normal Result: No harm done, marker recreated ``` @@ -1487,7 +1443,7 @@ npm test -- src/services/process/ProcessManager.test.ts **Migration Marker Logic**: ```typescript -// src/shared/worker-utils.ts:76-86 +// src/shared/worker-utils.ts:74-86 const pm2MigratedMarker = join(DATA_DIR, '.pm2-migrated'); if (!existsSync(pm2MigratedMarker)) { diff --git a/scripts/smart-install.js b/scripts/smart-install.js index 9b88ab32..a64a3a3d 100644 --- a/scripts/smart-install.js +++ b/scripts/smart-install.js @@ -2,8 +2,8 @@ /** * Smart Install Script for claude-mem * - * Ensures Bun runtime is installed (auto-installs if missing) - * and handles dependency installation when needed. + * Ensures Bun runtime and uv (Python package manager) are installed + * (auto-installs if missing) and handles dependency installation when needed. */ import { existsSync, readFileSync, writeFileSync } from 'fs'; import { execSync, spawnSync } from 'child_process'; @@ -46,6 +46,38 @@ function getBunVersion() { } } +/** + * Check if uv is installed and accessible + */ +function isUvInstalled() { + try { + const result = spawnSync('uv', ['--version'], { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + shell: IS_WINDOWS + }); + return result.status === 0; + } catch { + return false; + } +} + +/** + * Get uv version if installed + */ +function getUvVersion() { + try { + const result = spawnSync('uv', ['--version'], { + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + shell: IS_WINDOWS + }); + return result.status === 0 ? result.stdout.trim() : null; + } catch { + return null; + } +} + /** * Install Bun automatically based on platform */ @@ -111,6 +143,71 @@ function installBun() { } } +/** + * Install uv automatically based on platform + */ +function installUv() { + console.error('🐍 Installing uv for Python/Chroma support...'); + + try { + if (IS_WINDOWS) { + // Windows: Use PowerShell installer + console.error(' Installing via PowerShell...'); + execSync('powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"', { + stdio: 'inherit', + shell: true + }); + } else { + // Unix/macOS: Use curl installer + console.error(' Installing via curl...'); + execSync('curl -LsSf https://astral.sh/uv/install.sh | sh', { + stdio: 'inherit', + shell: true + }); + } + + // Verify installation + if (isUvInstalled()) { + const version = getUvVersion(); + console.error(`✅ uv ${version} installed successfully`); + return true; + } else { + // uv may be installed but not in PATH yet for this session + // Try common installation paths + 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']; + + for (const uvPath of uvPaths) { + if (existsSync(uvPath)) { + console.error(`✅ uv installed at ${uvPath}`); + console.error('⚠️ Please restart your terminal or add uv to PATH:'); + if (IS_WINDOWS) { + console.error(` $env:Path += ";${join(homedir(), '.local', 'bin')}"`); + } else { + console.error(` export PATH="$HOME/.local/bin:$PATH"`); + } + return true; + } + } + + throw new Error('uv installation completed but binary not found'); + } + } catch (error) { + console.error('❌ Failed to install uv automatically'); + console.error(' Please install manually:'); + if (IS_WINDOWS) { + console.error(' - winget install astral-sh.uv'); + console.error(' - Or: powershell -c "irm https://astral.sh/uv/install.ps1 | iex"'); + } else { + console.error(' - curl -LsSf https://astral.sh/uv/install.sh | sh'); + console.error(' - Or: brew install uv (macOS)'); + } + console.error(' Then restart your terminal and try again.'); + throw error; + } +} + /** * Check if dependencies need to be installed */ @@ -142,13 +239,14 @@ function installDeps() { writeFileSync(MARKER, JSON.stringify({ version: pkg.version, bun: getBunVersion(), + uv: getUvVersion(), installedAt: new Date().toISOString() })); } // Main execution try { - // Step 1: Ensure Bun is installed + // Step 1: Ensure Bun is installed (REQUIRED) if (!isBunInstalled()) { installBun(); @@ -160,7 +258,19 @@ try { } } - // Step 2: Install dependencies if needed + // Step 2: Ensure uv is installed (REQUIRED for vector search) + if (!isUvInstalled()) { + installUv(); + + // Re-check after installation + if (!isUvInstalled()) { + console.error('❌ uv is required but not available in PATH'); + console.error(' Please restart your terminal after installation'); + process.exit(1); + } + } + + // Step 3: Install dependencies if needed if (needsInstall()) { installDeps(); console.error('✅ Dependencies installed');