-
Notifications
You must be signed in to change notification settings - Fork 0
PowerShell
PowerShell 7 profile and prompt for Windows (and Linux where applicable).
| Tool | Purpose | Install |
|---|---|---|
| PowerShell 7+ | Shell | winget install Microsoft.PowerShell |
| PSReadLine 2.2+ | Line editing | ships with pwsh 7; update via Install-Module PSReadLine
|
| PSFzf | Fzf integration | Install-Module PSFzf |
| git-completion | Git tab completion | Install-Module git-completion |
| fzf | Fuzzy finder | winget install junegunn.fzf |
| zoxide | Smart cd
|
winget install ajeetdsouza.zoxide |
| Az.Tools.Predictor | Azure command prediction | Install-Module Az.Tools.Predictor |
| WinGet.CommandNotFound | Package suggestions | installed via Microsoft Store |
| jujutsu (jj) | Optional — jj VCS segment in the prompt | winget install jj-vcs.jj |
| File | Notes |
|---|---|
Microsoft.PowerShell_profile.ps1 |
Shared profile — used on all machines |
Profile/Set-Prompt.ps1 |
Prompt definition (jj/git + Azure context) |
Profile/AzCliAccount.ps1 |
Azure CLI named-profile switch (azs) — defines functions only; the profile calls Restore-AzActiveProfile in Phase 1 |
The installer generates a stub at ~/Documents/PowerShell/Microsoft.PowerShell_profile.ps1
that dot-sources the repo file — changes are live immediately without re-running setup.
Standalone PowerShell tools (not loaded by the profile — run on demand). Each has
comment-based help; run Get-Help .\Scripts\<Name>.ps1 -Full.
| Script | Purpose |
|---|---|
Set-CodingFont.ps1 |
Switch the coding font across all dotfiles targets at once — Windows Terminal (live + repo), VS Code (live + repo snapshot), and Vim guifont. Set-CodingFont commit
|
Install-CodingFont.ps1 |
Download + per-user install + session-activate Nerd Font (Mono) faces from the nerd-fonts release. Install-CodingFont CommitMono
|
New-FontComparison.ps1 |
Self-contained side-by-side HTML/PNG comparison of the candidate fonts with a PowerShell/C#/Bicep language switcher. -Fonts filters the set |
New-FontGlyphTest.ps1 |
Glyph-separation torture test (rn/m, cl/d, vv/w, 1lI|, 0O) across Commit/JetBrains/0xProto |
New-FontLigatureTest.ps1 |
Ligatures-on vs -off comparison so you can see what each font actually fuses |
The New-Font* tools subset each font to the sample glyphs and embed them as base64 woff2,
so the output HTML renders the real fonts in any browser. They need Python with
fonttools + brotli (pip install fonttools brotli) for subsetting and Edge for the PNG.
Generated .font-*.html/.png land at the repo root and are git-ignored.
The profile is structured in three phases to keep startup fast:
Phase 1 — blocking (runs before first prompt):
Single PSModulePath scan into $global:ProfileModules cache, PSReadLine
with PredictionSource History, all key bindings, prompt definition.
Sets RIPGREP_CONFIG_PATH and FZF_DEFAULT_OPTS_FILE pointing at repo files
(no copy needed). Reads AppsUseLightTheme from the registry once into
$_isDark, then sets FZF_DEFAULT_OPTS, LG_CONFIG_FILE (lazygit base +
theme), and EZA_CONFIG_DIR (eza theme dir) to catppuccin mocha or latte —
mirrors nvim's theme detection.
Sets _ZO_RESOLVE_SYMLINKS=1 so zoxide stores resolved paths; prevents
duplicate database entries when navigating through junctions.
Phase 2a — first idle:
PSFzf, zoxide, upgrade to PredictionSource HistoryAndPlugin, the fd-backed
FZF_*_COMMAND vars (kept separate from ripgreprc so pickers stay
.gitignore-clean while rg search stays exhaustive), the eza ll/la/lt
helpers, and the Azure context timer (Initialize-AzTimer, moved off the load
path). Guarded by
$global:ProfileDeferredDone. Interactive tools needed immediately.
Phase 2b — next idle:
az + zellij tab-completers, git-completion (+ g alias registration), WinGet
CommandNotFound. Guarded by $global:ProfileDeferredSecondaryDone. Split from 2a
so the first keypress isn't blocked by their import cost.
Phase 3 — async (background runspace):
Azure context refresh on a 60-second timer. The timer is created in Phase 2a
(Initialize-AzTimer); the refresh itself runs in a background runspace wired up
in Set-Prompt.ps1.
| Chord | Action |
|---|---|
Ctrl+r |
Reverse history search (fzf) |
Ctrl+t |
FZF file picker |
Alt+f |
FZF ripgrep search |
Alt+b |
FZF git branch switcher |
Alt+g |
FZF git worktree switcher |
Ctrl+f |
Forward char |
Ctrl+b |
Backward char |
Ctrl+p / Ctrl+n
|
Previous / next history |
Ctrl+a / Ctrl+e
|
Beginning / end of line |
Ctrl+w |
Delete word backward |
Ctrl+u |
Delete line backward |
Ctrl+Space / Shift+Tab
|
Menu complete |
Ctrl+[ (Oem4) |
Vi command mode |
- jj (Jujutsu) change-id, closest bookmark, ahead count, and state — shown instead of git in jj repos (takes precedence in colocated repos; toggle
ShowJj). Gate is a filesystem walk; up to 3 jj processes, all--ignore-working-copy. Rendersjj:<change-id> <bookmark> ↑<dist> *<files> ∅ ✎(∅empty,✎no description,!conflict) - Git branch and status — synchronous, 3 git processes per prompt (used when not in a jj repo)
- Azure subscription context — async via background runspace, refreshed every 60s
- Active az CLI profile tag (
az:<name>, oraz:defaultwhenAZURE_CONFIG_DIRis unset) — always shown so the current account is never in doubt; a cheap$env:AZURE_CONFIG_DIRread, noazprocess (see the profile-switch section below) - Last command exit status (colour coded) and execution time
- Truncated path for long directories
- Windows Terminal OSC 9;9 CWD tracking; also syncs the Win32 process CWD so Zellij opens new panes in the current directory
Switch the Azure CLI (az) between named profiles in the running shell — no new
terminal, no re-login. az keeps all state (including the token cache) under one config
dir, so each profile is its own dir at ~/.azure-profiles/<name>, selected by pointing
AZURE_CONFIG_DIR at it. The function is Switch-AzProfile, aliased azs:
azs # fzf picker: every profile plus default, with its cached identity
azs work # switch to ~/.azure-profiles/work
azs personal # switch to ~/.azure-profiles/personal
azs default # unset AZURE_CONFIG_DIR — back to az's own ~/.azure
azs work-admin # dir doesn't exist? it's created, and you're told to run: az login| Command | AZURE_CONFIG_DIR |
Prompt tag |
|---|---|---|
azs <name> |
~/.azure-profiles/<name> |
az:<name> |
azs default |
unset → az's own ~/.azure
|
az:default |
Names must match ^[A-Za-z0-9][A-Za-z0-9_-]*$ — that is also the path-injection guard,
since the name becomes a directory. default is reserved and never gets a directory.
One identity per profile dir is the point, not a side effect. az devops builds its
auth candidate list from the config dir's cached subscriptions and returns the first
identity that can list any project — so a dir holding two logins can silently act as an
account you did not pick. Never log two accounts into one profile dir.
There is no manifest or config file listing profiles: the directories under
~/.azure-profiles/ are the only registry. Adding a profile is "switch to it and log in",
never "edit a file". When revisiting profile storage or discovery, read
docs/adr/az-cli-profile-isolation.md for the
rejected alternatives and verified failure experiments; it is historical rationale, not
required reading for ordinary profile changes.
The active profile is always visible in the prompt as an az:<name> tag (magenta), or
az:default (green) when AZURE_CONFIG_DIR is unset — driven purely by that env var, so
it costs nothing and never spawns az.
-
Persistence: each switch writes the name to the
~/.azure-active-profilestate file. On startup the profile callsRestore-AzActiveProfile(Phase 1), whose budget is exactly one state-file read — no directory enumeration, no JSON parsing (all discovery lives in the picker). Adefault, missing, empty, or invalid state actively unsetsAZURE_CONFIG_DIR(it never merely skips), so an inherited value can't leak in. -
Extensions are shared, not per-profile:
Restore-AzActiveProfilesetsAZURE_EXTENSION_DIRunconditionally to~/.azure/cliextensions. Without it a non-default profile sees zero extensions andaz devops/az graphstop existing. -
The picker never runs
az: bareazslists the profiles throughfzf, previewing each one's identity read straight out of itsazureProfile.json. Works while logged out; a profile with no login shows theaz loginhint. -
prrfollows the active profile: switching clears the session-cached ADO bearer token ($global:__AdoAccessToken), soprr/Invoke-AdoPrReview(Phase 2b) re-authenticates against whichever account is now active. Runprrunder the profile that owns the PRs. - Each switch announces the target profile immediately, then makes a best-effort
az account showto append the active user (or anaz loginhint). The announce never blocks or throws ifazis slow, absent, or logged out; it never runsaz loginfor you.
- Create
~/.azure-profilesand move the old dir into it:~/.azure-personal→~/.azure-profiles/personal. - For every other account, run
azs <name>(which creates an empty dir) and thenaz loginonce inside it. Seed the dirs empty — copying an existing config dir would cache two identities in one profile, which is exactly what this design exists to prevent. - Leave
~/.azureas-is: on Windows it is a single case-insensitive directory shared with Azure PowerShell (itsAzureRmContext.jsonsits beside az'sazureProfile.json), so it is never swapped, junctioned, or moved. Just stop logging the CLI into it. - The existing
~/.azure-active-profilevalue maps onto~/.azure-profiles/<same name>. Runazsonce after migrating so the state file lands on a profile that exists.
There is a single shared profile — no per-machine variants. Machine-specific behaviour (work git identity, Azure subscription) is handled at other layers:
- Git identity via
[includeIf]ingit/gitconfig - Az context loaded async in
Profile/Set-Prompt.ps1
./setup.ps1 -Module powershellStubs the profile into the pwsh 7 and VSCode profile locations (both under
~/Documents/PowerShell/) and junctions ~/Documents/PowerShell/Profile →
powershell/Profile/. Windows PowerShell 5.1 is not targeted — the shared
profile is #Requires -Version 7. The installer warns about (but never deletes)
orphaned PS5 stubs under ~/Documents/WindowsPowerShell/ from older runs so you
can remove them manually.
Source: powershell/README.md in the dotfiles repo.
Getting started
Core (Windows)
Cross-platform
Agents & AI
Reference
Legacy (Linux)
Troubleshooting