diff --git a/powershell/Dotfiles/Dotfiles.psd1 b/powershell/Dotfiles/Dotfiles.psd1 index 752e234..c17ec81 100644 --- a/powershell/Dotfiles/Dotfiles.psd1 +++ b/powershell/Dotfiles/Dotfiles.psd1 @@ -41,6 +41,7 @@ 'Get-DotToolNudge' 'Test-DotNonInteractiveArg' 'Test-InteractiveShell' + 'Test-InMux' 'Get-DotfilesLinkPlan' 'ConvertTo-WslPath' # doctor: result model, aggregation + pure logic (host probes stay in the fragment) diff --git a/powershell/core/05-lib.ps1 b/powershell/core/05-lib.ps1 index 35091fe..166b585 100644 --- a/powershell/core/05-lib.ps1 +++ b/powershell/core/05-lib.ps1 @@ -17,7 +17,7 @@ # ============================================================================ # --- load contract (checked by tests/LoadContract.Tests.ps1) ------------------ -# provides: Test-SensitiveHistoryLine, Get-DotConfirmAnswer, Test-DotGum, Read-DotConfirm, Get-DotStringSha256, Get-DotSpinnerFrame, Invoke-DotSpinner, Test-DotEmailish, Get-DotToolNudge, Test-DotNonInteractiveArg, Test-InteractiveShell, Get-DotfilesLinkPlan, Test-DotColor, Test-DotUnicode, Get-DotGlyph, Write-DotHost, Write-DotBanner, Get-DotConsoleWidth, Format-DotWrap, Write-DotRule, Write-DotErr, Write-DotOk, Write-DotWarn +# provides: Test-SensitiveHistoryLine, Get-DotConfirmAnswer, Test-DotGum, Read-DotConfirm, Get-DotStringSha256, Get-DotSpinnerFrame, Invoke-DotSpinner, Test-DotEmailish, Get-DotToolNudge, Test-DotNonInteractiveArg, Test-InteractiveShell, Test-InMux, Get-DotfilesLinkPlan, Test-DotColor, Test-DotUnicode, Get-DotGlyph, Write-DotHost, Write-DotBanner, Get-DotConsoleWidth, Format-DotWrap, Write-DotRule, Write-DotErr, Write-DotOk, Write-DotWarn # requires: (none) # --- Test-SensitiveHistoryLine ------------------------------------------------ @@ -357,6 +357,19 @@ function Test-InteractiveShell { return -not (Test-DotNonInteractiveArg ([Environment]::GetCommandLineArgs())) } +# --- Test-InMux --------------------------------------------------------------- +# Are we running inside a psmux/tmux pane (i.e. the status bar is showing)? psmux +# exports TMUX + PSMUX_SESSION into pane shells (verified against psmux src/pane.rs); +# either marker means "in a pane". THE single source of truth for pane detection — +# shared by the psmux auto-attach guard (os/30-windows.ps1, so it never nests a +# session inside an existing pane) and the pill's auto-arm/status (os/33-psmux-pill.ps1), +# which previously each hand-maintained their own marker list and had already drifted. +function Test-InMux { + [OutputType([bool])] + param() + [bool]($env:TMUX -or $env:PSMUX_SESSION) +} + # --- Get-DotfilesLinkPlan ----------------------------------------------------- # THE single source of truth for every symlink this repo wires: one ordered list # that install.ps1 creates, uninstall.ps1 removes, and dotfiles-doctor verifies. diff --git a/powershell/os/30-windows.ps1 b/powershell/os/30-windows.ps1 index 2c6bd05..9991606 100644 --- a/powershell/os/30-windows.ps1 +++ b/powershell/os/30-windows.ps1 @@ -4,7 +4,7 @@ # --- load contract (checked by tests/LoadContract.Tests.ps1) ------------------ # provides: scu, scs, sci, scl, sccl, wgu, wgs, wgi, update-host, path, open, admin, setenv, getenv, modules-localize -# requires: Get-DotModulePrunePlan, Test-Cmd, Test-InteractiveShell, up, Write-DotErr, Write-DotHost, Write-DotWarn +# requires: Get-DotModulePrunePlan, Test-Cmd, Test-InMux, Test-InteractiveShell, up, Write-DotErr, Write-DotHost, Write-DotWarn # --- scoop (your primary CLI package manager on the host) --------------------- if (Test-Cmd scoop) { @@ -136,13 +136,11 @@ function modules-localize { } # --- Start psmux session (top-level interactive shell only) ------------------- -# $InMux must list every marker psmux sets inside a pane, or a shell that IS in a -# pane won't be recognised and we'd auto-launch a NESTED session. The authoritative -# pair — verified against psmux src/pane.rs and used by os/33-psmux-pill.ps1's -# Test-InMux — is TMUX + PSMUX_SESSION. (The old list checked PSMUX / PSMUX_PANE, -# which psmux does NOT export, and omitted PSMUX_SESSION; it only worked because -# TMUX happens to be set.) Keep TMUX_PANE too as a harmless belt-and-braces marker. -$InMux = $env:TMUX -or $env:PSMUX_SESSION -or $env:TMUX_PANE +# Pane detection is Test-InMux (core/05-lib.ps1) — the single source of truth, +# shared with os/33-psmux-pill.ps1. If a shell that IS in a pane weren't +# recognised we'd auto-launch a NESTED session, so the marker set (TMUX + +# PSMUX_SESSION, verified against psmux src/pane.rs) lives in exactly one place. +$InMux = Test-InMux # Only auto-launch for a *top-level interactive* shell. The profile is ALSO # loaded for `pwsh -Command ...` / `pwsh -File ...` (VS Code tasks, git hooks, diff --git a/powershell/os/33-psmux-pill.ps1 b/powershell/os/33-psmux-pill.ps1 index 8600875..902cbcd 100644 --- a/powershell/os/33-psmux-pill.ps1 +++ b/powershell/os/33-psmux-pill.ps1 @@ -33,16 +33,15 @@ # --- load contract (checked by tests/LoadContract.Tests.ps1) ------------------ # provides: psmux-pill-now, psmux-pill-enable, psmux-pill-disable, psmux-pill-status -# requires: Test-Cmd, Write-DotErr, Write-DotHost, Write-DotOk +# requires: Test-Cmd, Test-InMux, Write-DotErr, Write-DotHost, Write-DotOk if (-not (Test-Cmd psmux)) { return } $script:PillCache = Join-Path $env:LOCALAPPDATA 'dotfiles\psmux-netinfo.pill' $script:PillSource = 'PsmuxPillRefresh' # Register-ObjectEvent SourceIdentifier -# Inside a psmux pane? psmux exports TMUX + PSMUX_SESSION into pane shells -# (verified in psmux src/pane.rs). Either marker means "the bar is showing". -function script:Test-InMux { [bool]($env:TMUX -or $env:PSMUX_SESSION) } +# Pane detection (Test-InMux — "is the bar showing?") is shared from +# core/05-lib.ps1 now, so it can't drift from the psmux auto-attach guard's copy. function script:Get-PillScript { $p = if ($global:DOTFILES) { Join-Path $global:DOTFILES 'psmux\scripts\psmux-netinfo.ps1' } else { $null } diff --git a/tests/Lib.Tests.ps1 b/tests/Lib.Tests.ps1 index 646081f..5e9889a 100644 --- a/tests/Lib.Tests.ps1 +++ b/tests/Lib.Tests.ps1 @@ -58,6 +58,24 @@ Describe 'Test-DotNonInteractiveArg' { } } +Describe 'Test-InMux' { + BeforeAll { + $script:savedTmux = $env:TMUX + $script:savedSess = $env:PSMUX_SESSION + } + AfterAll { + if ($null -eq $script:savedTmux) { Remove-Item Env:TMUX -ErrorAction SilentlyContinue } else { $env:TMUX = $script:savedTmux } + if ($null -eq $script:savedSess) { Remove-Item Env:PSMUX_SESSION -ErrorAction SilentlyContinue } else { $env:PSMUX_SESSION = $script:savedSess } + } + BeforeEach { + Remove-Item Env:TMUX -ErrorAction SilentlyContinue + Remove-Item Env:PSMUX_SESSION -ErrorAction SilentlyContinue + } + It 'is $false outside a pane (no markers)' { Test-InMux | Should -BeFalse } + It 'is $true when TMUX is set' { $env:TMUX = 'default,1,0'; Test-InMux | Should -BeTrue } + It 'is $true when PSMUX_SESSION is set' { $env:PSMUX_SESSION = 'main'; Test-InMux | Should -BeTrue } +} + Describe 'Test-DotColor' { It 'enables colour by default' { Test-DotColor -NoColor '' -Term 'xterm' | Should -BeTrue } It 'disables colour when NO_COLOR set' { Test-DotColor -NoColor '1' -Term 'xterm' | Should -BeFalse } diff --git a/tests/Module.Tests.ps1 b/tests/Module.Tests.ps1 index d2a4638..c32676e 100644 --- a/tests/Module.Tests.ps1 +++ b/tests/Module.Tests.ps1 @@ -43,7 +43,7 @@ Describe 'Dotfiles module exports' { 'Test-DotGum', 'Test-DotEmailish', 'Get-DotSpinnerFrame', 'Format-DotSpinnerLine', 'Invoke-DotSpinner', 'Test-SensitiveHistoryLine', 'Get-DotStringSha256', 'Get-DotToolNudge', 'Get-DotfilesLinkPlan', - 'Test-DotNonInteractiveArg', 'Test-InteractiveShell', + 'Test-DotNonInteractiveArg', 'Test-InteractiveShell', 'Test-InMux', 'ConvertTo-WslPath', 'New-DoctorResult', 'Get-DoctorSummary', 'Get-DoctorGroup', 'Get-FragmentHealthResult', 'Get-DotRepoVersionDetail',