core/public/Resolve-JaxCliCommand.ps1
|
function Resolve-JaxCliCommand { [CmdletBinding()] param ( [string] $Command, [string[]] $CommandArgs ) $tokens = @() if ($null -ne $Command) { $tokens += $Command } if ($null -ne $CommandArgs) { $tokens += $CommandArgs } if ($tokens.Count -eq 0) { return @{ Command = $null Args = @() } } $definitions = Get-JaxCliParameters $lookup = @{} foreach ($definition in $definitions) { $lookup[$definition.Name.ToLowerInvariant()] = $definition foreach ($alias in $definition.Aliases) { $lookup[$alias.ToLowerInvariant()] = $definition } } $commandIndex = $null $index = 0 while ($index -lt $tokens.Count) { $token = $tokens[$index] if ($token.StartsWith('-')) { $name = $token.TrimStart('-') $valueInline = $null if ($name.Contains('=')) { $parts = $name.Split('=', 2) $name = $parts[0] $valueInline = $parts[1] } $normalized = $name.ToLowerInvariant() if ($lookup.ContainsKey($normalized)) { $definition = $lookup[$normalized] if ($definition.Type -ne 'switch' -and $null -eq $valueInline -and ($index + 1) -lt $tokens.Count) { $next = $tokens[$index + 1] if (-not $next.StartsWith('-')) { $index += 2 continue } } } elseif ($null -eq $valueInline -and ($index + 1) -lt $tokens.Count) { $next = $tokens[$index + 1] if (-not $next.StartsWith('-')) { $index += 2 continue } } $index++ continue } $commandIndex = $index break } if ($null -eq $commandIndex) { return @{ Command = $tokens[0] Args = if ($tokens.Count -gt 1) { @($tokens[1..($tokens.Count - 1)]) } else { @() } } } $args = @() if ($commandIndex -gt 0) { $args += $tokens[0..($commandIndex - 1)] } if (($commandIndex + 1) -lt $tokens.Count) { $args += $tokens[($commandIndex + 1)..($tokens.Count - 1)] } $resolvedCommand = $tokens[$commandIndex] $resolvedArgs = $args # If the "command" token isn't a known CLI command, treat it as a shorthand for: # jax run -only <token> # This enables: # ./jax/jax.ps1 <task> # ./jax/jax.ps1 <task> -env ... # and keeps "plan" as a first-class command (it is handled by jax.ps1 as run -plan). $knownCommands = @( 'help', 'init', # 'init-compat', 'init-legacy', 'env', 'list-envs', 'list-tasks', 'run', 'r', 'invoke', 'plan', 'autocomplete', 'create-flavour', 'settings', 'skill', 'info', 'vault' ) $normalized = if ($null -eq $resolvedCommand) { '' } else { $resolvedCommand.ToLowerInvariant() } if ($knownCommands -notcontains $normalized) { $resolvedArgs = @($resolvedCommand) + @($resolvedArgs) $resolvedCommand = 'run' } # 'r' and 'invoke' are aliases for 'run' (same command, same semantics) if ($normalized -eq 'r' -or $normalized -eq 'invoke') { $resolvedCommand = 'run' } # Positional env/task shorthand: # jax <env-path> [<task> ...] == jax -env <env-path> -only <task>,<task>,... # An env path is recognized by containing '/' (client/flow form); task selectors never do. # Explicit -env still wins (static param overrides args downstream in run/plan). if ($resolvedCommand -in @('run', 'plan') -and $resolvedArgs.Count -ge 1) { $first = [string]$resolvedArgs[0] if (-not $first.StartsWith('-') -and $first.Contains('/')) { $positionalEnv = $first $positionalTasks = @() $passthrough = @() $i = 1 while ($i -lt $resolvedArgs.Count) { $a = [string]$resolvedArgs[$i] if ($a.StartsWith('-')) { $passthrough += $a if (($i + 1) -lt $resolvedArgs.Count) { $next = [string]$resolvedArgs[$i + 1] if (-not $next.StartsWith('-')) { $passthrough += $next $i += 2 continue } } $i++ continue } $positionalTasks += $a $i++ } $rewritten = @('-env', $positionalEnv) if ($positionalTasks.Count -gt 0) { $rewritten += @('-only', ($positionalTasks -join ',')) } $rewritten += $passthrough $resolvedArgs = $rewritten } } return @{ Command = $resolvedCommand Args = $resolvedArgs } } |