AnyGet.psm1

# AnyGet - Universal Package Manager

# Define supported sources
$script:SupportedSources = @{
    'winget' = @{
        'Name'             = 'Windows Package Manager'
        'Command'          = 'winget'
        'InstallCommand'   = 'winget install {0} --accept-package-agreements --accept-source-agreements'
        'UninstallCommand' = 'winget uninstall {0}'
        'Platforms'        = @('Windows')
        'TestCommand'      = 'winget --version'
    }
    'scoop'  = @{
        'Name'             = 'Scoop'
        'Command'          = 'scoop'
        'InstallCommand'   = 'scoop install {0}'
        'UninstallCommand' = 'scoop uninstall {0}'
        'Platforms'        = @('Windows', 'Linux', 'macOS')
        'TestCommand'      = 'scoop --version'
    }
    'choco'  = @{
        'Name'             = 'Chocolatey'
        'Command'          = 'choco'
        'InstallCommand'   = 'choco install {0} -y'
        'UninstallCommand' = 'choco uninstall {0} -y'
        'Platforms'        = @('Windows')
        'TestCommand'      = 'choco --version'
    }
    'npm'    = @{
        'Name'             = 'Node Package Manager'
        'Command'          = 'npm'
        'InstallCommand'   = 'npm install -g {0}'
        'UninstallCommand' = 'npm uninstall -g {0}'
        'Platforms'        = @('Windows', 'Linux', 'macOS')
        'TestCommand'      = 'npm --version'
    }
}

# Helper function to get current platform
function Get-CurrentPlatform {
    if ($IsWindows -or $PSVersionTable.PSVersion.Major -lt 6) {
        return 'Windows'
    }
    elseif ($IsLinux) {
        return 'Linux'
    }
    elseif ($IsMacOS) {
        return 'macOS'
    }
    else {
        return 'Unknown'
    }
}

# Helper function to test if a source is available
function Test-SourceAvailability {
    param([string]$Source)
    
    if (-not $script:SupportedSources.ContainsKey($Source)) {
        return $false
    }
    
    $sourceInfo = $script:SupportedSources[$Source]
    $currentPlatform = Get-CurrentPlatform
    
    if ($currentPlatform -notin $sourceInfo.Platforms) {
        return $false
    }
    
    try {
        $testCommand = $sourceInfo.TestCommand
        $null = Invoke-Expression $testCommand 2>$null
        return $LASTEXITCODE -eq 0
    }
    catch {
        return $false
    }
}

# AnyGet-Sources function
function AnyGet-Sources {
    <#
    .SYNOPSIS
    Get information about available package manager sources.
     
    .DESCRIPTION
    This function returns information about all supported package managers and their availability on the current system.
     
    .PARAMETER AvailableOnly
    Show only sources that are currently available on the system.
     
    .EXAMPLE
    Get-AnyGetSources
    Shows all supported package managers and their availability.
     
    .EXAMPLE
    Get-AnyGetSources -AvailableOnly
    Shows only package managers that are currently available.
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [switch]$AvailableOnly
    )
    
    $currentPlatform = Get-CurrentPlatform
    $results = @()
    
    foreach ($source in $script:SupportedSources.Keys) {
        $sourceInfo = $script:SupportedSources[$source]
        $isAvailable = Test-SourceAvailability -Source $source
        $supportsCurrentPlatform = $currentPlatform -in $sourceInfo.Platforms
        
        if ($AvailableOnly -and -not $isAvailable) {
            continue
        }
        
        $result = [PSCustomObject]@{
            Source    = $source
            Name      = $sourceInfo.Name
            Command   = $sourceInfo.Command
            Available = $isAvailable
            Platforms = $sourceInfo.Platforms -join ', '
            Status    = if ($isAvailable) { 'Available' } elseif ($supportsCurrentPlatform) { 'Not Installed' } else { 'Not Supported' }
        }
        
        $results += $result
    }
    
    if ($results.Count -gt 0) {
        Write-Host "`nPackage Manager Sources:" -ForegroundColor Cyan
        Write-Host "=" * 50 -ForegroundColor Cyan
        
        $results | Format-Table -AutoSize -Property @(
            @{Name = 'Source'; Expression = { $_.Source }; Width = 10 },
            @{Name = 'Name'; Expression = { $_.Name }; Width = 20 },
            @{Name = 'Status'; Expression = { $_.Status }; Width = 15 },
            @{Name = 'Platforms'; Expression = { $_.Platforms }; Width = 20 }
        )
        
        Write-Host "`nCurrent Platform: $currentPlatform" -ForegroundColor Yellow
        Write-Host "Available Sources: $($results | Where-Object { $_.Available } | Measure-Object).Count" -ForegroundColor Green
    }
    else {
        Write-Warning "No package manager sources found matching the specified criteria."
    }
    
    return $results
}

# AnyGet-Install function
function AnyGet-Install {
    <#
    .SYNOPSIS
    Install a package using any available package manager.
     
    .DESCRIPTION
    This function searches for and installs a package using the first available package manager that finds the package.
     
    .PARAMETER Name
    The name of the package to install.
     
    .PARAMETER Source
    The specific package manager to use. If not specified, will try all available sources.
     
    .PARAMETER Force
    Force installation without prompts.
     
    .EXAMPLE
    Install-AnyGet -Name "git"
    Installs git using the first available package manager.
     
    .EXAMPLE
    Install-AnyGet -Name "nodejs" -Source "winget"
    Installs nodejs specifically using winget.
    #>

    
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$Name,
        
        [Parameter(Mandatory = $false)]
        [ValidateSet('any', 'winget', 'scoop', 'choco', 'npm')]
        [string]$Source = 'any',
        
        [Parameter(Mandatory = $false)]
        [switch]$Force
    )
    
    Write-Verbose "Installing package '$Name' using source '$Source'"
    
    # Get available sources
    $availableSources = @()
    foreach ($source in $script:SupportedSources.Keys) {
        if (Test-SourceAvailability -Source $source) {
            $availableSources += $source
        }
    }
    
    if ($availableSources.Count -eq 0) {
        Write-Error "No supported package managers are available on this system."
        return
    }
    
    # Determine which sources to try
    $sourcesToTry = @()
    if ($Source -eq 'any') {
        $sourcesToTry = $availableSources
    }
    else {
        if ($availableSources -contains $Source) {
            $sourcesToTry = @($Source)
        }
        else {
            Write-Error "Package manager '$Source' is not available on this system."
            return
        }
    }
    
    # Try each source until we find and install the package
    foreach ($sourceName in $sourcesToTry) {
        Write-Verbose "Trying to install '$Name' using $sourceName"
        
        $sourceInfo = $script:SupportedSources[$sourceName]
        $installCommand = $sourceInfo.InstallCommand -f $Name
        
        if ($Force) {
            switch ($sourceName) {
                'winget' { $installCommand += ' --force' }
                'choco' { $installCommand = $installCommand -replace ' -y', ' -y --force' }
            }
        }
        
        if ($PSCmdlet.ShouldProcess($Name, "Install using $sourceName")) {
            try {
                Write-Host "Installing '$Name' using $sourceName..." -ForegroundColor Green
                $result = Invoke-Expression $installCommand
                
                if ($LASTEXITCODE -eq 0) {
                    Write-Host "Successfully installed '$Name' using $sourceName" -ForegroundColor Green
                    return
                }
                else {
                    Write-Warning "Failed to install '$Name' using $sourceName (Exit code: $LASTEXITCODE)"
                }
            }
            catch {
                Write-Warning "Error installing '$Name' using $sourceName`: $($_.Message)"
            }
        }
    }
    
    Write-Error "Failed to install '$Name' using any available package manager."
}

# AnyGet-Uninstall function
function AnyGet-Uninstall {
    <#
    .SYNOPSIS
    Uninstall a package using any available package manager.
     
    .DESCRIPTION
    This function searches for and uninstalls a package using the first available package manager that finds the package.
     
    .PARAMETER Name
    The name of the package to uninstall.
     
    .PARAMETER Source
    The specific package manager to use. If not specified, will try all available sources.
     
    .PARAMETER Force
    Force uninstallation without prompts.
     
    .EXAMPLE
    Uninstall-AnyGet -Name "git"
    Uninstalls git using the first available package manager.
     
    .EXAMPLE
    Uninstall-AnyGet -Name "nodejs" -Source "winget"
    Uninstalls nodejs specifically using winget.
    #>

    
    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$Name,
        
        [Parameter(Mandatory = $false)]
        [ValidateSet('any', 'winget', 'scoop', 'choco', 'npm')]
        [string]$Source = 'any',
        
        [Parameter(Mandatory = $false)]
        [switch]$Force
    )
    
    Write-Verbose "Uninstalling package '$Name' using source '$Source'"
    
    # Get available sources
    $availableSources = @()
    foreach ($source in $script:SupportedSources.Keys) {
        if (Test-SourceAvailability -Source $source) {
            $availableSources += $source
        }
    }
    
    if ($availableSources.Count -eq 0) {
        Write-Error "No supported package managers are available on this system."
        return
    }
    
    # Determine which sources to try
    $sourcesToTry = @()
    if ($Source -eq 'any') {
        $sourcesToTry = $availableSources
    }
    else {
        if ($availableSources -contains $Source) {
            $sourcesToTry = @($Source)
        }
        else {
            Write-Error "Package manager '$Source' is not available on this system."
            return
        }
    }
    
    # Try each source until we find and uninstall the package
    foreach ($sourceName in $sourcesToTry) {
        Write-Verbose "Trying to uninstall '$Name' using $sourceName"
        
        $sourceInfo = $script:SupportedSources[$sourceName]
        $uninstallCommand = $sourceInfo.UninstallCommand -f $Name
        
        if ($Force) {
            switch ($sourceName) {
                'winget' { $uninstallCommand += ' --force' }
                'choco' { $uninstallCommand = $uninstallCommand -replace ' -y', ' -y --force' }
            }
        }
        
        if ($PSCmdlet.ShouldProcess($Name, "Uninstall using $sourceName")) {
            try {
                Write-Host "Uninstalling '$Name' using $sourceName..." -ForegroundColor Yellow
                $result = Invoke-Expression $uninstallCommand
                
                if ($LASTEXITCODE -eq 0) {
                    Write-Host "Successfully uninstalled '$Name' using $sourceName" -ForegroundColor Green
                    return
                }
                else {
                    Write-Warning "Failed to uninstall '$Name' using $sourceName (Exit code: $LASTEXITCODE)"
                }
            }
            catch {
                Write-Warning "Error uninstalling '$Name' using $sourceName`: $($_.Message)"
            }
        }
    }
    
    Write-Error "Failed to uninstall '$Name' using any available package manager."
}

# Main anyget function that handles subcommands
function anyget {
    <#
    .SYNOPSIS
    Universal package manager command-line interface.
     
    .DESCRIPTION
    Main command for AnyGet that handles subcommands like install, uninstall, and sources.
     
    .PARAMETER Command
    The subcommand to execute (install, uninstall, sources).
     
    .PARAMETER Name
    The name of the package (for install/uninstall commands).
     
    .PARAMETER Source
    The specific package manager to use.
     
    .PARAMETER Force
    Force operation without prompts.
     
    .EXAMPLE
    anyget install git
    Installs git using any available package manager.
     
    .EXAMPLE
    anyget uninstall nodejs -Source winget
    Uninstalls nodejs specifically using winget.
     
    .EXAMPLE
    anyget sources
    Shows available package managers.
    #>

    
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateSet('install', 'uninstall', 'sources')]
        [string]$Command,
        
        [Parameter(Mandatory = $false, Position = 1)]
        [string]$Name,
        
        [Parameter(Mandatory = $false)]
        [ValidateSet('any', 'winget', 'scoop', 'choco', 'npm')]
        [string]$Source = 'any',
        
        [Parameter(Mandatory = $false)]
        [switch]$Force
    )
    
    switch ($Command) {
        'install' {
            if (-not $Name) {
                Write-Error "Package name is required for install command. Usage: anyget install <package-name>"
                return
            }
            AnyGet-Install -Name $Name -Source $Source -Force:$Force
        }
        'uninstall' {
            if (-not $Name) {
                Write-Error "Package name is required for uninstall command. Usage: anyget uninstall <package-name>"
                return
            }
            AnyGet-Uninstall -Name $Name -Source $Source -Force:$Force
        }
        'sources' {
            AnyGet-Sources -AvailableOnly
        }
    }
}

# No aliases needed - use the main anyget function

# Export functions and aliases
Export-ModuleMember -Function @(
    'anyget',
    'AnyGet-Sources',
    'AnyGet-Install',
    'AnyGet-Uninstall'
)

# No aliases to export