PowerShell profile and test suite Matthew created a production-ready PowerShell prompt that supports PS 5.1 and PS 7+ with segments for Git, Python venv, Conda, Node, .NET SDK, exit code, jobs, admin, Kubernetes, Azure/AWS/GCP, depth, path, and timestamp. The prompt uses a Campbell palette with 24-bit ANSI for PS7 and ConsoleColor fallback for PS5, and includes environment capability probing to adapt to constrained language mode, non-interactive hosts, and ANSI support. | Requires -Version 5.1 | | ============================================================================= | | Matthew's Production-Ready PowerShell Prompt | | Supports: PS 5.1 Windows PowerShell and PS 7+ PowerShell Core | | Segments: Git · Python venv · Conda · Node · .NET SDK · Exit code · | | Jobs · Admin · Kubernetes · Azure/AWS/GCP · Depth · Path · | | Timestamp | | Palette: Campbell PS7 24-bit ANSI / Campbell PS5 ConsoleColor fallback | | ============================================================================= | | | | ── Version detection ──────────────────────────────────────────────────────── | | $script:PS7 = $PSVersionTable.PSVersion.Major -ge 7 | | | | ============================================================================= | | SECTION 0 – ENVIRONMENT CAPABILITY PROBE run ONCE at load | | The prompt renders only what the current host supports and skips work that | | isn't relevant, so it stays correct AND fast in every environment: a full | | ANSI console, a legacy/redirected host, a non-interactive script host, a | | remoting/embedded host, or a Constrained-Language WDAC/AppLocker session. | | Every probe is wrapped so a host that doesn't implement it can't break load. | | ============================================================================= | | | | Constrained Language Mode blocks .NET method/ctor/type calls that the rich | | segments rely on; detect it so we serve a minimal, CLM-safe prompt instead. | | $script:ConstrainedLang = $false | | try { $script:ConstrainedLang = $ExecutionContext.SessionState.LanguageMode -ne 'FullLanguage' } catch { } | | | | Interactive session? Only these benefit from PSReadLine. Default to $true and | | narrow ONLY on a strong non-interactive signal a service context, or an | | explicit -NonInteractive , so we never strip history search from a real | | interactive host — note embedded consoles e.g. the VS Code PowerShell | | Extension launch via -Command yet ARE interactive, so -Command is NOT a signal. | | $script:IsInteractive = $true | | try { | | if -not Environment ::UserInteractive { $script:IsInteractive = $false } | | elseif Environment ::GetCommandLineArgs -match '^-NonInteractive' { $script:IsInteractive = $false } | | } catch { } | | | | ANSI / virtual-terminal support → the coloured ANSI render path vs the plain | | ConsoleColor Write-Host path. PS7 in any modern terminal supports VT; a host | | that doesn't or lacks the property falls back to ConsoleColor output. | | $script:SupportsAnsi = $false | | try { $script:SupportsAnsi = $script:PS7 -and bool $Host.UI.SupportsVirtualTerminal } catch { } | | | | Settable window title? Some remoting/embedded hosts have a null RawUI or throw. | | $script:SupportsTitle = $false | | try { $null = $Host.UI.RawUI.WindowTitle; $script:SupportsTitle = $true } catch { } | | | | ============================================================================= | | SECTION 1 – COLOR PALETTE | | Campbell palette values match Windows Terminal's built-in Campbell theme. | | ANSI-capable host → 24-bit ANSI escape sequences ESC 38;2;R;G;Bm | | Otherwise → Write-Host -ForegroundColor nearest ConsoleColor | | ============================================================================= | | | | if $script:SupportsAnsi { | | ── Campbell 24-bit ANSI sequences ─────────────────────────────────────── | | $E = char 27 ESC character | | | | Foreground helpers returns an ANSI escape string | | function script:Fg { param int $R, int $G, int $B "$E 38;2;$R;$G;${B}m" } | | function script:Bg { param int $R, int $G, int $B "$E 48;2;$R;$G;${B}m" } | | $script:Reset = "$E 0m" | | $script:Bold = "$E 1m" | | $script:Dim = "$E 2m" | | | | Campbell named colours | | $script:CBlack = Fg 12 12 12 Black | | $script:CRed = Fg 197 15 31 Red | | $script:CGreen = Fg 19 161 14 Green | | $script:CYellow = Fg 193 156 0 Yellow | | $script:CBlue = Fg 0 55 218 Blue | | $script:CMagenta = Fg 136 0 128 Magenta | | $script:CCyan = Fg 58 150 221 Cyan | | $script:CWhite = Fg 204 204 204 White light gray | | $script:CBrBlack = Fg 118 118 118 Bright Black dark gray | | $script:CBrRed = Fg 231 72 86 Bright Red | | $script:CBrGreen = Fg 22 198 12 Bright Green | | $script:CBrYellow= Fg 249 241 165 Bright Yellow | | $script:CBrBlue = Fg 59 120 255 Bright Blue | | $script:CBrMag = Fg 180 0 158 Bright Magenta | | $script:CBrCyan = Fg 97 214 214 Bright Cyan | | $script:CBrWhite = Fg 242 242 242 Bright White | | | | Semantic aliases | | $script:ColPath = $script:CBrCyan | | $script:ColGitBranch= $script:CBrMag | | $script:ColGitDirty = $script:CBrRed | | $script:ColGitClean = $script:CBrGreen | | $script:ColGitInfo = $script:CYellow | | $script:ColPython = $script:CBrYellow | | $script:ColConda = $script:CYellow | | $script:ColNode = $script:CGreen | | $script:ColDotNet = $script:CBrBlue | | $script:ColErrCode = $script:CBrRed | | $script:ColOK = $script:CBrGreen | | $script:ColAdmin = $script:CBrRed | | $script:ColJobs = $script:CCyan | | $script:ColK8s = $script:CBrBlue | | $script:ColCloud = $script:CBrCyan | | $script:ColDepth = $script:CBrBlack | | $script:ColTime = $script:CBrBlack | | $script:ColSep = $script:CBrBlack | | $script:ColPromptOK = $script:CBrGreen | | $script:ColPromptErr= $script:CBrRed | | | | Render a styled string PS7 path – ANSI | | function script:Styled { | | param string $Color, string $Text, switch $Bold | | if $Bold { return "$script:Bold$Color$Text$script:Reset" } | | return "$Color$Text$script:Reset" | | } | | | | } else { | | ── Campbell PS5 ConsoleColor fallback ──────────────────────────────────── | | No ANSI; we build the prompt as an array of Write-Host calls instead. | | Prompt function writes each segment, then returns an empty string. | | | | $script:ColPath = 'Cyan' | | $script:ColGitBranch = 'Magenta' | | $script:ColGitDirty = 'Red' | | $script:ColGitClean = 'Green' | | $script:ColGitInfo = 'Yellow' | | $script:ColPython = 'Yellow' | | $script:ColConda = 'Yellow' | | $script:ColNode = 'Green' | | $script:ColDotNet = 'Blue' | | $script:ColErrCode = 'Red' | | $script:ColOK = 'Green' | | $script:ColAdmin = 'Red' | | $script:ColJobs = 'Cyan' | | $script:ColK8s = 'Blue' | | $script:ColCloud = 'Cyan' | | $script:ColDepth = 'DarkGray' | | $script:ColTime = 'DarkGray' | | $script:ColSep = 'DarkGray' | | $script:ColPromptOK = 'Green' | | $script:ColPromptErr = 'Red' | | | | In PS5 mode the prompt pieces are accumulated for Write-Host | | $script:PromptPieces = System.Collections.Generic.List hashtable ::new | | | | function script:Styled { | | param string $Color, string $Text, switch $Bold | | Returns nothing; accumulates into PromptPieces for later Write-Host | | $null = $script:PromptPieces.Add @{ Color = $Color; Text = $Text } | | return '' | | } | | } | | | | ============================================================================= | | SECTION 2 – UTILITY HELPERS | | ============================================================================= | | | | Separator character no Nerd Fonts dependency; swap to or if you have NF | | $script:Sep = '|' | | | | How to compare a path against $HOME for the '~' collapse. Windows paths are | | case-insensitive C:\ vs c:\, USERS vs Users , so an Ordinal compare can miss | | the home prefix and leave the path un-collapsed; Linux/macOS file systems | | PS7 only ARE case-sensitive, so stay Ordinal there. $IsWindows is only | | evaluated on PS7 the -and short-circuits on 5.1, where it doesn't exist . | | $script:PathComparison = System.StringComparison ::OrdinalIgnoreCase | | if $script:PS7 -and -not $IsWindows { | | $script:PathComparison = System.StringComparison ::Ordinal | | } | | | | Add a segment string to the prompt line ANSI returns inline; ConsoleColor queues | | function script:Seg { | | param string $Color, string $Text | | if $script:SupportsAnsi { | | return "$ Styled $Color "$Text" $ Styled $script:ColSep $script:Sep " | | } else { | | $null = $script:PromptPieces.Add @{ Color = $Color; Text = "$Text" } | | $null = $script:PromptPieces.Add @{ Color = $script:ColSep; Text = $script:Sep } | | return '' | | } | | } | | | | Safe command existence check avoids exceptions , cached per session. | | Use -ErrorAction Ignore, not SilentlyContinue: SilentlyContinue hides the | | error but STILL appends it to $Error, so every prompt render would leave | | "