functions/private/Get-KlippyGcodeCommands.ps1

function Get-KlippyGcodeCommands {
    <#
    .SYNOPSIS
        Gets available G-code commands from a Klipper printer.

    .DESCRIPTION
        Fetches the list of available G-code commands and macros from Klipper.
        Used for tab completion in the interactive console.
        Results are cached per printer session for performance.

    .PARAMETER Printer
        The printer object.

    .PARAMETER Force
        Force refresh of cached commands.

    .OUTPUTS
        Array of command objects with Name and Help properties.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [PSCustomObject]$Printer,

        [Parameter()]
        [switch]$Force
    )

    # Use module-scoped cache
    if (-not $script:GcodeCommandCache) {
        $script:GcodeCommandCache = @{}
    }

    $cacheKey = $Printer.Id

    # Return cached if available and not forcing refresh
    if (-not $Force -and $script:GcodeCommandCache.ContainsKey($cacheKey)) {
        return $script:GcodeCommandCache[$cacheKey]
    }

    try {
        Write-Verbose "[$($Printer.PrinterName)] Fetching G-code commands..."

        # Get G-code help from Klipper
        $response = Invoke-KlippyJsonRpc -Printer $Printer -Method "printer/gcode/help" -NoNormalize

        $commands = [System.Collections.Generic.List[PSCustomObject]]::new()

        # Parse the help response - it's a hashtable of command -> description
        if ($response) {
            foreach ($key in $response.PSObject.Properties.Name) {
                # Skip internal properties
                if ($key.StartsWith('_')) { continue }

                $commands.Add([PSCustomObject]@{
                    Name        = $key.ToUpper()
                    Help        = $response.$key
                    CommandType = if ($key -match '^[GM]\d') { 'GCode' } else { 'Macro' }
                })
            }
        }

        # Sort commands alphabetically
        $sortedCommands = $commands | Sort-Object Name

        # Cache the results
        $script:GcodeCommandCache[$cacheKey] = $sortedCommands

        Write-Verbose "[$($Printer.PrinterName)] Found $($sortedCommands.Count) G-code commands"

        return $sortedCommands
    }
    catch {
        Write-Warning "Failed to fetch G-code commands: $_"

        # Return basic G-code commands as fallback
        return @(
            [PSCustomObject]@{ Name = 'G0'; Help = 'Linear move'; CommandType = 'GCode' }
            [PSCustomObject]@{ Name = 'G1'; Help = 'Linear move'; CommandType = 'GCode' }
            [PSCustomObject]@{ Name = 'G28'; Help = 'Home axes'; CommandType = 'GCode' }
            [PSCustomObject]@{ Name = 'G90'; Help = 'Absolute positioning'; CommandType = 'GCode' }
            [PSCustomObject]@{ Name = 'G91'; Help = 'Relative positioning'; CommandType = 'GCode' }
            [PSCustomObject]@{ Name = 'M104'; Help = 'Set extruder temperature'; CommandType = 'GCode' }
            [PSCustomObject]@{ Name = 'M109'; Help = 'Wait for extruder temperature'; CommandType = 'GCode' }
            [PSCustomObject]@{ Name = 'M140'; Help = 'Set bed temperature'; CommandType = 'GCode' }
            [PSCustomObject]@{ Name = 'M190'; Help = 'Wait for bed temperature'; CommandType = 'GCode' }
            [PSCustomObject]@{ Name = 'M106'; Help = 'Set fan speed'; CommandType = 'GCode' }
            [PSCustomObject]@{ Name = 'M107'; Help = 'Fan off'; CommandType = 'GCode' }
        )
    }
}