@@ -1974,7 +1974,7 @@ function df {
19741974# Find and replace text in a file
19751975function sed ($file , $find , $replace ) {
19761976 if (-not $file -or -not (Test-Path - LiteralPath $file )) {
1977- Write-Warning " File not found: $file "
1977+ Write-Error " File not found: $file "
19781978 return
19791979 }
19801980 if ($null -eq $find -or $find -eq ' ' ) { Write-Error " Usage: sed <file> <find> <replace>" ; return }
@@ -2206,11 +2206,12 @@ if (Get-Command eza -ErrorAction SilentlyContinue) {
22062206 else {
22072207 Remove-Item Alias:\ls - Force - ErrorAction SilentlyContinue
22082208 }
2209- # ls/la/ll/lt: directory listing via eza (icons, git status, tree)
2210- function ls { eza -- icons @args }
2211- function la { eza - a -- icons @args }
2212- function ll { eza - la -- icons -- git @args }
2213- function lt { eza -- tree -- icons -- level= 2 @args }
2209+ # ls/la/ll/lt: directory listing via eza (icons, git status, tree). Pass the icon setting as
2210+ # --icons=WHEN: eza takes an optional value there, so a bare --icons swallows a following path.
2211+ function ls { eza -- icons= auto @args }
2212+ function la { eza - a -- icons= auto @args }
2213+ function ll { eza - la -- icons= auto -- git @args }
2214+ function lt { eza -- tree -- icons= auto -- level= 2 @args }
22142215}
22152216else {
22162217 if ($isInteractive ) { Write-Warning " eza not found. Install it with: winget install -e --id eza-community.eza" }
@@ -2765,7 +2766,7 @@ if (Get-Command wsl.exe -ErrorAction SilentlyContinue) {
27652766 $unc = Resolve-WslUncPath - Distro $Distro - Path $Path
27662767 if (-not $unc ) { return }
27672768 if (Get-Command eza - ErrorAction SilentlyContinue) {
2768- $ezaArgs = @ (' --tree' , " --level=$Depth " , ' --icons' , ' --git-ignore' )
2769+ $ezaArgs = @ (' --tree' , " --level=$Depth " , ' --icons=auto ' , ' --git-ignore' )
27692770 if ($All ) { $ezaArgs += ' -a' }
27702771 & eza @ezaArgs $unc
27712772 }
@@ -2835,7 +2836,10 @@ function reload { . $PROFILE }
28352836# Diagnose tools, caches, fonts, PATH, modules, and plugins with OK, WARN, or FAIL results.
28362837function Test-ProfileHealth {
28372838 [CmdletBinding ()]
2838- param ()
2839+ param (
2840+ # Emit the result objects for scripting; the rendered report prints either way.
2841+ [switch ]$PassThru
2842+ )
28392843
28402844 $results = @ ()
28412845
@@ -2891,38 +2895,65 @@ function Test-ProfileHealth {
28912895 $results += [pscustomobject ]@ { Category = ' Config' ; Check = ' user-settings.json' ; Status = ' WARN' ; Detail = ' missing (no overrides applied)' }
28922896 }
28932897
2894- # Font from terminal-config.json.fontInstall.displayName
2895- $tcPath = Join-Path $cacheDir ' terminal-config.json'
2896- if (Test-Path $tcPath ) {
2897- try {
2898- $tc = Get-Content $tcPath - Raw - ErrorAction Stop | ConvertFrom-Json - ErrorAction Stop
2899- $fontName = if ($tc.fontInstall ) { $tc.fontInstall.displayName } else { $null }
2900- if ($fontName ) {
2901- # Report unavailable System.Drawing as WARN on non-Windows platforms.
2902- if (-not (' System.Drawing.Text.InstalledFontCollection' -as [type ])) {
2903- Add-Type - AssemblyName System.Drawing - ErrorAction SilentlyContinue
2904- }
2905- if (-not (' System.Drawing.Text.InstalledFontCollection' -as [type ])) {
2906- $results += [pscustomobject ]@ { Category = ' Fonts' ; Check = $fontName ; Status = ' WARN' ; Detail = ' System.Drawing unavailable on this host; cannot verify' }
2907- }
2908- else {
2909- $installed = $false
2898+ # Check the font this install actually uses: a wizard choice in user-settings.json
2899+ # overrides the shipped terminal-config.json default.
2900+ $fontName = $null
2901+ try {
2902+ if (Test-Path $us ) {
2903+ $userCfg = Get-Content $us - Raw - ErrorAction Stop | ConvertFrom-Json - ErrorAction Stop
2904+ if ($userCfg.defaults -and $userCfg.defaults.font -and $userCfg.defaults.font.face ) {
2905+ $fontName = [string ]$userCfg.defaults.font.face
2906+ }
2907+ }
2908+ }
2909+ catch { $null = $_ }
2910+ if (-not $fontName ) {
2911+ $tcPath = Join-Path $cacheDir ' terminal-config.json'
2912+ if (Test-Path $tcPath ) {
2913+ try {
2914+ $tc = Get-Content $tcPath - Raw - ErrorAction Stop | ConvertFrom-Json - ErrorAction Stop
2915+ if ($tc.fontInstall ) { $fontName = [string ]$tc.fontInstall.displayName }
2916+ }
2917+ catch { $null = $_ }
2918+ }
2919+ }
2920+ if ($fontName ) {
2921+ # Report unavailable System.Drawing as WARN on non-Windows platforms.
2922+ if (-not (' System.Drawing.Text.InstalledFontCollection' -as [type ])) {
2923+ Add-Type - AssemblyName System.Drawing - ErrorAction SilentlyContinue
2924+ }
2925+ if (-not (' System.Drawing.Text.InstalledFontCollection' -as [type ])) {
2926+ $results += [pscustomobject ]@ { Category = ' Fonts' ; Check = $fontName ; Status = ' WARN' ; Detail = ' System.Drawing unavailable on this host; cannot verify' }
2927+ }
2928+ else {
2929+ $installed = $false
2930+ try {
2931+ $fc = New-Object System.Drawing.Text.InstalledFontCollection
2932+ $installed = $fc.Families.Name -contains $fontName
2933+ $fc.Dispose ()
2934+ }
2935+ catch { $null = $_ }
2936+ # GDI+ caches the family list per process, so a font installed after this session
2937+ # started reads as missing; the font registry is the authoritative fallback.
2938+ if (-not $installed ) {
2939+ foreach ($hive in @ (' HKCU:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' , ' HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts' )) {
29102940 try {
2911- $fc = New-Object System.Drawing.Text.InstalledFontCollection
2912- $installed = $fc.Families.Name -contains $fontName
2913- $fc.Dispose ()
2941+ $fontKey = Get-ItemProperty - Path $hive - ErrorAction Stop
2942+ if (@ ($fontKey.PSObject.Properties.Name | Where-Object { $_ -like " $fontName *" }).Count -gt 0 ) {
2943+ $installed = $true
2944+ break
2945+ }
29142946 }
29152947 catch { $null = $_ }
2916- if ($installed ) {
2917- $results += [pscustomobject ]@ { Category = ' Fonts' ; Check = $fontName ; Status = ' OK' ; Detail = ' installed' }
2918- }
2919- else {
2920- $results += [pscustomobject ]@ { Category = ' Fonts' ; Check = $fontName ; Status = ' FAIL' ; Detail = ' not installed (run setup.ps1 -Wizard or Update-Profile)' }
2921- }
29222948 }
29232949 }
2950+ if ($installed ) {
2951+ $results += [pscustomobject ]@ { Category = ' Fonts' ; Check = $fontName ; Status = ' OK' ; Detail = ' installed' }
2952+ }
2953+ else {
2954+ $results += [pscustomobject ]@ { Category = ' Fonts' ; Check = $fontName ; Status = ' FAIL' ; Detail = ' not installed (run setup.ps1 -Wizard or Update-Profile)' }
2955+ }
29242956 }
2925- catch { $null = $_ }
29262957 }
29272958
29282959 # PATH - WindowsApps (winget shims location)
@@ -2979,8 +3010,9 @@ function Test-ProfileHealth {
29793010 $summaryColor = if ($failCount -gt 0 ) { ' Red' } elseif ($warnCount -gt 0 ) { ' Yellow' } else { ' Green' }
29803011 Write-Host (" Summary: {0} OK, {1} WARN, {2} FAIL" -f $okCount , $warnCount , $failCount ) - ForegroundColor $summaryColor
29813012
2982- # Return the objects too so scripts can query: Test-ProfileHealth | Where Status -eq 'FAIL'
2983- $results
3013+ # Emit objects only on request: a bare call would otherwise render the table a second
3014+ # time through the default formatter. Scripts use -PassThru to query the rows.
3015+ if ($PassThru ) { $results }
29843016}
29853017Set-Alias - Name psp- doctor - Value Test-ProfileHealth - Scope Script
29863018
@@ -3354,7 +3386,8 @@ function Uninstall-Profile {
33543386 # Phase 5: Nerd Fonts (opt-in). Shell font installs land per-user on Windows 10/11 unless
33553387 # elevated, so both scopes are swept; only the machine-wide one needs admin.
33563388 if ($RemoveFonts ) {
3357- # Derive the uninstall font filter from terminal-config.json with the install default as fallback.
3389+ # Derive the uninstall font filter from what setup actually installed: a wizard font choice
3390+ # in user-settings.json wins over terminal-config.json, which wins over the install default.
33583391 $fontDisplayName = ' CaskaydiaCove NF'
33593392 try {
33603393 $tcPath = Join-Path $env: LOCALAPPDATA ' PowerShellProfile\terminal-config.json'
@@ -3364,6 +3397,16 @@ function Uninstall-Profile {
33643397 }
33653398 }
33663399 catch { $null = $_ }
3400+ try {
3401+ $usPath = Join-Path $env: LOCALAPPDATA ' PowerShellProfile\user-settings.json'
3402+ if (Test-Path $usPath ) {
3403+ $usCfg = Get-Content $usPath - Raw | ConvertFrom-Json
3404+ if ($usCfg.defaults -and $usCfg.defaults.font -and $usCfg.defaults.font.face ) {
3405+ $fontDisplayName = [string ]$usCfg.defaults.font.face
3406+ }
3407+ }
3408+ }
3409+ catch { $null = $_ }
33673410 $tokens = @ ($fontDisplayName -split ' \s+' | Where-Object { $_ })
33683411 # Registry entries keep the display form ("CaskaydiaCove NF Bold (TrueType)")...
33693412 $regNamePattern = ($tokens | ForEach-Object { [regex ]::Escape($_ ) }) -join ' .*'
@@ -5856,7 +5899,8 @@ function Start-ProfileTour {
58565899 $null = Read-Host ' Ready'
58575900 foreach ($cat in $categories ) {
58585901 Write-Host ' '
5859- Write-Host (" -- {0} ({1} commands) --" -f $cat.Name , $cat.Count ) - ForegroundColor Cyan
5902+ $countLabel = if ($cat.Count -eq 1 ) { ' command' } else { ' commands' }
5903+ Write-Host (" -- {0} ({1} {2}) --" -f $cat.Name , $cat.Count , $countLabel ) - ForegroundColor Cyan
58605904 foreach ($entry in ($cat.Group | Sort-Object Name | Select-Object - First 8 )) {
58615905 $synopsis = if ($entry.Synopsis ) { $entry.Synopsis } else { ' ' }
58625906 Write-Host (" {0,-22} {1}" -f $entry.Name , $synopsis ) - ForegroundColor Gray
@@ -6142,7 +6186,7 @@ ${g}reload${r} - Reload the PowerShell profile.
61426186${g} Clear-ProfileCache${r} - Reset profile caches plus OMP internal caches.
61436187${g} Clear-Cache${r} [-IncludeSystemCaches] - Clear user/system temp caches.
61446188${g} duration${r} - Elapsed time of the last command.
6145- ${g} Test-ProfileHealth${r} / ${g} psp-doctor${r} - Diagnose install: tools, caches, fonts, PATH.
6189+ ${g} Test-ProfileHealth${r} [-PassThru] / ${g} psp-doctor${r} - Diagnose install: tools, caches, fonts, PATH. -PassThru also returns the rows for scripting .
61466190${g} Uninstall-Profile${r} - Remove profile, caches, and WT changes. Use -All for everything, -HardResetWindowsTerminal to reset WT to defaults, -Force to uninstall PSFzf/tools even under CI/agent env vars.
61476191
61486192${c} Git${r}
0 commit comments