Refresh PATH: venv-aware merge order

Reconcile two competing concerns about Refresh-SessionPath /
Refresh-Environment surfaced by separate review rounds:

  - venv at the back -> activated venv loses precedence to system Python
  - process at the front -> stale shims (old node, old python, etc.)
    still on $env:Path can beat a freshly installed tool

New merge order:
  1. Activated venv Scripts dir, only if $env:VIRTUAL_ENV is set
  2. Machine PATH freshly read from registry
  3. User PATH freshly read from registry
  4. Current $env:Path as fallback

This way an explicitly-activated venv keeps priority while a tool the
script just installed wins over any stale entry that was already on
the inherited shell PATH. When no venv is active, fresh registry
entries take precedence as expected.
This commit is contained in:
Daniel Han 2026-04-16 07:24:28 +00:00
commit 6a12d01972
2 changed files with 28 additions and 8 deletions

View file

@ -100,13 +100,23 @@ function Install-UnslothStudio {
Write-Host ""
# ── Helper: refresh PATH from registry (deduplicating entries) ──
# Process entries first so an activated venv keeps precedence, then
# machine/user. Dedup by both raw and expanded form so %VAR% and
# already-expanded copies of the same dir don't both survive.
# Merge order:
# 1. Activated venv Scripts dir (only if $env:VIRTUAL_ENV is set) so an
# explicitly-activated venv keeps precedence.
# 2. Machine, then User PATH freshly read from registry so a tool we
# just installed wins over any stale shim still in $env:Path.
# 3. Current $env:Path as fallback so process-only entries that nothing
# else covers are not lost.
# Dedup compares both raw and expanded forms so %VAR% references don't
# survive twice (once as %VAR%\foo and once as the expanded literal).
function Refresh-SessionPath {
$machine = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
$user = [System.Environment]::GetEnvironmentVariable("Path", "User")
$merged = "$env:Path;$machine;$user"
$venvScripts = if ($env:VIRTUAL_ENV) { Join-Path $env:VIRTUAL_ENV "Scripts" } else { $null }
$sources = @()
if ($venvScripts) { $sources += $venvScripts }
$sources += @($machine, $user, $env:Path)
$merged = ($sources | Where-Object { $_ }) -join ";"
$seen = @{}
$unique = New-Object System.Collections.Generic.List[string]
foreach ($p in $merged -split ";") {

View file

@ -73,10 +73,20 @@ function Refresh-Environment {
}
$machinePath = [System.Environment]::GetEnvironmentVariable('Path', 'Machine')
$userPath = [System.Environment]::GetEnvironmentVariable('Path', 'User')
# Process entries first so an activated venv keeps precedence, then
# machine/user. Dedup by both raw and expanded form so %VAR% and
# already-expanded copies of the same dir don't both survive.
$merged = "$env:Path;$machinePath;$userPath"
# Merge order:
# 1. Activated venv Scripts dir (only if $env:VIRTUAL_ENV is set) so an
# explicitly-activated venv keeps precedence.
# 2. Machine, then User PATH freshly read from registry so a tool we
# just installed wins over any stale shim still in $env:Path.
# 3. Current $env:Path as fallback so process-only entries that nothing
# else covers are not lost.
# Dedup compares both raw and expanded forms so %VAR% references don't
# survive twice (once as %VAR%\foo and once as the expanded literal).
$venvScripts = if ($env:VIRTUAL_ENV) { Join-Path $env:VIRTUAL_ENV 'Scripts' } else { $null }
$sources = @()
if ($venvScripts) { $sources += $venvScripts }
$sources += @($machinePath, $userPath, $env:Path)
$merged = ($sources | Where-Object { $_ }) -join ';'
$seen = @{}
$unique = New-Object System.Collections.Generic.List[string]
foreach ($p in $merged -split ";") {