Appearance
Shell
Fish is the primary interactive shell on Unix-like hosts, Bash is the small fallback, and PowerShell is the native Windows peer. The goal is shared muscle memory for history, movement, prompt shape, and completion without forcing every shell to behave identically.
History
| Key | Bash/readline | Fish | PowerShell |
|---|---|---|---|
Up | Prefix history search | Atuin workspace search when available | Prefix history search |
Down | Prefix history search forward | Fish forward search/pager behavior | Prefix history search forward |
Ctrl-P | Prefix history search | Prefix history search | Prefix history search |
Ctrl-N | Prefix history search forward | Prefix history search forward | Prefix history search forward |
Ctrl-R | Atuin or reverse search | Atuin global search | Atuin or reverse search |
Readline includes /etc/inputrc first so host defaults such as terminal key sequences stay available, then the managed .inputrc overrides the keys this repo owns. Atuin owns broad interactive search where it is installed. The plain prefix search bindings remain local shell history controls, which keeps a fast path for repeating commands that start with the text already typed.
Editing
Tab uses menu-style completion where the shell supports it. Ctrl-Left and Ctrl-Right are the managed word movement contract for Bash/readline and PowerShell, with Fish using its standard terminal bindings. Delete remains an explicit forward-delete key in readline and PSReadLine. Bash enables bracketed paste through readline; Fish and PSReadLine keep their native paste behavior.
Prompt
Starship owns prompt rendering in all managed shells. The active prompt shows host, directory, Git and tool modules, and command duration when relevant. The clock module stays disabled; terminal scrollback already provides enough temporal context, and the extra timestamp made the prompt noisier across terminal emulators.
Fish intentionally enables Starship's transient prompt support: completed prompt lines collapse to the prompt character so scrollback stays compact. Bash and PowerShell keep completed prompts expanded. This difference is deliberate; the shared contract is that the active prompt shows the same operational context and Fish keeps history visually quieter after a command is submitted.
Project Environments
Mise and direnv have separate jobs:
text
mise tool versions and PATH
direnv project env vars, secret pointers, feature flags, local exportsProject tool versions belong in .mise.toml or language-native project files that mise understands. Direnv .envrc files should load local env data and set project variables without activating tools or rewriting PATH.
Good .envrc examples:
bash
source_up_if_exists .envrc.shared
dotenv_if_exists .env.local
export API_BASE_URL=http://127.0.0.1:3000
export FEATURE_PREVIEW=1
export RUFF_CACHE_DIR="$(project_cache_dir ruff)"
layout_tmpFor nested project directories, keep shared non-secret exports in an ancestor .envrc.shared and call source_up_if_exists .envrc.shared from the nested .envrc. Keep machine-local or secret values in .env.local, which should remain untracked.
Avoid using .envrc for tool or PATH activation:
bash
use mise
eval "$(mise activate bash)"
PATH_add ~/.local/share/mise/shims
layout pythonIf a project needs a different Node, Python, uv, pnpm, Ruff, or CLI version, pin it in .mise.toml and let the shell's mise activation own the resulting PATH.
Local Overrides
Machine-local shell quirks belong in ~/.config/dotfiles/local.d/, which is ignored by chezmoi. Bash, Fish, and PowerShell source their matching local files last, after managed startup has finished.
Interactive shells load all matching shell files in lexical order. Noninteractive startup loads only files that include the exact marker # dotfiles: noninteractive-safe, so script-safe environment tweaks can be kept separate from interactive aliases and functions.
See local overrides for the file names and examples.