private/Invoke-WtwEditorCli.ps1
|
function Invoke-WtwEditorCli { <# .SYNOPSIS Launch a CLI-style editor on a path, surviving editor CLI renames. .DESCRIPTION `wtw open` etc. used to do `& $editor $path` against the bare name from config / the resolver. That breaks when an editor renames its CLI between major versions and leaves a dangling stub on PATH — e.g. Antigravity v2 ships its CLI as `antigravity-ide` (app: "Antigravity IDE.app") while the v1 installer's `~/.antigravity/.../bin/antigravity` stub lingers and points at a binary that no longer exists. This helper resolves the logical editor name to the first CLI candidate that actually runs (via Test-WtwEditorCli), and on macOS falls back to launching the installed .app bundle by name. The candidate chains come from the shared family table (private/Get-WtwEditorFamily.ps1), so install-time detection and launch-time invocation can't drift apart again. .PARAMETER Cmd Logical editor command (e.g. 'antigravity', 'cursor', 'code'). .PARAMETER Path File or directory to open. Omitted for remote launches, where the target travels in -PreArgs as a `--folder-uri` / `--file-uri` value. .PARAMETER PreArgs Extra arguments inserted before Path. Used by the remote opener to pass `--remote ssh-remote+<host>` / `--folder-uri vscode-remote://…`. .PARAMETER PassThru Return the resolved invocation as @{ Exe; Arguments } instead of running it. Lets `--print-only` and the tests inspect the exact command line. #> param( [Parameter(Mandatory)] [string] $Cmd, [string] $Path, [string[]] $PreArgs = @(), [switch] $PassThru ) $member = Get-WtwEditorFamilyMember -Id $Cmd $candidates = if ($member) { @($member.Cli) } else { @($Cmd) } $macApps = if ($member) { @($member.MacApps) } else { @() } $flags = if ($member) { @($member.LaunchFlags) } else { @() } $arguments = @($flags) + @($PreArgs) if ($Path) { $arguments += $Path } # 1. First CLI candidate that resolves to a real, runnable binary. $runnable = $candidates | Where-Object { Test-WtwEditorCli -Cmd $_ } | Select-Object -First 1 if ($runnable) { if ($PassThru) { return @{ Exe = $runnable; Arguments = $arguments } } & $runnable @arguments return } # 2. macOS: CLI missing/broken but the app bundle is installed — open it. # `open -a` can only carry a filesystem path, so a remote launch (which # needs --folder-uri) has no bundle fallback and must report the missing CLI. if ($IsMacOS -and $macApps.Count -gt 0 -and $PreArgs.Count -eq 0 -and $Path) { $app = $macApps | Where-Object { Test-Path "/Applications/$_.app" } | Select-Object -First 1 if ($app) { if ($PassThru) { return @{ Exe = 'open'; Arguments = @('-a', $app, $Path) } } Write-Host " '$Cmd' CLI not on PATH — opening /Applications/$app.app instead." -ForegroundColor DarkGray Write-Host " Tip: in $app, run Cmd-Shift-P → 'Shell Command: Install ... command in PATH' to enable the CLI." -ForegroundColor DarkGray & open -a $app $Path return } } # 3. Nothing usable found. $tried = $candidates -join ', ' if ($PreArgs.Count -gt 0) { Write-Error "Editor '$Cmd' is not runnable (tried CLI: $tried). A remote open needs the editor CLI on PATH — the .app bundle fallback cannot pass a remote URI." return } Write-Error "Editor '$Cmd' is not runnable (tried CLI: $tried). Install it or set a different 'editor' in your wtw config." } function Get-WtwEditorCliName { <# .SYNOPSIS The runnable CLI name for a logical editor, or $null. .DESCRIPTION Same candidate chain Invoke-WtwEditorCli launches through, exposed for callers that need to run the CLI for something other than opening a path — `--list-extensions`, `--install-extension`. #> [CmdletBinding()] param([Parameter(Mandatory)] [string] $Cmd) $member = Get-WtwEditorFamilyMember -Id $Cmd $candidates = if ($member) { @($member.Cli) } else { @($Cmd) } return ($candidates | Where-Object { Test-WtwEditorCli -Cmd $_ } | Select-Object -First 1) } |