public/functions/Invoke-PowerStubCommand.ps1

<#
.SYNOPSIS
  Executes a stubbed element.
 
.DESCRIPTION
 
.LINK
 
.PARAMETER
 
.INPUTS
None. You cannot pipe objects to Invoke-Authenticate.
 
.OUTPUTS
 
.EXAMPLES
 
#>


function Invoke-PowerStubCommand {
    [cmdletbinding()]
    param(
        [parameter(Position = 0)] [string] $stub,
        [parameter(Position = 1)] [string] $command,
        # ValueFromRemainingArguments captures any positional arguments after stub and command
        [parameter(DontShow = $true, ValueFromRemainingArguments = $true)] [object[]] $RemainingArgs
    )

    DynamicParam {
        # Get stub and command from PSBoundParameters (not variables - they don't exist yet during DynamicParam)
        $stubValue = $PSBoundParameters['Stub']
        $commandValue = $PSBoundParameters['Command']

        # Only build dynamic params if both stub and command are provided
        if ($stubValue -and $commandValue) {
            $RuntimeParamDic = Get-PowerStubCommandDynamicParams $stubValue $commandValue
            return $RuntimeParamDic
        }

        # Return empty dictionary if we don't have both values yet
        return New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
    }

    begin {
        Write-Debug "Invoke-PowerStubCommand Begin"
    }

    process {
        Write-Debug "Invoke-PowerStubCommand Process"
    }

    end {
        Write-Debug "Invoke-PowerStubCommand Process"

        if (!$stub) {
            Show-PowerStubOverview
            return
        }

        # Virtual verb handling - these are reserved commands that don't map to script files
        $virtualVerbs = @('search', 'help', 'update')
        if ($virtualVerbs -contains $stub) {
            switch ($stub) {
                'search' {
                    if ($command) {
                        return Search-PowerStubCommands $command
                    }
                    else {
                        throw "Usage: pstb search <query>"
                    }
                }
                'help' {
                    if ($command -and $RemainingArgs -and $RemainingArgs.Count -gt 0) {
                        # pstb help <stub> <command>
                        return Get-PowerStubCommandHelp -Stub $command -Command $RemainingArgs[0]
                    }
                    elseif ($command) {
                        throw "Usage: pstb help <stub> <command>"
                    }
                    else {
                        throw "Usage: pstb help <stub> <command>"
                    }
                }
                'update' {
                    # Update git repos for stubs
                    # Usage: pstb update [stub] [--check]
                    # --check: Only show status (fetch from remote) without pulling
                    if (-not $Script:GitEnabled) {
                        throw "Git integration is disabled. Enable it with: Set-PowerStubConfigurationKey 'GitEnabled' `$true"
                    }
                    if (-not $Script:GitAvailable) {
                        throw "Git is not available on this system."
                    }

                    # Parse arguments for --check flag and stub name
                    $checkOnly = $false
                    $targetStub = $null
                    $allArgs = @()
                    if ($command) { $allArgs += $command }
                    if ($RemainingArgs) { $allArgs += $RemainingArgs }

                    foreach ($arg in $allArgs) {
                        if ($arg -eq '--check' -or $arg -eq '-Check') {
                            $checkOnly = $true
                        }
                        elseif (-not $arg.StartsWith('-')) {
                            $targetStub = $arg
                        }
                    }

                    $stubs = Get-PowerStubConfigurationKey 'Stubs'
                    $processedRepos = @{}

                    if ($targetStub) {
                        # Process specific stub
                        if (-not ($stubs.Keys -contains $targetStub)) {
                            throw "Stub '$targetStub' not found."
                        }
                        $stubConfig = $stubs[$targetStub]
                        $stubPath = Get-PowerStubPath -StubConfig $stubConfig
                        $gitInfo = Get-PowerStubGitInfo -Path $stubPath -Fetch:$checkOnly
                        if (-not $gitInfo.IsRepo) {
                            throw "Stub '$targetStub' is not in a Git repository."
                        }

                        if ($checkOnly) {
                            # Show status only
                            if ($gitInfo.BehindCount -gt 0) {
                                Write-Host "Stub '$targetStub' is $($gitInfo.BehindCount) commit(s) behind." -ForegroundColor Yellow
                            }
                            elseif ($gitInfo.AheadCount -gt 0) {
                                Write-Host "Stub '$targetStub' is $($gitInfo.AheadCount) commit(s) ahead." -ForegroundColor Cyan
                            }
                            else {
                                Write-Host "Stub '$targetStub' is up to date." -ForegroundColor Green
                            }
                        }
                        else {
                            # Do actual update
                            Write-Host "Updating stub '$targetStub'..." -ForegroundColor Cyan
                            $result = Update-PowerStubGitRepo -Path $stubPath
                            if ($result.Success) {
                                Write-Host " $($result.Message)" -ForegroundColor Green
                            }
                            else {
                                Write-Host " $($result.Message)" -ForegroundColor Red
                            }
                        }
                    }
                    else {
                        # Process all stubs
                        $behindCount = 0
                        foreach ($stubName in $stubs.Keys) {
                            $stubConfig = $stubs[$stubName]
                            $stubPath = Get-PowerStubPath -StubConfig $stubConfig
                            $gitInfo = Get-PowerStubGitInfo -Path $stubPath -Fetch:$checkOnly
                            if ($gitInfo.IsRepo -and $gitInfo.RepoRoot -and -not $processedRepos.ContainsKey($gitInfo.RepoRoot)) {
                                if ($checkOnly) {
                                    # Show status only
                                    if ($gitInfo.BehindCount -gt 0) {
                                        Write-Host "Stub '$stubName' is $($gitInfo.BehindCount) commit(s) behind." -ForegroundColor Yellow
                                        $behindCount++
                                    }
                                    elseif ($gitInfo.AheadCount -gt 0) {
                                        Write-Host "Stub '$stubName' is $($gitInfo.AheadCount) commit(s) ahead." -ForegroundColor Cyan
                                    }
                                    else {
                                        Write-Host "Stub '$stubName' is up to date." -ForegroundColor Green
                                    }
                                }
                                else {
                                    # Do actual update
                                    Write-Host "Updating stub '$stubName' ($($gitInfo.RepoRoot))..." -ForegroundColor Cyan
                                    $result = Update-PowerStubGitRepo -Path $stubPath
                                    if ($result.Success) {
                                        Write-Host " $($result.Message)" -ForegroundColor Green
                                    }
                                    else {
                                        Write-Host " $($result.Message)" -ForegroundColor Red
                                    }
                                }
                                $processedRepos[$gitInfo.RepoRoot] = $true
                            }
                        }
                        if ($processedRepos.Count -eq 0) {
                            Write-Host "No stubs with Git repositories found." -ForegroundColor Yellow
                        }
                        elseif ($checkOnly) {
                            if ($behindCount -gt 0) {
                                Write-Host "`n$behindCount stub(s) have updates available. Run 'pstb update' to pull changes." -ForegroundColor Yellow
                            }
                            else {
                                Write-Host "`nAll $($processedRepos.Count) stub(s) are up to date." -ForegroundColor Green
                            }
                        }
                        else {
                            Write-Host "`nUpdated $($processedRepos.Count) repository(ies)." -ForegroundColor Cyan
                        }
                    }
                    return
                }
            }
        }

        if (!$command) {
            Show-PowerStubCommands $stub
            return
        }

        # Check if stub is registered first
        $stubs = Get-PowerStubConfigurationKey 'Stubs'
        if (-not $stubs -or -not $stubs.ContainsKey($stub)) {
            $registeredStubs = if ($stubs) { ($stubs.Keys -join ', ') } else { '(none)' }
            Throw "Stub '$stub' is not registered. Registered stubs: $registeredStubs`n`nTo register: New-PowerStub -Name '$stub' -Path '<path-to-stub-folder>'"
        }

        $commandObj = Get-PowerStubCommand $stub $command
        if (!$commandObj) {
            $stubConfig = $stubs[$stub]
            $stubPath = Get-PowerStubPath -StubConfig $stubConfig
            Throw "Command '$command' not found in stub '$stub'.`n`nStub path: $stubPath`nExpected: $stubPath\Commands\$command.ps1 or $stubPath\Commands\$command\$command.ps1"
        }

        $line = $myinvocation.line
        Write-Debug "line: $line"
        Write-Debug "stub: $stub"
        Write-Debug "command: $command"

        $srch = "$stub $command"
        $i = $line.IndexOf($srch)
        $cmdArgs = $line.Substring($i + $srch.Length).Trim()

        $cmd = $commandObj.Path

        if ($cmdArgs) {
            Write-Host "Invoking $cmd with arguments: $cmdArgs"
            invoke-CheckedCommandWithParams $cmd $null $cmdArgs $true
        }
        elseif ($RemainingArgs -and $RemainingArgs.Count -gt 0) {
            Write-Host "Invoking $cmd with positional arguments"
            invoke-CheckedCommandWithParams $cmd $RemainingArgs $null $true
        }
        else {
            Write-Host "Invoking $cmd"
            invoke-CheckedCommandWithParams $cmd $null $null $true
        }
    }
}