src/functions/command-catalog.ps1
|
function Get-ScriptParameterInfo { <# .SYNOPSIS Extracts parameter metadata (name, type, switch, ValidateSet, default) from a scriptblock. #> [OutputType([pscustomobject])] param( [scriptblock]$Func, [int]$Skip = 0, $Exclude = @('$_context', '$_self'), [System.Collections.IDictionary]$Entry = $null ) if (!$Func -or !$Func.AST.ParamBlock -or !$Func.AST.ParamBlock.Parameters) { return @() } $scriptArgs = Get-ScriptArgs -func $Func -skip $Skip -exclude $Exclude -entry $Entry $defaultsByName = @{} $skipped = 0 foreach ($paramAst in $Func.AST.ParamBlock.Parameters) { $nameWithDollar = "$($paramAst.Name)" if ($nameWithDollar -in $Exclude) { continue } if ($skipped -lt $Skip) { $skipped++ continue } $paramName = $paramAst.Name.ToString().Trim('$') $defaultValue = $null if ($null -ne $paramAst.DefaultValue) { try { $defaultValue = $paramAst.DefaultValue.SafeGetValue() } catch { $defaultValue = $paramAst.DefaultValue.Extent.Text } } $defaultsByName[$paramName] = $defaultValue } $result = [System.Collections.Generic.List[object]]::new() foreach ($name in $scriptArgs.Keys) { $dynParam = $scriptArgs[$name] $validateSet = @() foreach ($attr in $dynParam.Attributes) { if ($attr -is [System.Management.Automation.ValidateSetAttribute]) { $validateSet = @($attr.ValidValues) break } } $isSwitch = $dynParam.ParameterType -eq [switch] $typeName = if ($dynParam.ParameterType -eq [string]) { 'string' } elseif ($dynParam.ParameterType -eq [int] -or $dynParam.ParameterType -eq [int32]) { 'int' } elseif ($dynParam.ParameterType -eq [bool]) { 'bool' } elseif ($isSwitch) { 'switch' } elseif ($dynParam.ParameterType.Namespace -eq 'System') { $dynParam.ParameterType.Name } else { $dynParam.ParameterType.FullName } $result.Add([pscustomobject]@{ Name = $name Type = $typeName IsSwitch = $isSwitch ValidateSet = $validateSet DefaultValue = $(if ($defaultsByName.ContainsKey($name)) { $defaultsByName[$name] } else { $null }) }) | Out-Null } return @($result) } function Get-ConfigMapCommandCatalog { <# .SYNOPSIS Returns a structured catalog of map commands and their parameters for agents and automation. .PARAMETER Map An imported config map (hashtable/ordered dictionary). .PARAMETER Path Optional entry name to describe a single command. Throws if not found. .PARAMETER Language Map language: build or conf. #> [OutputType([pscustomobject])] param( [Parameter(Mandatory = $true)] [ValidateScript({ $_ -is [System.Collections.IDictionary] })] $Map, [string]$Path, [ValidateSet('build', 'conf')]$Language = 'build' ) $scripts = Get-CompletionList $Map -language $Language $reservedKeys = (Get-MapLanguage $Language).reservedKeys $scriptItems = if ($Map -is [System.Collections.Specialized.OrderedDictionary]) { @($scripts.GetEnumerator()) } else { @($scripts.GetEnumerator() | Sort-Object Name) } if ($PSBoundParameters.ContainsKey('Path') -and $null -ne $Path -and $Path -ne '') { $match = $scriptItems | Where-Object { $_.Name -eq $Path } if (-not $match) { # Case-insensitive fallback $match = $scriptItems | Where-Object { $_.Name -ieq $Path } } if (-not $match) { throw "Entry '$Path' not found. Run 'qbuild !describe' to see all available commands." } $scriptItems = @($match) } foreach ($item in $scriptItems) { $name = $item.Name $script = $item.Value if ($name -in $reservedKeys) { continue } $isParent = Test-IsParentEntry $script -Language $Language -ReservedKeys $reservedKeys $description = '' if ($script -is [System.Collections.IDictionary] -and $script.description) { $description = [string]$script.description } $parameters = @() try { $entryCommand = Get-EntryCommand $script 'exec' } catch { $entryCommand = $null } if ($entryCommand -is [scriptblock]) { $entryContext = if ($script -is [System.Collections.IDictionary]) { $script } else { $null } $parameters = @(Get-ScriptParameterInfo -Func $entryCommand -Entry $entryContext) } [pscustomobject]@{ Name = $name Description = $description IsParent = [bool]$isParent Parameters = $parameters } } } |