public/Invoke-Octa.ps1

function Invoke-Octa {
    <#
        .SYNOPSIS
        Interactive entry point: language menu (first run) -> banner + main menu -> dispatch,
        looping back to the menu after each action so the whole session (every category, the
        built-in tools, undo) happens from this one running terminal - the user never needs to
        re-invoke bin/octa.ps1 with a different flag. FR-018, FR-022, FR-023.

        ponytail: direct single-keypress shortcuts are guaranteed unique for items 1-9; with
        more than 9 categories, items 10+ are still always reachable via Up/Down + Enter (no
        functionality is lost), just without a guaranteed single-digit direct key. Upgrade path
        if this matters: a two-keystroke buffered input mode instead of raw ReadKey.
    #>

    [CmdletBinding()]
    param()

    $strings = Get-OctaStrings
    $categories = @(Get-OctaCategory)

    while ($true) {
        $items = @()
        for ($i = 0; $i -lt $categories.Count; $i++) {
            $items += [pscustomobject]@{ Key = ($i + 1).ToString(); Label = $categories[$i].DisplayName; Description = $categories[$i].Description }
        }
        $items += [pscustomobject]@{ Key = 'q'; Label = $strings.menuQuickClean; Description = '' }
        $items += [pscustomobject]@{ Key = 'u'; Label = $strings.menuUndo; Description = '' }
        $items += [pscustomobject]@{ Key = 'd'; Label = $strings.menuDashboard; Description = '' }
        $items += [pscustomobject]@{ Key = 's'; Label = $strings.menuStartupManager; Description = '' }
        $items += [pscustomobject]@{ Key = 't'; Label = $strings.menuTasks; Description = '' }
        $items += [pscustomobject]@{ Key = 'c'; Label = $strings.menuDiskCleanup; Description = '' }
        $items += [pscustomobject]@{ Key = 'r'; Label = $strings.menuUninstaller; Description = '' }
        $items += [pscustomobject]@{ Key = 'n'; Label = $strings.menuNetworkCleanup; Description = '' }
        $items += [pscustomobject]@{ Key = 'p'; Label = $strings.menuScheduler; Description = '' }
        $items += [pscustomobject]@{ Key = 'w'; Label = $strings.menuSoftwareUpdater; Description = '' }
        $items += [pscustomobject]@{ Key = 'y'; Label = $strings.menuHistory; Description = '' }
        $items += [pscustomobject]@{ Key = 'k'; Label = $strings.menuDiskUsage; Description = '' }
        $items += [pscustomobject]@{ Key = 'j'; Label = $strings.menuDiskHealth; Description = '' }
        $items += [pscustomobject]@{ Key = 'm'; Label = $strings.menuMalwareScan; Description = '' }
        $items += [pscustomobject]@{ Key = 'l'; Label = $strings.menuLanguage; Description = '' }
        $items += [pscustomobject]@{ Key = 'h'; Label = $strings.menuHelp; Description = '' }
        $items += [pscustomobject]@{ Key = 'x'; Label = $strings.menuExit; Description = '' }

        $choice = Show-OctaMenu -Items $items -Header $strings.mainMenuHeader -NavHint $strings.menuNavHint

        switch ($choice) {
            'q' { Invoke-OctaQuickClean -Apply | Format-List }
            'u' { Undo-OctaRun -RunId 'latest' | Format-List }
            'd' { Show-OctaDashboard | Out-Null }
            's' { Invoke-OctaStartupManager | Out-Null }
            't' { Invoke-OctaTaskBrowser | Out-Null }
            'c' { Invoke-OctaDiskCleanup | Out-Null }
            'r' { Invoke-OctaUninstaller | Out-Null }
            'n' { Invoke-OctaNetworkCleanup | Out-Null }
            'p' { Invoke-OctaScheduler | Out-Null }
            'w' { Invoke-OctaSoftwareUpdater | Out-Null }
            'y' { Show-OctaHistory | Out-Null }
            'k' { Show-OctaDiskUsage | Out-Null }
            'j' { Show-OctaDiskHealth | Out-Null }
            'm' { Invoke-OctaMalwareScan | Format-List }
            'l' {
                $culture = Show-OctaLanguageMenu
                Set-OctaLanguage -Culture $culture
                $strings = Get-OctaStrings
            }
            'h' { Write-Host 'https://github.com/jpiribe/octa (see README for full reference)' }
            'x' { return }
            default {
                $index = [int]$choice - 1
                if ($index -ge 0 -and $index -lt $categories.Count) {
                    Invoke-OctaCategory -CategoryId $categories[$index].Id -Apply | Format-List
                }
            }
        }

        if ($choice -ne 'x') {
            Write-Host ''
            Write-Host -NoNewline $strings.pressKeyToContinue
            [Console]::ReadKey($true) | Out-Null
        }
    }
}