بنقرة واحدة
powershell-config
Conventions for the modular, fast-loading PowerShell profile in this dotfiles repo
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Conventions for the modular, fast-loading PowerShell profile in this dotfiles repo
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
Idempotent, self-bootstrapping install scripts for Windows and Unix
Load contract, thin-stub pattern, and module boundaries for the dotfiles repo
How to add a `dotfiles` CLI subcommand with Windows/Unix parity
How to declare packages and register tools across winget/scoop/apt in the dotfiles repo
POSIX-first bash/zsh configuration and Windows parity rules for the dotfiles repo
استنادا إلى تصنيف SOC المهني
| name | powershell-config |
| description | Conventions for the modular, fast-loading PowerShell profile in this dotfiles repo |
| domain | powershell |
| confidence | high |
| source | manual |
The PowerShell profile is split into small, focused dot-sourced files under powershell/. The system
$PROFILE contains only a two-line bootstrap stub (written once by bootstrap/install.ps1):
$env:DOTFILES = "<repo-root>"
. "$env:DOTFILES\powershell\profile.ps1"
profile.ps1 is the sole entry point for all real configuration. It is the only file you ever edit
when changing the load order or adding a new top-level concern.
Load order (profile.ps1):
aliases.ps1 — Linux-style wrappers and git shortcutspsreadline.ps1 — PSReadLine options and key bindingsprompt.ps1 — Oh My Posh initialisation with full fallback chaincompletions.ps1 — module imports (Terminal-Icons, posh-git, zoxide, PSFzf) and argument completersmodules/*.ps1 — drop-in files, dot-sourced alphabetically (auto-discovered, no registration)bin/ prepended to $env:PATH (idempotent — checks for existing entry first)All temp variables ($_dot, $_modulesDir, $_binDir, $_vtSupported, $_ompTheme) are cleaned
up with Remove-Variable … -ErrorAction SilentlyContinue at the end of each file so they never
pollute the session.
Alias source of truth: shared/aliases.json. Trinity implements in powershell/aliases.ps1;
Tank implements the same catalog in shell/common.sh. Every alias visible to the user should have
a matching entry in shared/aliases.json before it is implemented in either shell.
Every external tool call is wrapped in a Get-Command X -ErrorAction SilentlyContinue check.
A missing binary must never throw, never warn, and never slow down startup.
if (Get-Command eza -ErrorAction SilentlyContinue) {
function ls { eza --icons --group-directories-first @args }
} else {
function ls { Get-ChildItem @args } # built-in fallback
}
The same guard applies to module imports:
if (Get-Module Terminal-Icons -ListAvailable -ErrorAction SilentlyContinue) {
Import-Module Terminal-Icons -ErrorAction SilentlyContinue
}
And to Invoke-Expression-based initialisers:
if (Get-Command zoxide -ErrorAction SilentlyContinue) {
Invoke-Expression (& { (zoxide init powershell | Out-String) })
}
Use a function, never Set-Alias, whenever the alias must forward arguments or flags. @args
is the splatting idiom that passes all positional and named arguments through transparently.
function grep { rg @args } # forwards -i, -l, --glob, etc.
function cat { bat --style=plain @args }
Use Set-Alias only for zero-argument identity mappings (e.g. Set-Alias vi vim) — but prefer
functions even there if future argument support is plausible.
Theme resolution uses $env:DOTFILES, not any user-specific path. The chain is:
$env:DOTFILES\powershell\themes\dotfiles.omp.json$env:POSH_THEMES_PATH\jandedobbeleer.omp.jsonprompt function (if oh-my-posh is not installed at all)if (Get-Command oh-my-posh -ErrorAction SilentlyContinue) {
$_ompTheme = if ($env:DOTFILES -and (Test-Path "$env:DOTFILES\powershell\themes\dotfiles.omp.json")) {
"$env:DOTFILES\powershell\themes\dotfiles.omp.json"
} elseif ($env:POSH_THEMES_PATH -and (Test-Path "$env:POSH_THEMES_PATH\jandedobbeleer.omp.json")) {
"$env:POSH_THEMES_PATH\jandedobbeleer.omp.json"
} else { $null }
if ($_ompTheme) {
oh-my-posh init pwsh --config $_ompTheme | Invoke-Expression
} else {
oh-my-posh init pwsh | Invoke-Expression
}
Remove-Variable _ompTheme
} else {
function prompt {
$branch = ''
if (Get-Command git -ErrorAction SilentlyContinue) {
$ref = git symbolic-ref --short HEAD 2>$null
if ($ref) { $branch = " [$ref]" }
}
"$($executionContext.SessionState.Path.CurrentLocation)$branch`n> "
}
}
ListView requires a VT-capable console with a known window width. Wrap it in a check so it
degrades to InlineView in non-interactive contexts (test runners, pwsh -NonInteractive, etc.):
if (-not (Get-Module PSReadLine -ErrorAction SilentlyContinue)) { return }
if (-not [System.Environment]::UserInteractive) { return }
$_vtSupported = $Host.UI.RawUI -and $Host.UI.RawUI.WindowSize.Width -gt 0
Set-PSReadLineOption -PredictionSource HistoryAndPlugin
if ($_vtSupported) {
Set-PSReadLineOption -PredictionViewStyle ListView
}
Remove-Variable _vtSupported -ErrorAction SilentlyContinue
To add a self-contained feature (e.g. dotfiles-agent.psm1 or a new helper script), drop a .ps1
file in powershell\modules\. It is automatically dot-sourced in alphabetical order after the
four core files. No registration or changes to profile.ps1 are needed.
# profile.ps1 — modules loop (do not change this)
$_modulesDir = "$_dot\powershell\modules"
if (Test-Path $_modulesDir) {
Get-ChildItem -Path $_modulesDir -Filter '*.ps1' |
Sort-Object Name |
ForEach-Object { . $_.FullName }
}
bin/ PATH prepend (idempotent)$_binDir = "$_dot\bin"
if ((Test-Path $_binDir) -and ($env:PATH -notlike "*$_binDir*")) {
$env:PATH = "$_binDir$([IO.Path]::PathSeparator)$env:PATH"
}
The check $env:PATH -notlike "*$_binDir*" prevents duplicates on reload.
When adding a new alias, add it to shared/aliases.json first, then implement in
powershell\aliases.ps1 (Trinity) and shell\common.sh (Tank).
"myalias": {
"_note": "Human-readable description of what it does",
"windows": "preferred-cmdlet | fallback-cmdlet",
"unix": "preferred-tool | fallback-tool"
}
Omit windows or unix if the alias is platform-specific. Prefix-only metadata keys with _
(e.g. _comment, _owner) — these are skipped by tooling.
# aliases.ps1
if (Get-Command eza -ErrorAction SilentlyContinue) {
function ls { eza --icons --group-directories-first @args }
function ll { eza -la --icons --group-directories-first @args }
function la { eza -a --icons @args }
function l { eza --icons @args }
function lt { eza --tree --level=2 --icons @args }
} else {
function ls { Get-ChildItem @args }
function ll { Get-ChildItem -Force @args }
function la { Get-ChildItem -Force @args }
function l { Get-ChildItem @args }
function lt { Get-ChildItem -Recurse @args }
}
$_modulesDir = "$_dot\powershell\modules"
if (Test-Path $_modulesDir) {
Get-ChildItem -Path $_modulesDir -Filter '*.ps1' |
Sort-Object Name |
ForEach-Object { . $_.FullName }
}
Drop powershell\modules\my-feature.ps1 and it loads automatically — no edits to profile.ps1.
if (Get-Command oh-my-posh -ErrorAction SilentlyContinue) {
$_ompTheme = if ($env:DOTFILES -and (Test-Path "$env:DOTFILES\powershell\themes\dotfiles.omp.json")) {
"$env:DOTFILES\powershell\themes\dotfiles.omp.json"
} elseif ($env:POSH_THEMES_PATH -and (Test-Path "$env:POSH_THEMES_PATH\jandedobbeleer.omp.json")) {
"$env:POSH_THEMES_PATH\jandedobbeleer.omp.json"
} else { $null }
if ($_ompTheme) { oh-my-posh init pwsh --config $_ompTheme | Invoke-Expression }
else { oh-my-posh init pwsh | Invoke-Expression }
Remove-Variable _ompTheme
} else {
function prompt {
$branch = ''
if (Get-Command git -ErrorAction SilentlyContinue) {
$ref = git symbolic-ref --short HEAD 2>$null
if ($ref) { $branch = " [$ref]" }
}
"$($executionContext.SessionState.Path.CurrentLocation)$branch`n> "
}
}
if (-not (Get-Module PSReadLine -ErrorAction SilentlyContinue)) { return }
if (-not [System.Environment]::UserInteractive) { return }
$_vtSupported = $Host.UI.RawUI -and $Host.UI.RawUI.WindowSize.Width -gt 0
Set-PSReadLineOption -PredictionSource HistoryAndPlugin
if ($_vtSupported) { Set-PSReadLineOption -PredictionViewStyle ListView }
if ((Get-Command fzf -ErrorAction SilentlyContinue) -and
(Get-Module PSFzf -ListAvailable -ErrorAction SilentlyContinue)) {
Import-Module PSFzf -ErrorAction SilentlyContinue
Set-PsFzfOption -PSReadlineChordProvider 'Ctrl+t'
Set-PsFzfOption -PSReadlineChordReverseHistory 'Ctrl+r'
}
# BAD — throws on a fresh machine; breaks every startup
oh-my-posh init pwsh --config "$env:DOTFILES\powershell\themes\dotfiles.omp.json" | Invoke-Expression
Always wrap with Get-Command oh-my-posh -ErrorAction SilentlyContinue first.
# BAD — clobbers the built-in and cannot forward arguments
Set-Alias ls eza
Use a function instead so @args forwarding works and the built-in can be reached if needed.
# BAD — breaks on any machine that is not josecorral's
oh-my-posh init pwsh --config "C:\Users\josecorral\poshv3.json" | Invoke-Expression
All paths must be relative to $env:DOTFILES. Never embed a username or absolute drive path.
# BAD — network call during startup; blocks the shell for seconds
$latestRelease = Invoke-RestMethod "https://api.github.com/repos/..."
Profile code must be synchronous and local-only. Anything that touches the network or disk heavily should be a lazy-loaded module or a separate command, not part of the dot-sourced chain.
# BAD — PowerShell-only alias, no catalog entry, Tank never sees it
function gco { git checkout @args }
Add the entry to shared/aliases.json with _note, windows, and unix fields. Then implement
in powershell\aliases.ps1 (Trinity) and shell\common.sh (Tank). Single source of truth prevents
catalog drift between shells.
# BAD — profile.ps1 becomes a dumping ground
# profile.ps1
function my-new-feature { ... }
Add a file to powershell\modules\ instead. profile.ps1 only ever contains the load-order
skeleton and the bin/ PATH prepend — never feature logic.
# BAD — Import-Module prints an error if the module is not installed
Import-Module Terminal-Icons
Always guard with Get-Module Terminal-Icons -ListAvailable -ErrorAction SilentlyContinue first,
then import with -ErrorAction SilentlyContinue.