install-docker.ps1•34.5 kB
#!/usr/bin/env powershell
param(
    [string]$Option = "",
    [switch]$Help,
    [switch]$Status,
    [switch]$Reset,
    [switch]$VerboseOutput
)
# Script-level variables for folder and Docker args
$script:Folders = @()
$script:DockerArgs = @()
# Colors and output functions
function Write-Success { param($Message) Write-Host "[SUCCESS] $Message" -ForegroundColor Green }
function Write-Error { param($Message) Write-Host "[ERROR] $Message" -ForegroundColor Red }
function Write-Warning { param($Message) Write-Host "[WARNING] $Message" -ForegroundColor Yellow }
function Write-Info { param($Message) Write-Host "[INFO] $Message" -ForegroundColor Blue }
function Write-Header {
    Write-Host ""
    Write-Host "================================================================" -ForegroundColor Blue
    Write-Host "                         CLAUDE                                 " -ForegroundColor Blue  
    Write-Host "                   SERVER COMMANDER                             " -ForegroundColor Blue
    Write-Host "                    Docker Installer                           " -ForegroundColor Blue
    Write-Host "================================================================" -ForegroundColor Blue
    Write-Host ""
    Write-Info "Experiment with AI in secure sandbox environment that won't mess up your main computer"
    Write-Host ""
}
function Test-Docker {
    while ($true) {
        try {
            $null = Get-Command docker -ErrorAction Stop
        } catch {
            Write-Error "Docker is not installed or not found"
            Write-Host ""
            Write-Error "Please install Docker first:"
            Write-Error "Download Docker Desktop: https://www.docker.com/products/docker-desktop/"
            Write-Host ""
            $null = Read-Host "Press Enter when Docker Desktop is installed or Ctrl+C to exit"
            continue
        }
        Write-Info "Checking Docker installation and daemon status..."
        try {
            $null = docker info 2>$null
            if ($LASTEXITCODE -eq 0) {
                Write-Success "Docker is installed and running"
                break
            } else {
                throw "Docker daemon not running"
            }
        } catch {
            Write-Error "Docker is installed but not running"
            Write-Host ""
            Write-Error "Please start Docker Desktop and try again"
            Write-Info "Make sure Docker Desktop is fully started (check system tray)"
            Write-Host ""
            $null = Read-Host "Press Enter when Docker Desktop is running or Ctrl+C to exit"
            continue
        }
    }
}
function Get-DockerImage {
    Write-Info "Pulling latest Docker image (this may take a moment)..."
    try {
        docker pull mcp/desktop-commander:latest
        if ($LASTEXITCODE -eq 0) {
            Write-Success "Docker image ready: mcp/desktop-commander:latest"
        } else {
            Write-Error "Failed to pull Docker image"
            Write-Info "Check your internet connection and Docker Hub access"
            exit 1
        }
    } catch {
        Write-Error "Failed to get Docker image"
        Write-Info "This could be a network issue or Docker Hub being unavailable"
        exit 1
    }
}function Ask-ForFolders {
    Write-Host ""
    Write-Host "Folder Access Setup" -ForegroundColor Blue
    Write-Info "By default, Desktop Commander will have access to your user folder:"
    Write-Info "Folder: $env:USERPROFILE"
    Write-Host ""
    $response = Read-Host "Press Enter to accept user folder access or 'y' to customize"
    
    $script:Folders = @()
    
    if ($response -match "^[Yy]$") {
        Write-Host ""
        Write-Info "Custom folder selection:"
        $homeResponse = Read-Host "Mount your complete home directory ($env:USERPROFILE)? [Y/n]"
        
        switch ($homeResponse.ToLower()) {
            { $_ -in @("n", "no") } { 
                Write-Info "Skipping home directory"
            }
            default { 
                $script:Folders += $env:USERPROFILE
                Write-Success "Added home directory access"
            }
        }
        Write-Host ""
        Write-Info "Add extra folders outside home directory (optional):"
        
        while ($true) {
            $customDir = Read-Host "Enter folder path (or Enter to finish)"
            
            if ([string]::IsNullOrEmpty($customDir)) {
                break
            }
            
            $customDir = [System.Environment]::ExpandEnvironmentVariables($customDir)
            $customDir = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($customDir)
            
            if (Test-Path $customDir -PathType Container) {
                $script:Folders += $customDir
                Write-Success "Added: $customDir"
            } else {
                $addAnyway = Read-Host "Folder doesn't exist. Add anyway? [y/N]"
                if ($addAnyway -match "^[Yy]$") {
                    $script:Folders += $customDir
                    Write-Info "Added: $customDir (will create if needed)"
                }
            }
        }
        if ($script:Folders.Count -eq 0) {
            Write-Host ""
            Write-Warning "WARNING: No folders selected - Desktop Commander will have NO file access"
            Write-Host ""
            Write-Info "This means:"
            Write-Host "  - Desktop Commander cannot read or write any files on your computer"
            Write-Host "  - It cannot help with coding projects, file management, or document editing"
            Write-Host "  - It will only work for system commands and package installation"
            Write-Host "  - This makes Desktop Commander much less useful than intended"
            Write-Host ""
            Write-Info "You probably want to share at least some folder to work with files"
            Write-Info "Most users share their home directory: $env:USERPROFILE"
            Write-Host ""
            $confirm = Read-Host "Continue with NO file access? [y/N]"
            if ($confirm -notmatch "^[Yy]$") {
                Write-Info "Restarting folder selection..."
                Ask-ForFolders
                return
            }
            Write-Warning "Proceeding with no file access - Desktop Commander will be limited"
        }
    } else {
        $script:Folders += $env:USERPROFILE
        Write-Success "Using default access to your user folder"
    }
}
function Initialize-Volumes {
    Write-Info "Setting up persistent development environment"
    Write-Host ""
    Write-Info "Creating essential volumes for development persistence:"
    Write-Info "- dc-system: All system packages, binaries, libraries"
    Write-Info "- dc-home: User configs, dotfiles, SSH keys, git config"
    Write-Info "- dc-workspace: Development files and projects"
    Write-Info "- dc-packages: Package databases, caches, logs"
    Write-Host ""
    $volumes = @("dc-system", "dc-home", "dc-workspace", "dc-packages")
    $volumesCreated = 0
    foreach ($volume in $volumes) {
        try {
            $null = docker volume inspect $volume 2>$null
            if ($LASTEXITCODE -ne 0) {
                docker volume create $volume | Out-Null
                if ($LASTEXITCODE -eq 0) {
                    Write-Success "Created volume: $volume"
                    $volumesCreated++
                } else {
                    Write-Warning "Failed to create volume: $volume"
                }
            } else {
                Write-Info "Volume already exists: $volume"
            }
        } catch {
            Write-Warning "Could not manage volume: $volume"
        }
    }
    if ($volumesCreated -gt 0) {
        Write-Host ""
        Write-Success "Created $volumesCreated new volume(s)"
    }
    Write-Success "Persistent environment ready - your tools will survive restarts!"
}
function Build-DockerArgs {
    Write-Info "Building Docker configuration..."
    $script:DockerArgs = @("run", "-i", "--rm")
    $essentialVolumes = @(
        "dc-system:/usr",
        "dc-home:/root", 
        "dc-workspace:/workspace",
        "dc-packages:/var"
    )
    foreach ($volume in $essentialVolumes) {
        $script:DockerArgs += "-v"
        $script:DockerArgs += $volume
    }
    foreach ($folder in $script:Folders) {
        # Get the full path structure after drive letter for absolute mounting
        if ($folder -match '^([A-Za-z]):(.+)$') {
            $absolutePath = $matches[2].TrimStart('\').Replace('\', '/')
            $script:DockerArgs += "-v"
            $script:DockerArgs += "${folder}:/home/$absolutePath"
        } else {
            # Fallback for non-standard paths - use basename
            $folderName = Split-Path $folder -Leaf
            $script:DockerArgs += "-v"
            $script:DockerArgs += "${folder}:/home/${folderName}"
        }
    }
    $script:DockerArgs += "mcp/desktop-commander:latest"
    if ($VerboseOutput) {
        Write-Info "Docker configuration ready"
        Write-Info "Essential volumes: 4 volumes"
        Write-Info "Mounted folders: $($script:Folders.Count) folders" 
        Write-Info "Container mode: Auto-remove after each use (--rm)"
    }
}function Find-ClaudeProcess {
    Write-Info "Looking for Claude Desktop processes..."
    
    # Try different process name patterns for Claude
    $processNames = @("Claude", "claude", "Claude Desktop", "claude-desktop", "ClaudeDesktop")
    $foundProcesses = @()
    
    foreach ($processName in $processNames) {
        if ($VerboseOutput) { Write-Info "Checking for process pattern: $processName" }
        try {
            $processes = Get-Process -Name $processName -ErrorAction SilentlyContinue
            if ($processes) {
                foreach ($proc in $processes) {
                    $procPath = try { $proc.Path } catch { "Unknown" }
                    $procWindowTitle = try { $proc.MainWindowTitle } catch { "N/A" }
                    
                    $foundProcesses += @{
                        Name = $proc.ProcessName
                        Id = $proc.Id
                        Path = $procPath
                        WindowTitle = $procWindowTitle
                    }
                    if ($VerboseOutput) { Write-Info "Found: $($proc.ProcessName) (PID: $($proc.Id))" }
                }
            }
        } catch {
            if ($VerboseOutput) { Write-Info "No processes found for pattern: $processName" }
        }
    }
    
    # Also try WMI for more comprehensive search
    if ($VerboseOutput) { Write-Info "Searching with WMI for Claude-related processes..." }
    try {
        $wmiProcesses = Get-WmiObject Win32_Process | Where-Object { 
            $_.Name -like "*claude*" -or 
            $_.CommandLine -like "*claude*" -or
            $_.ExecutablePath -like "*claude*"
        }
        
        foreach ($proc in $wmiProcesses) {
            $foundProcesses += @{
                Name = $proc.Name
                Id = $proc.ProcessId
                Path = $proc.ExecutablePath
                CommandLine = $proc.CommandLine
            }
            if ($VerboseOutput) { Write-Info "WMI Found: $($proc.Name) (PID: $($proc.ProcessId))" }
        }
    } catch {
        if ($VerboseOutput) { Write-Info "WMI search failed or no additional processes found" }
    }
    
    return $foundProcesses
}
function Stop-ClaudeProcess {
    Write-Info "Attempting to stop Claude Desktop..."
    
    $processes = Find-ClaudeProcess
    
    if ($processes.Count -eq 0) {
        Write-Info "No Claude Desktop processes found running"
        return $true
    }
    
    Write-Info "Found $($processes.Count) Claude-related process(es)"
    foreach ($proc in $processes) {
        Write-Info "  - $($proc.Name) (PID: $($proc.Id))"
        if ($proc.Path -and $proc.Path -ne "Unknown" -and $VerboseOutput) {
            Write-Info "    Path: $($proc.Path)"
        }
    }
    
    $stoppedCount = 0
    
    # Method 1: Graceful shutdown using Stop-Process
    Write-Info "Attempting graceful shutdown..."
    foreach ($proc in $processes) {
        try {
            Stop-Process -Id $proc.Id -ErrorAction Stop
            Write-Success "Gracefully stopped: $($proc.Name) (PID: $($proc.Id))"
            $stoppedCount++
        } catch {
            Write-Warning "Could not gracefully stop: $($proc.Name) (PID: $($proc.Id)) - $($_.Exception.Message)"
        }
    }
    
    # Wait a moment for graceful shutdown
    Start-Sleep -Seconds 2
    
    # Method 2: Force kill remaining processes
    $remainingProcesses = Find-ClaudeProcess
    if ($remainingProcesses.Count -gt 0) {
        Write-Info "Force stopping remaining processes..."
        foreach ($proc in $remainingProcesses) {
            try {
                Stop-Process -Id $proc.Id -Force -ErrorAction Stop
                Write-Success "Force stopped: $($proc.Name) (PID: $($proc.Id))"
                $stoppedCount++
            } catch {
                Write-Error "Could not force stop: $($proc.Name) (PID: $($proc.Id)) - $($_.Exception.Message)"
            }
        }
    }
    
    # Final verification
    Start-Sleep -Seconds 1
    $finalProcesses = Find-ClaudeProcess
    if ($finalProcesses.Count -eq 0) {
        Write-Success "All Claude processes stopped successfully"
        return $true
    } else {
        Write-Warning "Some Claude processes may still be running"
        return $false
    }
}
function Find-ClaudeExecutable {
    if ($VerboseOutput) { Write-Info "Searching for Claude Desktop executable..." }
    
    # Common installation paths for Claude Desktop
    $claudePaths = @(
        "$env:LOCALAPPDATA\Programs\Claude\Claude.exe",
        "$env:PROGRAMFILES\Claude\Claude.exe", 
        "$env:PROGRAMFILES(x86)\Claude\Claude.exe",
        "$env:APPDATA\Claude\Claude.exe",
        "$env:USERPROFILE\AppData\Local\Programs\Claude\Claude.exe"
    )
    
    if ($VerboseOutput) { Write-Info "Checking standard installation paths..." }
    foreach ($path in $claudePaths) {
        if ($VerboseOutput) { Write-Info "Checking: $path" }
        if (Test-Path $path) {
            if ($VerboseOutput) { Write-Info "Found Claude executable: $path" }
            return $path
        }
    }
    
    # Check registry for installation location
    if ($VerboseOutput) { Write-Info "Searching Windows registry for Claude installation..." }
    try {
        $uninstallKeys = @(
            "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*",
            "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*",
            "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*"
        )
        
        foreach ($keyPath in $uninstallKeys) {
            try {
                $entries = Get-ItemProperty $keyPath -ErrorAction SilentlyContinue | Where-Object { 
                    $_.DisplayName -like "*Claude*" 
                }
                foreach ($entry in $entries) {
                    if ($entry.InstallLocation) {
                        $possibleExe = Join-Path $entry.InstallLocation "Claude.exe"
                        if ($VerboseOutput) { Write-Info "Registry found: $($entry.DisplayName) at $possibleExe" }
                        if (Test-Path $possibleExe) {
                            if ($VerboseOutput) { Write-Info "Found Claude executable via registry: $possibleExe" }
                            return $possibleExe
                        }
                    }
                    if ($entry.UninstallString) {
                        $uninstallDir = Split-Path $entry.UninstallString -Parent
                        $possibleExe = Join-Path $uninstallDir "Claude.exe"
                        if ($VerboseOutput) { Write-Info "Trying uninstall directory: $possibleExe" }
                        if (Test-Path $possibleExe) {
                            if ($VerboseOutput) { Write-Info "Found Claude executable via uninstall path: $possibleExe" }
                            return $possibleExe
                        }
                    }
                }
            } catch {
                if ($VerboseOutput) { Write-Info "Could not check registry key: $keyPath" }
            }
        }
    } catch {
        if ($VerboseOutput) { Write-Info "Registry search failed" }
    }
    
    # Last resort: search common program directories
    if ($VerboseOutput) { Write-Info "Performing broader search in program directories..." }
    $searchDirs = @(
        "$env:LOCALAPPDATA\Programs",
        "$env:PROGRAMFILES",
        "$env:PROGRAMFILES(x86)"
    )
    
    foreach ($dir in $searchDirs) {
        if (Test-Path $dir) {
            if ($VerboseOutput) { Write-Info "Searching in: $dir" }
            try {
                $found = Get-ChildItem -Path $dir -Recurse -Name "Claude.exe" -ErrorAction SilentlyContinue | Select-Object -First 1
                if ($found) {
                    $fullPath = Join-Path $dir $found
                    if ($VerboseOutput) { Write-Info "Found Claude executable via search: $fullPath" }
                    return $fullPath
                }
            } catch {
                if ($VerboseOutput) { Write-Info "Could not search in: $dir" }
            }
        }
    }
    
    return $null
}
function Start-ClaudeProcess {
    Write-Info "Attempting to start Claude Desktop..."
    
    $claudePath = Find-ClaudeExecutable
    
    if (-not $claudePath) {
        Write-Warning "Could not find Claude Desktop executable"
        Write-Info "Claude may not be installed or may be in a non-standard location"
        Write-Info "You can start Claude Desktop manually after installation completes"
        return $false
    }
    
    Write-Success "Found Claude executable: $claudePath"
    
    try {
        Write-Info "Starting Claude Desktop..."
        if ($VerboseOutput) { Write-Info "Executing: Start-Process -FilePath '$claudePath' -PassThru" }
        $process = Start-Process -FilePath $claudePath -PassThru -ErrorAction Stop
        
        if ($process) {
            Write-Success "Process started with PID: $($process.Id)"
            
            # Wait for the process to initialize
            Write-Info "Waiting 5 seconds for Claude to initialize..."
            Start-Sleep -Seconds 5
            
            # Check if the process is still running
            try {
                $runningProcess = Get-Process -Id $process.Id -ErrorAction Stop
                Write-Success "Claude Desktop is running and stable (PID: $($runningProcess.Id))"
                Write-Success "Process name: $($runningProcess.ProcessName)"
                if ($runningProcess.MainWindowTitle -and $VerboseOutput) {
                    Write-Info "Window title: $($runningProcess.MainWindowTitle)"
                }
                return $true
            } catch {
                Write-Warning "Process started but exited quickly (PID: $($process.Id))"
                Write-Info "This might indicate a configuration issue"
                return $false
            }
        } else {
            Write-Error "Failed to start process - Start-Process returned null"
            return $false
        }
    } catch {
        Write-Error "Exception starting Claude Desktop: $($_.Exception.Message)"
        if ($VerboseOutput) { Write-Info "Full exception: $($_.Exception)" }
        return $false
    }
}
function Restart-Claude {
    Write-Info "Attempting to restart Claude Desktop..."
    
    # Step 1: Stop Claude
    $stopSuccess = Stop-ClaudeProcess
    
    if (-not $stopSuccess) {
        Write-Warning "Failed to stop Claude completely, but continuing with startup attempt"
    }
    
    # Step 2: Start Claude
    $startSuccess = Start-ClaudeProcess
    
    # Step 3: Final verification
    Write-Info "Verifying Claude restart..."
    Start-Sleep -Seconds 2
    $finalProcesses = Find-ClaudeProcess
    
    if ($startSuccess -and $finalProcesses.Count -gt 0) {
        Write-Success "Claude Desktop restart: SUCCESSFUL"
        Write-Info "Claude Desktop is now running with updated configuration"
        return $true
    } else {
        Write-Warning "Claude Desktop restart: PARTIAL or FAILED"
        Write-Info "You may need to start Claude Desktop manually"
        Write-Info "The configuration has been updated and will work when Claude starts"
        return $false
    }
}
function Update-ClaudeConfig {
    Write-Info "Updating Claude Desktop configuration..."
    $configPath = "$env:APPDATA\Claude\claude_desktop_config.json"
    Write-Info "Config location: $configPath"
    # No backup needed - direct config update
    $configDir = Split-Path $configPath -Parent
    if (!(Test-Path $configDir)) {
        New-Item -ItemType Directory -Path $configDir -Force | Out-Null
        Write-Info "Created config directory"
    }
    # Read existing config or create new
    $config = @{}
    if (Test-Path $configPath) {
        try {
            # Read as JSON object first, then convert to hashtable if needed
            $jsonContent = Get-Content $configPath -Raw | ConvertFrom-Json
            
            # Convert PSCustomObject to hashtable for easier manipulation
            $config = @{}
            foreach ($property in $jsonContent.PSObject.Properties) {
                if ($property.Name -eq "mcpServers" -and $property.Value) {
                    # Preserve existing MCP servers
                    $config.mcpServers = @{}
                    foreach ($serverProperty in $property.Value.PSObject.Properties) {
                        $serverConfig = @{
                            command = $serverProperty.Value.command
                        }
                        if ($serverProperty.Value.args) {
                            $serverConfig.args = @($serverProperty.Value.args)
                        }
                        if ($serverProperty.Value.env) {
                            $serverConfig.env = @{}
                            foreach ($envProperty in $serverProperty.Value.env.PSObject.Properties) {
                                $serverConfig.env[$envProperty.Name] = $envProperty.Value
                            }
                        }
                        $config.mcpServers[$serverProperty.Name] = $serverConfig
                    }
                    Write-Info "Preserved $($config.mcpServers.Count) existing MCP server(s)"
                } else {
                    $config[$property.Name] = $property.Value
                }
            }
        } catch {
            Write-Warning "Could not parse existing config, creating new one"
            Write-Warning "Error: $($_.Exception.Message)"
            $config = @{}
        }
    } else {
        Write-Info "Creating new Claude configuration"
    }
    # Ensure mcpServers section exists
    if (!$config.mcpServers) {
        $config.mcpServers = @{}
        Write-Info "Created new mcpServers section"
    }
    # Check if our server already exists
    if ($config.mcpServers.ContainsKey("desktop-commander")) {
        Write-Info "Updating existing Desktop Commander configuration"
    } else {
        Write-Info "Adding new Desktop Commander configuration"
    }
    # Convert PowerShell array to proper format for JSON
    $argsArray = @()
    foreach ($arg in $script:DockerArgs) {
        $argsArray += $arg
    }
    # Add/update our server configuration (this preserves all other servers)
    $config.mcpServers["desktop-commander"] = @{
        command = "docker"
        args = $argsArray
    }
    # Save configuration
    try {
        $jsonConfig = $config | ConvertTo-Json -Depth 10
        [System.IO.File]::WriteAllText($configPath, $jsonConfig, [System.Text.UTF8Encoding]::new($false))
        Write-Success "Claude configuration updated successfully"
        Write-Info "Server 'desktop-commander' added to MCP servers"
        Write-Info "Total MCP servers configured: $($config.mcpServers.Count)"
        
        # List all configured servers
        if ($config.mcpServers.Count -gt 1) {
            Write-Host ""
            Write-Info "All configured MCP servers:"
            foreach ($serverName in $config.mcpServers.Keys) {
                if ($serverName -eq "desktop-commander") {
                    Write-Info "  * $serverName (Desktop Commander) - UPDATED"
                } else {
                    Write-Info "  * $serverName (preserved)"
                }
            }
        }
        
        # Show what folders are mounted for our server
        if ($script:Folders.Count -gt 0) {
            Write-Host ""
            Write-Info "Folders accessible to Desktop Commander:"
            foreach ($folder in $script:Folders) {
                $folderName = Split-Path $folder -Leaf
                Write-Info "  Folder: $folder"
                Write-Info "    -> /home/$folderName (home-style path)"
                
                # Show Windows-style path too
                $windowsPath = $folder.Replace('\', '/')
                if ($windowsPath -match '^([A-Za-z]):(.*)') {
                    $windowsStylePath = "/$($matches[1].ToLower())$($matches[2])"
                    Write-Info "    -> $windowsStylePath (windows-style path)"
                }
            }
        } else {
            Write-Warning "No folders mounted - limited file access"
        }
    } catch {
        Write-Error "Failed to save Claude configuration"
        Write-Error "Error: $($_.Exception.Message)"
        if (Test-Path "$configPath.backup-*") {
            Write-Info "You can restore from backup if needed"
        }
        exit 1
    }
}
function Show-Status {
    Write-Header
    Write-Info "Checking installation status..."
    Write-Host ""
    try {
        $null = docker info 2>$null
        if ($LASTEXITCODE -eq 0) {
            Write-Success "Docker daemon: Running"
        } else {
            Write-Warning "Docker daemon: Not running"
        }
    } catch {
        Write-Warning "Docker: Not available"
    }
    try {
        $null = docker image inspect desktop-commander:latest 2>$null
        if ($LASTEXITCODE -eq 0) {
            Write-Success "Docker image: Available"
        } else {
            Write-Warning "Docker image: Missing"
        }
    } catch {
        Write-Warning "Docker image: Cannot check"
    }
    $volumes = @("dc-system", "dc-home", "dc-workspace", "dc-packages")
    $volumesFound = 0
    Write-Host ""
    Write-Info "Persistent Volumes Status:"
    foreach ($volume in $volumes) {
        try {
            $null = docker volume inspect $volume 2>$null
            if ($LASTEXITCODE -eq 0) {
                $volumesFound++
                Write-Success "  OK: $volume"
            } else {
                Write-Warning "  MISSING: $volume"
            }
        } catch {
            Write-Warning "  UNKNOWN: $volume (cannot check)"
        }
    }
    $configPath = "$env:APPDATA\Claude\claude_desktop_config.json"
    if (Test-Path $configPath) {
        try {
            $config = Get-Content $configPath | ConvertFrom-Json
            if ($config.mcpServers."desktop-commander") {
                Write-Success "Claude config: Desktop Commander configured"
            } else {
                Write-Warning "Claude config: Missing Desktop Commander server"
            }
        } catch {
            Write-Warning "Claude config: Cannot parse"
        }
    } else {
        Write-Warning "Claude config: File not found"
    }
    Write-Host ""
    Write-Host "Status Summary:" -ForegroundColor Yellow
    Write-Host "  Essential volumes: $volumesFound/4 found"
    Write-Host "  Container mode: Auto-remove (fresh containers)"
    Write-Host "  Persistence: Data stored in volumes"
    Write-Host ""
    if ($volumesFound -eq 4) {
        Write-Success "Ready to use with Claude!"
        Write-Info "Each command creates a fresh container that uses your persistent volumes."
    } elseif ($volumesFound -gt 0) {
        Write-Warning "Some volumes missing - may need to reinstall"
        Write-Info "Run reset and reinstall to fix this"
    } else {
        Write-Error "No volumes found - please run full installation"
        Write-Info "Run: .\install-docker-clean.ps1"
    }
}function Reset-Installation {
    Write-Header
    Write-Warning "This will remove ALL persistent container data!"
    Write-Info "This includes:"
    Write-Info "  - All installed packages and software"
    Write-Info "  - All user configurations and settings"
    Write-Info "  - All development projects in /workspace"
    Write-Info "  - All package caches and databases"
    Write-Host ""
    Write-Info "Your mounted folders will NOT be affected."
    Write-Host ""
    $confirm = Read-Host "Are you sure you want to reset everything? [y/N]"
    if ($confirm -match "^[yY]") {
        Write-Info "Cleaning up containers and volumes..."
        try {
            $containers = docker ps -q --filter "ancestor=mcp/desktop-commander:latest" 2>$null
            if ($containers -and $LASTEXITCODE -eq 0) {
                docker stop $containers 2>$null | Out-Null
                docker rm $containers 2>$null | Out-Null
                Write-Info "Stopped running containers"
            }
        } catch {
            # Ignore errors here
        }
        Write-Info "Removing persistent volumes..."
        $volumes = @("dc-system", "dc-home", "dc-workspace", "dc-packages")
        $removedCount = 0
        foreach ($volume in $volumes) {
            try {
                docker volume rm $volume 2>$null | Out-Null
                if ($LASTEXITCODE -eq 0) {
                    Write-Success "Removed volume: $volume"
                    $removedCount++
                } else {
                    Write-Warning "Volume $volume is still in use or doesn't exist"
                }
            } catch {
                Write-Warning "Error removing volume: $volume"
            }
        }
        Write-Host ""
        Write-Success "Persistent data reset complete!"
        if ($removedCount -gt 0) {
            Write-Success "Successfully removed $removedCount volume(s)"
        }
        Write-Host ""
        Write-Info "To reinstall after reset:"
        Write-Info "Run: .\install-docker-clean.ps1"
    } else {
        Write-Info "Reset cancelled"
    }
}
function Show-Help {
    Write-Host "Desktop Commander Docker Installation (Enhanced)" -ForegroundColor Blue
    Write-Host ""
    Write-Host "Usage:"
    Write-Host "  .\install-docker-clean.ps1                 - Interactive installation with folder selection"
    Write-Host "  .\install-docker-clean.ps1 -Status         - Check installation status"
    Write-Host "  .\install-docker-clean.ps1 -Reset          - Reset all data"
    Write-Host "  .\install-docker-clean.ps1 -VerboseOutput  - Show detailed output"
    Write-Host "  .\install-docker-clean.ps1 -Help           - Show this help"
    Write-Host ""
    Write-Host "Features:"
    Write-Host "  - Interactive folder selection (like Mac version)"
    Write-Host "  - Custom folder mounting outside home directory"
    Write-Host "  - Persistent development environment"
    Write-Host "  - Enhanced configuration options"
    Write-Host ""
    Write-Host "Troubleshooting:"
    Write-Host "If you broke the Docker container or need a fresh start:"
    Write-Host "  .\install-docker-clean.ps1 -Reset"
    Write-Host "  .\install-docker-clean.ps1"
    Write-Host ""
    Write-Host "This will completely reset your persistent environment and reinstall everything fresh."
}
function Start-Installation {
    Write-Header
    if ($Help) {
        Show-Help
        return
    }
    if ($Status) {
        Show-Status
        return
    }
    if ($Reset) {
        Reset-Installation
        return
    }
    Test-Docker
    Get-DockerImage
    Ask-ForFolders
    Initialize-Volumes
    Build-DockerArgs
    Update-ClaudeConfig
    Restart-Claude
    Write-Host ""
    Write-Success "Installation successfully completed! Thank you for using Desktop Commander!"
    Write-Host ""
    Write-Info "How it works:"
    Write-Info "- Desktop Commander runs in isolated containers"
    Write-Info "- Your development tools and configs persist between uses"
    Write-Info "- Each command creates a fresh, clean container"
    
    if ($script:Folders.Count -gt 0) {
        Write-Host ""
        Write-Info "Your accessible folders (dual mount paths):"
        foreach ($folder in $script:Folders) {
            $folderName = Split-Path $folder -Leaf
            Write-Info "  Folder: $folder"
            Write-Info "    -> /home/$folderName (home-style)"
            
            # Show Windows-style path too
            $windowsPath = $folder.Replace('\', '/')
            if ($windowsPath -match '^([A-Za-z]):(.*)') {
                $windowsStylePath = "/$($matches[1].ToLower())$($matches[2])"
                Write-Info "    -> $windowsStylePath (windows-style)"
            }
        }
        Write-Host ""
        Write-Info "Path Translation Examples:"
        Write-Info "  Windows: C:\Users\wonde\projects\file.txt"
        Write-Info "  Docker:  /c/users/wonde/projects/file.txt  (windows-style)"
        Write-Info "  Docker:  /home/wonde/projects/file.txt     (home-style)"
    }
    
    Write-Host ""
    Write-Info "To refresh/reset your persistent environment:"
    Write-Info "- Run: .\install-docker-clean.ps1 -Reset"
    Write-Info "- This removes all installed packages and resets everything"
    Write-Host ""
    Write-Info "If you broke the Docker container or need a fresh start:"
    Write-Info "- Run: .\install-docker-clean.ps1 -Reset"
    Write-Info "- Then: .\install-docker-clean.ps1"
    Write-Info "- This will reset everything and reinstall from scratch"
    Write-Host ""
    Write-Info "Claude Desktop has been automatically restarted (if possible)"
    Write-Success "Desktop Commander is available as 'desktop-commander' in Claude"
    
    Write-Host ""
    Write-Info "Next steps: Install anything you want - it will persist!"
    Write-Info "- Global packages: npm install -g typescript"  
    Write-Info "- User configs: git config, SSH keys, .bashrc"
    Write-Host ""
    Write-Info "Need help or have feedback? Happy to jump on a quick call:"
    Write-Info "   https://calendar.app.google/SHMNZN5MJznJWC5A7"
    Write-Host ""
    Write-Info "Join our community: https://discord.com/invite/kQ27sNnZr7"
}
# Run installation
Start-Installation