# Claude Mnemonic - Windows Installation Script # Usage: irm https://raw.githubusercontent.com/lukaszraczylo/claude-mnemonic/main/scripts/install.ps1 | iex # # Or with a specific version: # $env:MNEMONIC_VERSION = "v1.0.0"; irm https://raw.githubusercontent.com/lukaszraczylo/claude-mnemonic/main/scripts/install.ps1 | iex param( [string]$Version = $env:MNEMONIC_VERSION, [switch]$Uninstall ) $ErrorActionPreference = "Stop" # Configuration $GitHubRepo = "lukaszraczylo/claude-mnemonic" $InstallDir = "$env:USERPROFILE\.claude\plugins\marketplaces\claude-mnemonic" $CacheDir = "$env:USERPROFILE\.claude\plugins\cache\claude-mnemonic\claude-mnemonic" $PluginsFile = "$env:USERPROFILE\.claude\plugins\installed_plugins.json" $SettingsFile = "$env:USERPROFILE\.claude\settings.json" $MarketplacesFile = "$env:USERPROFILE\.claude\plugins\known_marketplaces.json" $PluginKey = "claude-mnemonic@claude-mnemonic" function Write-Info { param($Message) Write-Host "[INFO] $Message" -ForegroundColor Blue } function Write-Success { param($Message) Write-Host "[OK] $Message" -ForegroundColor Green } function Write-Warn { param($Message) Write-Host "[WARN] $Message" -ForegroundColor Yellow } function Write-Error { param($Message) Write-Host "[ERROR] $Message" -ForegroundColor Red; exit 1 } function Get-LatestVersion { try { $headers = @{} if ($env:GITHUB_TOKEN) { $headers["Authorization"] = "token $env:GITHUB_TOKEN" } $release = Invoke-RestMethod -Uri "https://api.github.com/repos/$GitHubRepo/releases/latest" -Headers $headers return $release.tag_name } catch { $errorMsg = $_.Exception.Message if ($errorMsg -match "rate limit" -or $_.Exception.Response.StatusCode -eq 403) { Write-Host "" Write-Host "[ERROR] GitHub API rate limit exceeded." -ForegroundColor Red Write-Host "" Write-Host "You have a few options:" -ForegroundColor Yellow Write-Host " 1. Wait ~1 hour for the rate limit to reset" Write-Host " 2. Specify a version manually:" Write-Host " `$env:MNEMONIC_VERSION = 'v0.6.1'; irm https://raw.githubusercontent.com/$GitHubRepo/main/scripts/install.ps1 | iex" -ForegroundColor Cyan Write-Host " 3. Use a GitHub token (set `$env:GITHUB_TOKEN)" Write-Host " 4. Clone and build from source:" Write-Host " git clone https://github.com/$GitHubRepo.git" -ForegroundColor Cyan Write-Host " cd claude-mnemonic; make build; make install" -ForegroundColor Cyan exit 1 } Write-Error "Failed to fetch latest version from GitHub: $_" } } function Stop-ExistingWorker { Write-Info "Stopping existing worker (if running)..." Get-Process | Where-Object { $_.ProcessName -like "*worker*" -and $_.Path -like "*claude-mnemonic*" } | Stop-Process -Force -ErrorAction SilentlyContinue Start-Sleep -Seconds 1 } function Install-Release { param([string]$Version) $TempDir = New-Item -ItemType Directory -Path "$env:TEMP\claude-mnemonic-$(Get-Random)" -Force try { # Construct download URL $VersionClean = $Version -replace "^v", "" $ArchiveName = "claude-mnemonic_${VersionClean}_windows_amd64.zip" $DownloadUrl = "https://github.com/$GitHubRepo/releases/download/$Version/$ArchiveName" Write-Info "Downloading $ArchiveName..." $ZipPath = Join-Path $TempDir "release.zip" Invoke-WebRequest -Uri $DownloadUrl -OutFile $ZipPath -UseBasicParsing Write-Info "Extracting archive..." Expand-Archive -Path $ZipPath -DestinationPath $TempDir -Force Stop-ExistingWorker # Create installation directories Write-Info "Installing to $InstallDir..." New-Item -ItemType Directory -Path "$InstallDir\hooks" -Force | Out-Null New-Item -ItemType Directory -Path "$InstallDir\.claude-plugin" -Force | Out-Null # Copy binaries Copy-Item "$TempDir\worker.exe" "$InstallDir\" -Force Copy-Item "$TempDir\mcp-server.exe" "$InstallDir\" -Force Copy-Item "$TempDir\hooks\*" "$InstallDir\hooks\" -Force # Copy plugin configuration Copy-Item "$TempDir\.claude-plugin\*" "$InstallDir\.claude-plugin\" -Force Write-Success "Binaries installed to $InstallDir" } finally { Remove-Item -Recurse -Force $TempDir -ErrorAction SilentlyContinue } } function Register-Plugin { param([string]$Version) $Timestamp = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.000Z") $VersionClean = $Version -replace "^v", "" $CachePath = "$CacheDir\$VersionClean" # Ensure directories exist New-Item -ItemType Directory -Path "$env:USERPROFILE\.claude\plugins" -Force | Out-Null # Clean up old cache versions to prevent stale binaries $CacheBase = Split-Path $CachePath -Parent if (Test-Path $CacheBase) { Write-Info "Cleaning up old cache versions..." Get-ChildItem -Path $CacheBase -Directory | Where-Object { $_.Name -ne $VersionClean } | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue } New-Item -ItemType Directory -Path $CachePath -Force | Out-Null # Create JSON files if they don't exist if (-not (Test-Path $PluginsFile)) { '{"version": 2, "plugins": {}}' | Out-File -Encoding UTF8 $PluginsFile } if (-not (Test-Path $SettingsFile)) { '{}' | Out-File -Encoding UTF8 $SettingsFile } if (-not (Test-Path $MarketplacesFile)) { '{}' | Out-File -Encoding UTF8 $MarketplacesFile } # Copy files to cache directory New-Item -ItemType Directory -Path "$CachePath\.claude-plugin" -Force | Out-Null New-Item -ItemType Directory -Path "$CachePath\hooks" -Force | Out-Null Copy-Item "$InstallDir\*" $CachePath -Recurse -Force -ErrorAction SilentlyContinue try { # Update installed_plugins.json $Plugins = Get-Content $PluginsFile -Raw | ConvertFrom-Json $PluginEntry = @( @{ scope = "user" installPath = $CachePath version = $VersionClean installedAt = $Timestamp lastUpdated = $Timestamp isLocal = $true } ) if (-not $Plugins.plugins) { $Plugins | Add-Member -NotePropertyName "plugins" -NotePropertyValue @{} -Force } $Plugins.plugins | Add-Member -NotePropertyName $PluginKey -NotePropertyValue $PluginEntry -Force $Plugins | ConvertTo-Json -Depth 10 | Out-File -Encoding UTF8 $PluginsFile Write-Success "Plugin registered in installed_plugins.json" # Update settings.json $Settings = Get-Content $SettingsFile -Raw | ConvertFrom-Json if (-not $Settings.enabledPlugins) { $Settings | Add-Member -NotePropertyName "enabledPlugins" -NotePropertyValue @{} -Force } $Settings.enabledPlugins | Add-Member -NotePropertyName $PluginKey -NotePropertyValue $true -Force # Configure statusline $StatuslineCmd = "$InstallDir\hooks\statusline.exe" $StatuslineEntry = @{ type = "command" command = $StatuslineCmd padding = 0 } $Settings | Add-Member -NotePropertyName "statusLine" -NotePropertyValue $StatuslineEntry -Force $Settings | ConvertTo-Json -Depth 10 | Out-File -Encoding UTF8 $SettingsFile Write-Success "Plugin enabled in settings.json" Write-Success "Statusline configured in settings.json" # Update known_marketplaces.json $Marketplaces = Get-Content $MarketplacesFile -Raw | ConvertFrom-Json $MarketplaceEntry = @{ source = @{ source = "directory" path = $InstallDir } installLocation = $InstallDir lastUpdated = $Timestamp } $Marketplaces | Add-Member -NotePropertyName "claude-mnemonic" -NotePropertyValue $MarketplaceEntry -Force $Marketplaces | ConvertTo-Json -Depth 10 | Out-File -Encoding UTF8 $MarketplacesFile Write-Success "Marketplace registered in known_marketplaces.json" # Register MCP server in settings.json $McpBinary = Join-Path $InstallDir "mcp-server.exe" if (Test-Path $McpBinary) { Write-Info "Registering MCP server in settings.json..." # Reload settings to include any previous updates $Settings = Get-Content $SettingsFile -Raw | ConvertFrom-Json # Ensure mcpServers object exists if (-not $Settings.mcpServers) { $Settings | Add-Member -NotePropertyName "mcpServers" -NotePropertyValue @{} -Force } # Add MCP server entry $McpEntry = @{ command = $McpBinary args = @("--project", "`${CLAUDE_PROJECT}") env = @{} } $Settings.mcpServers | Add-Member -NotePropertyName "claude-mnemonic" -NotePropertyValue $McpEntry -Force $Settings | ConvertTo-Json -Depth 10 | Out-File -Encoding UTF8 $SettingsFile Write-Success "MCP server registered successfully" } else { Write-Warn "MCP server binary not found at $McpBinary, skipping MCP registration" } } catch { Write-Warn "Plugin registration encountered an error: $_" } } function Start-Worker { $WorkerPath = Join-Path $InstallDir "worker.exe" if (-not (Test-Path $WorkerPath)) { Write-Error "Worker binary not found at $WorkerPath" } Write-Info "Starting worker service..." Start-Process -FilePath $WorkerPath -WindowStyle Hidden Start-Sleep -Seconds 2 try { $response = Invoke-WebRequest -Uri "http://localhost:37777/health" -UseBasicParsing -TimeoutSec 5 Write-Success "Worker started successfully at http://localhost:37777" } catch { Write-Warn "Worker may not have started properly. Check the process manually." } } function Test-OptionalDependencies { $missingDeps = @() # Check for Python 3.13+ try { $pythonVersion = & python --version 2>&1 if ($pythonVersion -match "Python (\d+)\.(\d+)") { $major = [int]$Matches[1] $minor = [int]$Matches[2] if ($major -lt 3 -or ($major -eq 3 -and $minor -lt 13)) { $missingDeps += "Python 3.13+ (found $major.$minor)" } } } catch { $missingDeps += "Python 3.13+" } # Check for uvx try { $null = Get-Command uvx -ErrorAction Stop } catch { $missingDeps += "uvx" } if ($missingDeps.Count -gt 0) { Write-Host "" Write-Warn "Optional dependencies missing (needed for semantic search):" foreach ($dep in $missingDeps) { Write-Host " - $dep" } Write-Host "" Write-Info "Install on Windows:" Write-Host " winget install Python.Python.3.13" Write-Host " pip install uv" Write-Host "" Write-Info "Note: Requires Python 3.13+." Write-Host "" Write-Info "Semantic search will be disabled until these are installed." Write-Info "Core functionality (SQLite storage, full-text search) will work." Write-Host "" } else { Write-Success "Optional dependencies found (semantic search enabled)" } } function Uninstall-ClaudeMnemonic { param([switch]$KeepData) Write-Info "Uninstalling Claude Mnemonic..." Stop-ExistingWorker # Remove directories Remove-Item -Recurse -Force $InstallDir -ErrorAction SilentlyContinue Remove-Item -Recurse -Force $CacheDir -ErrorAction SilentlyContinue # Remove from JSON files try { if (Test-Path $PluginsFile) { $Plugins = Get-Content $PluginsFile -Raw | ConvertFrom-Json $Plugins.plugins.PSObject.Properties.Remove($PluginKey) $Plugins | ConvertTo-Json -Depth 10 | Out-File -Encoding UTF8 $PluginsFile } if (Test-Path $SettingsFile) { $Settings = Get-Content $SettingsFile -Raw | ConvertFrom-Json if ($Settings.enabledPlugins) { $Settings.enabledPlugins.PSObject.Properties.Remove($PluginKey) } # Remove statusline if it's ours if ($Settings.statusLine -and $Settings.statusLine.command -match "claude-mnemonic") { $Settings.PSObject.Properties.Remove("statusLine") } # Remove MCP server entry if ($Settings.mcpServers) { $Settings.mcpServers.PSObject.Properties.Remove("claude-mnemonic") } $Settings | ConvertTo-Json -Depth 10 | Out-File -Encoding UTF8 $SettingsFile } if (Test-Path $MarketplacesFile) { $Marketplaces = Get-Content $MarketplacesFile -Raw | ConvertFrom-Json $Marketplaces.PSObject.Properties.Remove("claude-mnemonic") $Marketplaces | ConvertTo-Json -Depth 10 | Out-File -Encoding UTF8 $MarketplacesFile } } catch { Write-Warn "Error cleaning up JSON files: $_" } # Handle data directory $DataDir = "$env:USERPROFILE\.claude-mnemonic" if (Test-Path $DataDir) { if ($KeepData) { Write-Warn "Keeping data directory: $DataDir" } else { Remove-Item -Recurse -Force $DataDir -ErrorAction SilentlyContinue Write-Success "Data directory removed" } } Write-Success "Claude Mnemonic uninstalled successfully" } # Main Write-Host "" Write-Host "================================================================" -ForegroundColor Cyan Write-Host " Claude Mnemonic - Windows Installation Script " -ForegroundColor Cyan Write-Host " Persistent Memory System for Claude Code " -ForegroundColor Cyan Write-Host "================================================================" -ForegroundColor Cyan Write-Host "" if ($Uninstall) { Uninstall-ClaudeMnemonic exit 0 } # Get version if (-not $Version) { Write-Info "Fetching latest release..." $Version = Get-LatestVersion } Write-Info "Installing version: $Version" # Install Install-Release -Version $Version Register-Plugin -Version $Version Start-Worker Test-OptionalDependencies Write-Host "" Write-Host "================================================================" -ForegroundColor Green Write-Host " Installation Complete! " -ForegroundColor Green Write-Host "================================================================" -ForegroundColor Green Write-Host " Dashboard: http://localhost:37777" -ForegroundColor White Write-Host "" Write-Host " Start a new Claude Code session to activate memory." -ForegroundColor White Write-Host "================================================================" -ForegroundColor Green Write-Host ""