Public/VsCodeHelpers.ps1
|
function Start-VsCode { <# .SYNOPSIS Opens Visual Studio Code with typed PowerShell parameters. .DESCRIPTION Wraps the `code` CLI with discoverable, tab-completable parameters. Supports opening files/folders, profiles, diff, goto, extension management, and more. Niche options can be passed via -RemainingArgs. .PARAMETER Path One or more files or folders to open. .PARAMETER Profile Open with the specified VS Code profile name. Tab-completes from profiles defined in VS Code's globalStorage/storage.json. .PARAMETER NewWindow Force opening in a new window. .PARAMETER ReuseWindow Force opening in an existing window. .PARAMETER Diff Compare two files. Provide exactly two file paths. .PARAMETER Goto Open a file at a specific line and character (file:line[:character]). .PARAMETER Add Add folder(s) to the last active window. .PARAMETER Wait Wait for the files to be closed before returning. .PARAMETER Agents Open the agents window. .PARAMETER DisableExtensions Disable all installed extensions for this window. .PARAMETER DisableExtension Disable specific extensions by ID for this window. .PARAMETER Log Set the log level. .PARAMETER InstallExtension Install or update extensions by ID or VSIX path. .PARAMETER UninstallExtension Uninstall extensions by ID. .PARAMETER ListExtensions List installed extensions. .PARAMETER ShowVersions Show versions when listing extensions. Only valid with -ListExtensions. .PARAMETER AddMcp Add an MCP server definition (JSON string). .PARAMETER RemainingArgs Additional arguments passed through to the code CLI. .EXAMPLE Start-VsCode . Opens the current directory in VS Code. .EXAMPLE Start-VsCode -Profile 'Backend' Opens VS Code with the Backend profile. .EXAMPLE Start-VsCode -Diff file1.cs file2.cs Compares two files in the VS Code diff editor. .EXAMPLE Start-VsCode -Goto 'src\main.cs:42' Opens main.cs at line 42. .EXAMPLE Start-VsCode -ListExtensions -ShowVersions Lists installed extensions with version numbers. .EXAMPLE Start-VsCode -Agents Opens the VS Code agents window. #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Position = 0)] [string[]]$Path, [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete) $storageFile = Join-Path $env:APPDATA 'Code\User\globalStorage\storage.json' if (Test-Path $storageFile) { $profiles = (Get-Content $storageFile -Raw | ConvertFrom-Json).userDataProfiles @($profiles | ForEach-Object { $_.name }) | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { if ($_ -match '\s') { "'$_'" } else { $_ } } } })] [string]$Profile, [switch]$NewWindow, [switch]$ReuseWindow, [ValidateCount(2, 2)] [string[]]$Diff, [string]$Goto, [string[]]$Add, [switch]$Wait, [switch]$Agents, [switch]$DisableExtensions, [string[]]$DisableExtension, [ValidateSet('critical', 'error', 'warn', 'info', 'debug', 'trace', 'off')] [string]$Log, [string[]]$InstallExtension, [string[]]$UninstallExtension, [switch]$ListExtensions, [switch]$ShowVersions, [string]$AddMcp, [Parameter(ValueFromRemainingArguments)] [string[]]$RemainingArgs ) $codeExe = (Get-Command code -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source $codeArgs = @() if ($Profile) { $codeArgs += '--profile', $Profile } if ($NewWindow) { $codeArgs += '--new-window' } if ($ReuseWindow) { $codeArgs += '--reuse-window' } if ($Diff) { $codeArgs += '--diff', $Diff[0], $Diff[1] } if ($Goto) { $codeArgs += '--goto', $Goto } if ($Add) { foreach ($a in $Add) { $codeArgs += '--add', $a } } if ($Wait) { $codeArgs += '--wait' } if ($Agents) { $codeArgs += '--agents' } if ($DisableExtensions) { $codeArgs += '--disable-extensions' } if ($DisableExtension) { foreach ($e in $DisableExtension) { $codeArgs += '--disable-extension', $e } } if ($Log) { $codeArgs += '--log', $Log } if ($InstallExtension) { foreach ($e in $InstallExtension) { $codeArgs += '--install-extension', $e } } if ($UninstallExtension) { foreach ($e in $UninstallExtension) { $codeArgs += '--uninstall-extension', $e } } if ($ListExtensions) { $codeArgs += '--list-extensions' } if ($ShowVersions) { $codeArgs += '--show-versions' } if ($AddMcp) { $codeArgs += '--add-mcp', $AddMcp } if ($Path) { $codeArgs += $Path } if ($RemainingArgs) { $codeArgs += $RemainingArgs } if ($PSCmdlet.ShouldProcess("$codeExe $($codeArgs -join ' ')", 'Execute')) { & $codeExe @codeArgs } } function Start-VsCodeChat { <# .SYNOPSIS Starts a VS Code chat session with typed PowerShell parameters. .DESCRIPTION Wraps `code chat [options] [prompt]` with discoverable parameters. .PARAMETER Prompt The prompt to run in the chat session. .PARAMETER Mode The chat mode: ask, edit, agent, or a custom mode identifier. Defaults to agent. .PARAMETER AddFile Add files as context to the chat session. .PARAMETER Maximize Maximize the chat session view. .PARAMETER ReuseWindow Force using the last active window. .PARAMETER NewWindow Force opening an empty window. .PARAMETER Profile Open with the specified VS Code profile name. .PARAMETER RemainingArgs Any additional arguments are passed through to the `code chat` command. .EXAMPLE Start-VsCodeChat 'Fix the bug in main.cs' Starts an agent chat session with the given prompt. .EXAMPLE Start-VsCodeChat -Mode edit 'Refactor the auth module' Starts an edit-mode chat session. .EXAMPLE Start-VsCodeChat 'Add tests' -AddFile src\auth.cs, src\auth.test.cs Starts a chat session with context files attached. #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Position = 0)] [string]$Prompt, [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete) @('ask', 'edit', 'agent') | Where-Object { $_ -like "$wordToComplete*" } })] [string]$Mode, [string[]]$AddFile, [switch]$Maximize, [switch]$ReuseWindow, [switch]$NewWindow, [ArgumentCompleter({ param($commandName, $parameterName, $wordToComplete) $storageFile = Join-Path $env:APPDATA 'Code\User\globalStorage\storage.json' if (Test-Path $storageFile) { $profiles = (Get-Content $storageFile -Raw | ConvertFrom-Json).userDataProfiles @($profiles | ForEach-Object { $_.name }) | Where-Object { $_ -like "$wordToComplete*" } | ForEach-Object { if ($_ -match '\s') { "'$_'" } else { $_ } } } })] [string]$Profile, [Parameter(ValueFromRemainingArguments)] [string[]]$RemainingArgs ) $codeExe = (Get-Command code -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source $chatArgs = @('chat') if ($Mode) { $chatArgs += '--mode', $Mode } if ($AddFile) { foreach ($f in $AddFile) { $chatArgs += '--add-file', $f } } if ($Maximize) { $chatArgs += '--maximize' } if ($ReuseWindow) { $chatArgs += '--reuse-window' } if ($NewWindow) { $chatArgs += '--new-window' } if ($Profile) { $chatArgs += '--profile', $Profile } if ($Prompt) { $chatArgs += $Prompt } if ($RemainingArgs) { $chatArgs += $RemainingArgs } if ($PSCmdlet.ShouldProcess("$codeExe $($chatArgs -join ' ')", 'Execute')) { & $codeExe @chatArgs } } function Get-VsCodeExtension { <# .SYNOPSIS List installed VS Code extensions. .DESCRIPTION Parses the output of 'code --list-extensions --show-versions' into typed VsCodeExtension objects with Publisher, Name, FullId, and Version properties. .PARAMETER Name Filter by extension ID. Supports wildcards. .PARAMETER Category Filter by extension category (e.g., 'themes', 'linters'). .PARAMETER Profile List extensions for a specific VS Code profile. .EXAMPLE Get-VsCodeExtension Lists all installed extensions. .EXAMPLE Get-VsCodeExtension -Name ms-python* Lists extensions matching the filter. .EXAMPLE Get-VsCodeExtension -Profile 'Backend' Lists extensions in a specific profile. #> [OutputType('VsCodeExtension')] [CmdletBinding()] param( [Parameter(Position = 0)] [string]$Name = '*', [string]$Category, [string]$Profile ) $codeExe = (Get-Command code -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source $listArgs = @('--list-extensions', '--show-versions') if ($Category) { $listArgs += '--category', $Category } if ($Profile) { $listArgs += '--profile', $Profile } & $codeExe @listArgs 2>$null | ForEach-Object { if ($_ -match '^(.+?)\.(.+?)@(.+)$') { $ext = [PSCustomObject]@{ PSTypeName = 'VsCodeExtension' Publisher = $Matches[1] Name = $Matches[2] FullId = "$($Matches[1]).$($Matches[2])" Version = $Matches[3] } if ($ext.FullId -like $Name -or $ext.Name -like $Name) { $ext } } } } function Install-VsCodeExtension { <# .SYNOPSIS Install a VS Code extension. .DESCRIPTION Wraps 'code --install-extension'. Idempotent — installs or updates. Accepts pipeline input from Get-VsCodeExtension or string IDs. .PARAMETER Id The extension ID (publisher.name) or path to a .vsix file. .PARAMETER InputObject A VsCodeExtension object from Get-VsCodeExtension. .PARAMETER PreRelease Install the pre-release version. .PARAMETER Profile Install into a specific VS Code profile. .EXAMPLE Install-VsCodeExtension -Id ms-python.python Installs the Python extension. .EXAMPLE Install-VsCodeExtension -Id ms-python.python -PreRelease Installs the pre-release version. #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'ById')] param( [Parameter(ParameterSetName = 'ById', Position = 0, Mandatory)] [ValidateNotNullOrEmpty()] [string]$Id, [Parameter(ParameterSetName = 'ByObject', Mandatory, ValueFromPipeline)] [PSObject]$InputObject, [switch]$PreRelease, [string]$Profile ) process { $extId = if ($PSCmdlet.ParameterSetName -eq 'ById') { $Id } else { $InputObject.FullId } $codeExe = (Get-Command code -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source if ($PSCmdlet.ShouldProcess($extId, 'code --install-extension')) { $installArgs = @('--install-extension', $extId) if ($PreRelease) { $installArgs += '--pre-release' } if ($Profile) { $installArgs += '--profile', $Profile } & $codeExe @installArgs 2>&1 | ForEach-Object { Write-Verbose $_ } } } } function Uninstall-VsCodeExtension { <# .SYNOPSIS Uninstall a VS Code extension. .DESCRIPTION Wraps 'code --uninstall-extension'. Accepts pipeline from Get-VsCodeExtension. .PARAMETER Id The extension ID (publisher.name). .PARAMETER InputObject A VsCodeExtension object from Get-VsCodeExtension. .EXAMPLE Uninstall-VsCodeExtension -Id ms-python.python .EXAMPLE Get-VsCodeExtension old-ext* | Uninstall-VsCodeExtension #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'ByObject')] param( [Parameter(ParameterSetName = 'ById', Position = 0, Mandatory)] [string]$Id, [Parameter(ParameterSetName = 'ByObject', Mandatory, ValueFromPipeline)] [PSObject]$InputObject ) process { $extId = if ($PSCmdlet.ParameterSetName -eq 'ById') { $Id } else { $InputObject.FullId } $codeExe = (Get-Command code -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source if ($PSCmdlet.ShouldProcess($extId, 'code --uninstall-extension')) { & $codeExe '--uninstall-extension' $extId 2>&1 | ForEach-Object { Write-Verbose $_ } } } } function Update-VsCodeExtension { <# .SYNOPSIS Update all installed VS Code extensions to the latest version. .DESCRIPTION Wraps 'code --update-extensions'. VS Code's CLI only supports bulk update, not per-extension update. .EXAMPLE Update-VsCodeExtension Updates all installed extensions. #> [CmdletBinding(SupportsShouldProcess)] param() $codeExe = (Get-Command code -CommandType Application -ErrorAction Stop | Select-Object -First 1).Source if ($PSCmdlet.ShouldProcess('all extensions', 'code --update-extensions')) { & $codeExe '--update-extensions' 2>&1 | ForEach-Object { Write-Verbose $_ } } } |