completers/sbx_completer/sbx_completer.ps1

<#
.SYNOPSIS
Registers Docker Sandboxes (`sbx`) tab-completion through the installed `sbx` executable.

.DESCRIPTION
This script registers importer-safe native completers for `sbx` and `sbx.exe`.
The generated completion script (`sbx completion powershell`) is resolved lazily
at completion time, cached, and then invoked through the installed Docker
Sandboxes CLI's own PowerShell completer, so subcommands, flags and sandbox
names always match the installed `sbx` version.

Run once per session, or dot-source it from your PowerShell profile.
#>


Set-StrictMode -Version Latest

function Get-SbxCommandPath {
    $cachedPath = Get-Variable -Name SbxCommandPath -Scope Script -ErrorAction Ignore
    if ($null -ne $cachedPath) {
        return $cachedPath.Value
    }

    $sbxCommand = Get-Command -Name 'sbx', 'sbx.exe' -CommandType Application -ErrorAction Ignore | Select-Object -First 1
    $script:SbxCommandPath = if ($null -ne $sbxCommand) { $sbxCommand.Source } else { $null }
    $script:SbxCommandPath
}

function Get-SbxGeneratedCompletionScript {
    # A completion callback must stay silent: diagnostics go to the verbose stream only.
    $sbxCommandPath = Get-SbxCommandPath
    if ([string]::IsNullOrWhiteSpace($sbxCommandPath)) {
        Write-Verbose 'Docker Sandboxes CLI (sbx) was not found in PATH.'
        return $null
    }

    try {
        $completionScript = $null | & $sbxCommandPath completion powershell 2>$null | ForEach-Object { $_ -replace '\e\[[0-9;?]*[ -/]*[@-~]', '' } | Out-String

        if ([string]::IsNullOrWhiteSpace($completionScript)) {
            Write-Verbose 'sbx returned an empty completion script.'
            return $null
        }

        return $completionScript
    } catch {
        Write-Verbose ("Failed to load sbx completion: {0}" -f $_.Exception.Message)
        $null
    }
}

function Get-SbxCompletionInvoker {
    $cachedInvoker = Get-Variable -Name SbxCompletionInvoker -Scope Script -ErrorAction Ignore
    if ($null -ne $cachedInvoker) {
        return $cachedInvoker.Value
    }

    # A failed discovery is cached too, so a machine without sbx pays for it once per session.
    if (Get-Variable -Name SbxCompletionUnavailable -Scope Script -ErrorAction Ignore) {
        return $null
    }

    $completionScript = Get-SbxGeneratedCompletionScript
    if ([string]::IsNullOrWhiteSpace($completionScript)) {
        $script:SbxCompletionUnavailable = $true
        return $null
    }

    # The upstream script registers only the bare 'sbx' name; registration is owned by this file.
    $completionScript = $completionScript -replace (
        "(?m)^\s*Register-ArgumentCompleter\s+-CommandName\s+'sbx'\s+-ScriptBlock\s+\$\{__sbxCompleterBlock\}\s*\r?$"
    ), ''

    # With no suggestions and the default directive (file completion allowed, e.g. 'sbx cp <path>')
    # $Values is $null, and '$null | ForEach-Object' still runs once, so the block would build a
    # CompletionResult from a null name and leave an exception in $Error on every such Tab.
    $completionScript = $completionScript -replace (
        '(?m)^(\s*)\$Values \| ForEach-Object \{\s*$'
    ), '$1$$Values | Where-Object { $$null -ne $$_ } | ForEach-Object {'

    # cobra's block dereferences properties of an empty pipeline when there are no suggestions; run it
    # outside this script's strict mode so those accesses stay silent.
    $completionInvokerSource = @"
Set-StrictMode -Off
$completionScript

& `${__sbxCompleterBlock} @args
"@


    try {
        $script:SbxCompletionInvoker = [scriptblock]::Create($completionInvokerSource)
        return $script:SbxCompletionInvoker
    } catch {
        Write-Verbose ("Failed to prepare sbx completion: {0}" -f $_.Exception.Message)
        $script:SbxCompletionUnavailable = $true
        $null
    }
}

function Invoke-SbxCompletion {
    param(
        [string]$wordToComplete,
        [System.Management.Automation.Language.CommandAst]$commandAst,
        [int]$cursorPosition
    )

    $completionInvoker = Get-SbxCompletionInvoker
    if ($null -eq $completionInvoker) {
        return
    }

    # cobra's block lets the child process's 'Completion ended with directive' banner reach stderr;
    # redirecting the whole invocation keeps the console clean. Its "" sentinel
    # (ShellCompDirectiveNoFileComp) and empty output are passed through unchanged.
    try {
        @(& $completionInvoker $wordToComplete $commandAst $cursorPosition 2>$null)
    } catch {
        Write-Verbose ("sbx completion block failed: {0}" -f $_.Exception.Message)
    }
}

Register-ArgumentCompleter -Native -CommandName @('sbx', 'sbx.exe') -ScriptBlock {
    param($wordToComplete, $commandAst, $cursorPosition)

    Invoke-SbxCompletion -wordToComplete $wordToComplete -commandAst $commandAst -cursorPosition $cursorPosition
}