public/Select-WayforgeHarness.ps1

function Select-WayforgeHarness {
    <#
    .SYNOPSIS
        Interactive multiselect of harnesses, pre-checked from detection.
 
    .DESCRIPTION
        Shows the known harnesses with installed/configured tags, pre-selecting
        those that are installed or already configured. The user toggles entries
        by number and confirms; the chosen harness names are returned (suitable
        for Sync-WayforgeHarness -Harness or New-WayforgeProject -Harness).
 
    .PARAMETER ProjectPath
        The project to inspect. Defaults to the current directory.
 
    .PARAMETER HomePath
        The home directory to inspect. Defaults to the user profile.
 
    .EXAMPLE
        Sync-WayforgeHarness -Harness (Select-WayforgeHarness)
 
        Prompts for harnesses (pre-checked from what's installed) and syncs them.
    #>

    [CmdletBinding()]
    [OutputType([string[]])]
    param(
        [string] $ProjectPath = (Get-Location).Path,
        [string] $HomePath = $(if ($env:USERPROFILE) { $env:USERPROFILE } else { $HOME })
    )

    $info = @(Get-WayforgeHarness -ProjectPath $ProjectPath -HomePath $HomePath)
    $selected = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
    foreach ($h in $info) { if ($h.Installed -or $h.Configured) { [void]$selected.Add($h.Name) } }

    while ($true) {
        Write-Host 'Select harnesses to enforce (pre-checked = installed or configured):'
        for ($i = 0; $i -lt $info.Count; $i++) {
            $h = $info[$i]
            $mark = if ($selected.Contains($h.Name)) { '[x]' } else { '[ ]' }
            $tags = @()
            if ($h.Installed) { $tags += 'installed' }
            if ($h.Configured) { $tags += 'configured' }
            $suffix = if ($tags) { " ($($tags -join ', '))" } else { '' }
            Write-Host (' {0,2}. {1} {2}{3}' -f ($i + 1), $mark, $h.Name, $suffix)
        }

        $answer = Read-Host "Toggle numbers (comma-separated), 'a' for all, 'n' for none, or Enter to confirm"
        if ([string]::IsNullOrWhiteSpace($answer)) { break }

        switch -Regex ($answer.Trim()) {
            '^a$' { foreach ($h in $info) { [void]$selected.Add($h.Name) }; continue }
            '^n$' { $selected.Clear(); continue }
            default {
                foreach ($token in ($answer -split '[,\s]+' | Where-Object { $_ })) {
                    $index = 0
                    if ([int]::TryParse($token, [ref]$index) -and $index -ge 1 -and $index -le $info.Count) {
                        $name = $info[$index - 1].Name
                        if ($selected.Contains($name)) { [void]$selected.Remove($name) } else { [void]$selected.Add($name) }
                    }
                }
            }
        }
    }

    return @($info | Where-Object { $selected.Contains($_.Name) } | ForEach-Object { $_.Name })
}