diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 73181ed..474e953 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -210,6 +210,51 @@ function Start-Worker { } } +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) @@ -286,6 +331,7 @@ Write-Info "Installing version: $Version" Install-Release -Version $Version Register-Plugin -Version $Version Start-Worker +Test-OptionalDependencies Write-Host "" Write-Host "================================================================" -ForegroundColor Green diff --git a/scripts/install.sh b/scripts/install.sh index 95334ed..00d7400 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -133,26 +133,43 @@ download_release() { tmp_dir=$(mktemp -d) trap "rm -rf $tmp_dir" EXIT - # Construct download URL - local archive_name="claude-mnemonic_${version#v}_${platform}.tar.gz" + # Construct download URL (use .zip for Windows, .tar.gz for others) + local archive_ext="tar.gz" + if [[ "$platform" == windows_* ]]; then + archive_ext="zip" + fi + local archive_name="claude-mnemonic_${version#v}_${platform}.${archive_ext}" local download_url="https://github.com/${GITHUB_REPO}/releases/download/${version}/${archive_name}" info "Downloading ${archive_name}..." - if ! curl -sSL -o "$tmp_dir/release.tar.gz" "$download_url"; then + if ! curl -sSL -o "$tmp_dir/release.${archive_ext}" "$download_url"; then error "Failed to download release from: $download_url" fi info "Extracting archive..." - if ! tar -xzf "$tmp_dir/release.tar.gz" -C "$tmp_dir"; then - error "Failed to extract archive" + if [[ "$archive_ext" == "zip" ]]; then + if ! unzip -q "$tmp_dir/release.zip" -d "$tmp_dir"; then + error "Failed to extract archive" + fi + else + if ! tar -xzf "$tmp_dir/release.tar.gz" -C "$tmp_dir"; then + error "Failed to extract archive" + fi fi # Stop existing worker if running info "Stopping existing worker (if running)..." pkill -9 -f 'claude-mnemonic.*worker' 2>/dev/null || true pkill -9 -f '\.claude/plugins/.*/worker' 2>/dev/null || true - lsof -ti :37777 | xargs kill -9 2>/dev/null || true + # Kill process on port 37777 (use lsof on macOS, ss/fuser on Linux) + if command -v lsof &> /dev/null; then + lsof -ti :37777 | xargs kill -9 2>/dev/null || true + elif command -v ss &> /dev/null; then + ss -tlnp 'sport = :37777' 2>/dev/null | awk 'NR>1 {print $6}' | grep -oP 'pid=\K[0-9]+' | xargs -r kill -9 2>/dev/null || true + elif command -v fuser &> /dev/null; then + fuser -k 37777/tcp 2>/dev/null || true + fi sleep 1 # Create installation directories @@ -327,18 +344,18 @@ check_optional_deps() { case "$(uname -s)" in Darwin) info "Install on macOS:" - echo " brew install python3" - echo " pip3 install uvx" + echo " brew install python@3.13" + echo " pip3 install uv" ;; Linux) info "Install on Linux:" echo " sudo apt install python3 python3-pip" - echo " pip3 install uvx" + echo " pip3 install uv" ;; MINGW*|MSYS*|CYGWIN*) info "Install on Windows:" - echo " winget install Python.Python.3" - echo " pip install uvx" + echo " winget install Python.Python.3.13" + echo " pip install uv" ;; esac echo "" @@ -433,7 +450,14 @@ if [[ "${1:-}" == "--uninstall" ]]; then info "Stopping worker processes..." pkill -9 -f 'claude-mnemonic.*worker' 2>/dev/null || true pkill -9 -f '\.claude/plugins/.*/worker' 2>/dev/null || true - lsof -ti :37777 | xargs kill -9 2>/dev/null || true + # Kill process on port 37777 (use lsof on macOS, ss/fuser on Linux) + if command -v lsof &> /dev/null; then + lsof -ti :37777 | xargs kill -9 2>/dev/null || true + elif command -v ss &> /dev/null; then + ss -tlnp 'sport = :37777' 2>/dev/null | awk 'NR>1 {print $6}' | grep -oP 'pid=\K[0-9]+' | xargs -r kill -9 2>/dev/null || true + elif command -v fuser &> /dev/null; then + fuser -k 37777/tcp 2>/dev/null || true + fi sleep 1 info "Removing plugin directories..." diff --git a/scripts/uninstall.ps1 b/scripts/uninstall.ps1 index 8b158be..2b318d0 100644 --- a/scripts/uninstall.ps1 +++ b/scripts/uninstall.ps1 @@ -67,10 +67,19 @@ try { if (Test-Path $SettingsFile) { $Settings = Get-Content $SettingsFile -Raw | ConvertFrom-Json + $modified = $false if ($Settings.enabledPlugins -and $Settings.enabledPlugins.PSObject.Properties[$PluginKey]) { $Settings.enabledPlugins.PSObject.Properties.Remove($PluginKey) + $modified = $true + } + # Remove statusline if it's ours + if ($Settings.statusLine -and $Settings.statusLine.command -match "claude-mnemonic") { + $Settings.PSObject.Properties.Remove("statusLine") + $modified = $true + } + if ($modified) { $Settings | ConvertTo-Json -Depth 10 | Out-File -Encoding UTF8 $SettingsFile - Write-Success "Removed from settings.json" + Write-Success "Removed from settings.json (including statusline)" } } diff --git a/scripts/uninstall.sh b/scripts/uninstall.sh index 2dd48ad..b6f0ba6 100755 --- a/scripts/uninstall.sh +++ b/scripts/uninstall.sh @@ -51,7 +51,14 @@ echo "" info "Stopping worker processes..." pkill -9 -f 'claude-mnemonic.*worker' 2>/dev/null || true pkill -9 -f '\.claude/plugins/.*/worker' 2>/dev/null || true -lsof -ti :37777 | xargs kill -9 2>/dev/null || true +# Kill process on port 37777 (use lsof on macOS, ss/fuser on Linux) +if command -v lsof &> /dev/null; then + lsof -ti :37777 | xargs kill -9 2>/dev/null || true +elif command -v ss &> /dev/null; then + ss -tlnp 'sport = :37777' 2>/dev/null | awk 'NR>1 {print $6}' | grep -oP 'pid=\K[0-9]+' | xargs -r kill -9 2>/dev/null || true +elif command -v fuser &> /dev/null; then + fuser -k 37777/tcp 2>/dev/null || true +fi sleep 1 success "Worker processes stopped" @@ -79,8 +86,9 @@ if command -v jq &> /dev/null; then fi if [[ -f "$SETTINGS_FILE" ]]; then - jq 'del(.enabledPlugins["'"$PLUGIN_KEY"'"])' "$SETTINGS_FILE" > "${SETTINGS_FILE}.tmp" && mv "${SETTINGS_FILE}.tmp" "$SETTINGS_FILE" - success "Removed from settings.json" + # Remove plugin from enabled plugins and remove statusline if it's ours + jq 'del(.enabledPlugins["'"$PLUGIN_KEY"'"]) | if .statusLine.command | test("claude-mnemonic") then del(.statusLine) else . end' "$SETTINGS_FILE" > "${SETTINGS_FILE}.tmp" && mv "${SETTINGS_FILE}.tmp" "$SETTINGS_FILE" + success "Removed from settings.json (including statusline)" fi if [[ -f "$MARKETPLACES_FILE" ]]; then diff --git a/ui/package-lock.json b/ui/package-lock.json index 03c65bb..ac97210 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "claude-mnemonic-dashboard", - "version": "v0.6.1-3-gd68a700-dirty", + "version": "v0.6.1-4-g0a0dfc1-dirty", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-mnemonic-dashboard", - "version": "v0.6.1-3-gd68a700-dirty", + "version": "v0.6.1-4-g0a0dfc1-dirty", "dependencies": { "vue": "^3.5.13" }, diff --git a/ui/package.json b/ui/package.json index f51d0c6..d72f755 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "claude-mnemonic-dashboard", - "version": "v0.6.1-3-gd68a700-dirty", + "version": "v0.6.1-4-g0a0dfc1-dirty", "private": true, "type": "module", "scripts": { diff --git a/ui/src/composables/useSSE.ts b/ui/src/composables/useSSE.ts index 3e78b5a..035d8c1 100644 --- a/ui/src/composables/useSSE.ts +++ b/ui/src/composables/useSSE.ts @@ -61,11 +61,38 @@ export function useSSE() { isConnected.value = false } + // Handle page unload/refresh to ensure SSE connection is closed immediately + const handleBeforeUnload = () => { + disconnect() + } + + // Handle pagehide for mobile browsers and bfcache + const handlePageHide = (event: PageTransitionEvent) => { + if (event.persisted) { + // Page is being cached (bfcache), disconnect but don't prevent reconnect + disconnect() + } + } + + // Handle pageshow to reconnect if page was restored from bfcache + const handlePageShow = (event: PageTransitionEvent) => { + if (event.persisted && !eventSource) { + connect() + } + } + onMounted(() => { + // Add listeners to close SSE on page refresh/navigation + window.addEventListener('beforeunload', handleBeforeUnload) + window.addEventListener('pagehide', handlePageHide) + window.addEventListener('pageshow', handlePageShow) connect() }) onUnmounted(() => { + window.removeEventListener('beforeunload', handleBeforeUnload) + window.removeEventListener('pagehide', handlePageHide) + window.removeEventListener('pageshow', handlePageShow) disconnect() })