Public/Start-DevShell.ps1

function Get-VsInstallerPath {
    [CmdletBinding()]
    param()

    $programFilesX86 = ${env:ProgramFiles(x86)}
    if (-not $programFilesX86) {
        $programFilesX86 = [Environment]::GetFolderPath('ProgramFilesX86')
    }
    if (-not $programFilesX86) { return $null }

    Join-Path $programFilesX86 'Microsoft Visual Studio\Installer'
}

function Get-CleanLoginPath {
    [CmdletBinding()]
    param()

    $separator = [System.IO.Path]::PathSeparator
    $segments = foreach ($pathValue in @(
            [Environment]::GetEnvironmentVariable('Path', 'Machine'),
            [Environment]::GetEnvironmentVariable('Path', 'User')
        )) {
        if ($pathValue) {
            $pathValue -split [regex]::Escape($separator) | Where-Object { $_ }
        }
    }

    $installerPath = Get-VsInstallerPath
    if ($installerPath) {
        $segments += $installerPath
    }

    ($segments | Select-Object -Unique) -join $separator
}

function Invoke-DevShellProcess {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [hashtable]$Environment
    )

    Start-Process -FilePath 'pwsh' -ArgumentList @('-NoLogo') -NoNewWindow -Wait -Environment $Environment
}

function Start-DevShell {
    <#
    .SYNOPSIS
        Starts a nested PowerShell session for a Visual Studio developer environment.
 
    .DESCRIPTION
        Starts pwsh inline in the current window and waits until the nested shell
        exits. The selected Visual Studio year is passed to the child process via
        VSDEV_VERSION, with VSDEV_ARCH and VSDEV_HOSTARCH carrying architecture
        choices. The child process receives a clean login PATH composed from the
        Machine and User PATH values plus the Visual Studio Installer directory,
        so an already-loaded toolset in the parent process is not inherited.
 
        This cmdlet is only a launcher. A profile or provider module in the child
        session is expected to honor VSDEV_VERSION by invoking the matching
        Set-VS<year> command.
 
    .PARAMETER Version
        Visual Studio year to request, such as 2022 or 2026. Defaults to the
        latest year returned by Get-InstalledVsVersion.
 
    .PARAMETER Arch
        Target architecture to pass to the child session through VSDEV_ARCH.
        Defaults to amd64.
 
    .PARAMETER HostArch
        Host architecture to pass to the child session through VSDEV_HOSTARCH.
        Defaults to amd64.
 
    .EXAMPLE
        Start-DevShell
        Starts a nested PowerShell session for the latest discoverable Visual
        Studio developer environment.
 
    .EXAMPLE
        Start-DevShell 2022
        Starts a nested PowerShell session requesting Visual Studio 2022.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Position = 0)]
        [ArgumentCompleter({
            param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
            Get-InstalledVsVersion |
                Where-Object { "$_" -like "$wordToComplete*" } |
                ForEach-Object { "$_" }
        })]
        [int]$Version,

        [ValidateNotNullOrEmpty()]
        [string]$Arch = 'amd64',

        [ValidateNotNullOrEmpty()]
        [string]$HostArch = 'amd64'
    )

    Assert-WindowsOnly -CommandName $MyInvocation.MyCommand.Name

    $installedVersions = @(Get-InstalledVsVersion | ForEach-Object { [int]$_ } | Sort-Object -Descending -Unique)
    if ($installedVersions.Count -eq 0) {
        throw 'No installed Visual Studio developer environments were found. Install Visual Studio and a provider that exposes Set-VS<year> commands.'
    }

    $selectedVersion = if ($PSBoundParameters.ContainsKey('Version')) { $Version } else { $installedVersions[0] }
    if ($selectedVersion -notin $installedVersions) {
        throw "Visual Studio $selectedVersion is not available. Available versions: $($installedVersions -join ', ')."
    }

    $environment = @{
        VSDEV_VERSION  = [string]$selectedVersion
        VSDEV_ARCH     = $Arch
        VSDEV_HOSTARCH = $HostArch
        PATH           = Get-CleanLoginPath
    }

    Invoke-DevShellProcess -Environment $environment
}