A first-class terminal on Windows. The native Windows host β PowerShell, Windows Terminal, scoop/winget, and the WSL2 bridge.
pwsh Β· terminal Β· psmux Β· wsl2
The native-host layer of my multi-OS dotfiles fleet. This repo owns the Windows host: PowerShell as the daily-driver shell, Windows Terminal, the scoop/winget package layer, and the bridge to my Linux distros running under WSL2.
It deliberately does not configure WSL distros. Core and Kali configure themselves from their own repos inside WSL. This repo's job is to make the host excellent and then get out of the way.
ββ--βββββββββββββββββββββββββββββββββββββββββ
this repo β β Windows host: pwsh + Terminal + scoop β
β + psmux (native tmux) + WSL bridge β
βββββββββββββββββββββ¬ββββββββββββββββββββββββ
β wsl
βββββββββββββββββββββΌββββββββββββββββββββββ--ββ
other repos ββ WSL2: Core / Kali (zsh/tmux/...) β
βββββββββββββββββββββββββββββββββββββββββββββββ
powershell/profile.ps1 is symlinked to $PROFILE and dot-sources fragments
in order:
- core/ β cross-fleet aliases, prompt (starship), history (PSReadLine), fuzzy nav (fzf/zoxide), update nudge, 1Password helpers, general helpers. Same feel as zsh everywhere.
- os/ β Windows-native: scoop/winget helpers, clipboard, the WSL bridge, psmux multiplexer helper, scheduled maintenance.
- local.ps1 β machine-specific, gitignored. Secrets and per-host overrides.
There is intentionally no offensive layer here. The offensive role is unique to the Kali station, same as everywhere else in the fleet β this repo is a productivity/host repo only.
New fragments load automatically β the loader globs each layer directory in
name order, so dropping a core/NN-name.ps1 or os/NN-name.ps1 in is all it
takes (no install.ps1 change needed).
Requires PowerShell 7 (pwsh) and Developer Mode enabled (or run
elevated) so symlinks work.
From a fresh box, bootstrap.ps1 clones the repo and runs the installer for you
(it needs git and pwsh 7+):
irm https://raw.githubusercontent.com/dotgibson/dotfiles-Windows/main/bootstrap.ps1 | iexKnobs (all optional env vars): DOTFILES_DIR (clone location), DOTFILES_REF
(pin a commit/tag for a reproducible setup), DOTFILES_REPO (your fork's URL),
DOTFILES_BOOTSTRAP_ARGS (extra install.ps1 args, e.g. '-SkipPackages').
Integrity-gated β verify the script against the pinned hash before running it
(piping straight to iex trusts whatever the URL serves):
$b = irm https://raw.githubusercontent.com/dotgibson/dotfiles-Windows/main/bootstrap.ps1
$h = [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData(
[Text.Encoding]::UTF8.GetBytes(($b -replace "`r`n","`n")))).ToLower()
if ($h -eq '7d6855b163c8e9179e1b137c410416bfa0b41c95f94b768732cf2bf22e6292c6') { $b | iex }
else { Write-Error "bootstrap.ps1 hash mismatch: $h" }bootstrap.ps1 never pipes a further network script into iex itself: it clones
over git (pin DOTFILES_REF for an exact, content-addressed checkout) and hands
off to install.ps1, where scoop's installer stays behind the existing
DOTFILES_SCOOP_SHA256 gate.
git clone <your-remote>/dotfiles-Windows.git
cd dotfiles-Windows
.\install.ps1 # packages + symlinks
# or, to only re-wire links:
.\install.ps1 -SkipPackages
# preview every change without touching anything:
.\install.ps1 -DryRun
# unattended (CI / no prompts):
.\install.ps1 -NonInteractive
.\install.ps1 -Help # full option listThe installer is idempotent (re-running only fixes what drifted), prompts before backing up a real file it's about to replace, and on the first run asks for your git name/email. To reverse it:
.\uninstall.ps1 # remove only the symlinks that point into this repo
.\uninstall.ps1 -RestoreBackups # also restore the newest *.bak per link
.\uninstall.ps1 -DryRunThen:
- Open a new PowerShell window to load the profile.
- Set your name/email in
~/.gitconfig.local. - Review
~/.wslconfigand runwsl --shutdownto apply mirrored networking. - (Optional)
maint-installto register the daily maintenance task. - (Optional)
muxto drop into a persistent psmux session.
- "cannot be loaded ... not digitally signed" β execution policy.
install.ps1now setsRemoteSignedfor your user and unblocks the repo files automatically, but if you hit this before the script can even start, do it once by hand:Set-ExecutionPolicy RemoteSigned -Scope CurrentUser, thenGet-ChildItem -Recurse -File | Unblock-File. IfGet-ExecutionPolicy -ListshowsMachinePolicy/UserPolicyset, that's Group Policy (managed/gov machines) and you can't override it yourself. - Prefer
git cloneover downloading an archive. Cloned files don't carry the "Mark of the Web," so the unblock step never comes up. - Pinning the bootstrap's third-party fetches (supply chain). The scoop
installer can be integrity-gated: set
DOTFILES_SCOOP_SHA256to the expected hash and the installer aborts on mismatch. The psmuxppmplugin clone can be pinned to an exact commit/tag withDOTFILES_PPM_REF; otherwise it tracks the default branch, and the installer verifies the expectedppm\folder is present before copying it. - Use PowerShell 7 (
pwsh), not Windows PowerShell 5.1. The bootstrap tolerates 5.1 and warns you, but the profile targets the pwsh path β do daily work in pwsh. - A package fails to install? The installer logs it and keeps going, then
prints a
skipped:summary. Re-run.\install.ps1to retry (installed apps are skipped). Some scoop packages withpersistblocks (e.g.btop-lhm) can trip on a leftover config from an interrupted run βscoop uninstall <name>then reinstall, or drop it frompackages\scoopfile.json. - Garbled glyphs or unwanted colour? Output honours
NO_COLOR(set it to strip all colour) andDOTFILES_ASCII=1(swap theβ β β β’glyphs for ASCII on a legacy codepage console). Both also apply toinstall.ps1,dotfiles-doctor, anddothelp. - Something half-loaded? Run
dotfiles-doctorβ the Profile fragments check reports any fragment that failed to load, anddotfiles-doctor -Fixauto-remediates the common issues (execution policy, missing links, modules on OneDrive).
dotfiles-Windows/
βββ install.ps1 bootstrap (env var, packages, symlinks)
βββ uninstall.ps1 remove repo symlinks (optionally restore backups)
βββ .githooks/pre-commit runs tests/Invoke-Validation.ps1 before commits
βββ powershell/
β βββ profile.ps1 loader (coreβosβlocal)
β βββ core/ aliases, shared lib, tool inits, functions, completions, help
β β 00-aliases 05-lib 08-git-safety 10-tools 15-update 20-functions 25-television
β β 40-op 45-crypto 50-completions 55-help 57-health-nudge
β βββ os/ windows helpers + wsl bridge + psmux + maint + doctor
β β 30-windows 31-wsl-bridge 32-psmux 33-psmux-pill 40-maint 45-doctor
β βββ local.ps1.example copy to local.ps1 (gitignored)
βββ maint/Maintenance.ps1 unattended daily maint runner (Task Scheduler)
βββ windows-terminal/settings.json
βββ starship/starship.toml same prompt as the fleet (tokyonight-storm)
βββ git/ (.gitconfig, .gitignore_global)
βββ ssh/config hardened (no ControlMaster on Win OpenSSH)
βββ psmux/psmux.conf native host tmux (psmux), symlinked to ~/.config/psmux/
β psmux.reset.conf scripts/ (keybinds split out + popup helper scripts)
βββ nvim/ symlinked to %LOCALAPPDATA%\nvim (vendor Core)
βββ wsl/windows.wslconfig.example canonical host WSL2 config (mirrored net)
βββ packages/ (scoopfile.json, winget.json, Install-Packages.ps1)
βββ docs/ (TOOLS.md, PORTING-NOTES.md)
| Command | Does |
|---|---|
ll / la / lt |
eza listings (long / all / tree) |
cat / catp |
bat (no-pager / paged) |
z foo |
zoxide jump; cd is rebound to z |
Ctrl+t / Ctrl+r |
fzf file picker / history search |
http / dns / gmd |
xh / doggo / glow (guarded if installed; gmd renders markdown) |
lg |
lazygit |
serve [port] [-Local] |
HTTP server in the CWD; prints the host LAN URL (-Local binds localhost only) |
fif <term> / fbr |
find-in-files / fuzzy git-branch checkout |
tmux / psmux / pmux |
native host multiplexer (psmux; reads ~/.config/psmux/psmux.conf) |
mux [session] |
attach-or-create a psmux session (defaults to main) |
psmux-pill-enable / psmux-pill-disable |
enable/disable the file-backed operator/VPN status pill (off the render path) |
up / up -y / up -n |
apply scoop+winget updates (-y auto-confirms winget; -n/-Preview lists only) |
update-check |
force the "updates available" check now |
maint-install [HH:MM] |
register the daily maintenance task |
maint-run / maint-log -f / maint-status |
run now / follow log / next-run |
opsecret / optoken / openv / opssh |
1Password CLI helpers |
kali / cdwsl |
jump into Kali / into Kali at the current dir |
wsls / hostip |
WSL distro status / host LAN IP |
tools |
open the host tool docs |
dothelp [filter] / dothelp -i |
in-shell command index (-i = fzf picker, copies the pick) |
dotfiles-doctor [-Fix] |
health-check the setup, and optionally auto-remediate |
This repo is the host/productivity layer only β no offensive tooling is
installed or configured here. That role lives on the Kali station (its own
repo, inside WSL). The bridge functions (kali, cdwsl) are just how you get
there from the host shell. psmux gives you tmux-style multiplexing on the host;
the genuine tmux for Linux work still lives in WSL.
# one-time: provision the same test toolchain CI uses (Pester + PSScriptAnalyzer,
# pinned to the CI versions; idempotent). A test gates these against ci.yml drift.
pwsh -NoProfile -File tests/Install-DevDeps.ps1
# fast, dependency-free gate (syntax + JSON/manifests + module pins + editorconfig):
pwsh -NoProfile -File tests/Invoke-Validation.ps1
# ^ also runs PSScriptAnalyzer automatically IF it's installed (errors gate the
# run); it's skipped cleanly when absent, so the gate stays Gallery-free offline.
# full behavioral suite (needs Pester 5):
Invoke-Pester -Path testsinstall.ps1 wires core.hooksPath = .githooks, so the validator runs on every
commit (bypass a single one with git commit --no-verify). CI mirrors this: a
fast Linux gate, then PSScriptAnalyzer + Pester (with a coverage gate) on Windows
β heavy jobs are skipped for docs-only changes. Actions are pinned to commit
SHAs and kept current by Dependabot. See CHANGELOG.md for the
DX/UX overhaul history.