Private/Search-WingetApps.ps1

function Search-WingetApps {
    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param([string]$SearchTerm)

    Write-Verbose "Searching for apps matching: $SearchTerm"
    
    # Run winget search and capture output
    $searchResults = winget search $SearchTerm --accept-source-agreements | Out-String
    
    # Parse the results (skip header lines)
    $lines = $searchResults -split "`n" | Where-Object { $_.Trim() -ne "" }
    $apps = @()
    
    # Find the separator line (dashes) to know where data starts; handle varying winget formats
    $dataStartIndex = 0
    for ($i = 0; $i -lt $lines.Count; $i++) {
        if ($lines[$i] -match "^[-\s]+$" -and $lines[$i] -match "-{2,}") {
            $dataStartIndex = $i + 1
            break
        }
    }
    
    # Parse each app line
    for ($i = $dataStartIndex; $i -lt $lines.Count; $i++) {
        $line = $lines[$i].Trim()
        if ($line -and
            $line -notmatch "^\d+\s+matches?\s+found" -and
            $line -notmatch "^More\s+than" -and
            $line -notmatch "^Name\s+Id\s+" ) {   # skip header row if separator detection missed it
            # Split by multiple spaces to separate columns
            $parts = $line -split '\s{2,}'
            if ($parts.Count -ge 3) {
                $apps += [PSCustomObject]@{
                    Name    = $parts[0].Trim()
                    Id      = $parts[1].Trim()
                    Version = if ($parts.Count -gt 2) { $parts[2].Trim() } else { "" }
                    Source  = if ($parts.Count -gt 3) { $parts[$parts.Count - 1].Trim() } else { "winget" }
                }
            }
        }
    }
    
    return $apps
}