π€ ZSHAND User Configuration GuideΒΆ
π This guide covers how to configure your shell, where files go, how overrides work, and how to extend the framework with your own code.
π§ Philosophy
The framework is opinionated by default but fully overridable. You should never need to edit framework files β everything is customizable from your config directory.
π Config DirectoryΒΆ
All user configuration lives under a single XDG-compliant directory:
Override the location by setting ZSHAND_CONFIG_DIR in your environment before the shell starts (e.g., in ~/.zshenv).
π Directory StructureΒΆ
~/.config/zshand/
βββ π env.zsh Environment variables (auto-exported)
βββ π secrets.zsh API keys and tokens (chmod 600)
βββ π aliases.zsh Custom aliases (loaded after framework)
βββ π€οΈ path.txt Additional PATH entries (one per line)
βββ βοΈ config.toml Framework behavior settings
βββ π bin/ Personal scripts (added to $PATH)
βββ π οΈ functions/ Custom zsh functions
βββ β¨οΈ widgets/ Custom ZLE widgets
βββ πͺ¨ hooks/ Custom integration hooks
βββ βοΈ rc.d/ Core config overrides (numbered like core/)
βββ π init.d/ Pre-initialization scripts
βββ π post.d/ Post-initialization scripts
βββ π plugins/ Plugin configuration (e.g., P10k theme)
First-time setup
Run zsh setup.zsh to create this directory structure with helpful template files. You can also copy from samples/user-config/.
π Load OrderΒΆ
Understanding when each file loads helps you decide where to put things:
ββ Shell starts ββββββββββββββββββββββββββββββββββββββββββββββ
β β
β 1. π init.d/*.zsh Early overrides (before core) β
β 2. π env.zsh Environment variables β
β 3. π secrets.zsh API keys (auto-exported) β
β 4. βοΈ config.toml Framework behavior flags β
β 5. π€οΈ path.txt + bin/ PATH configuration β
β 6. βοΈ rc.d/*.zsh Core overrides (merged with core)β
β 7. βοΈ Framework core 02_vars β 04_path β ... β 90 β
β 8. πͺ¨ Framework hooks Tool integrations β
β 9. π οΈ functions/ Your custom functions β
β 10. β¨οΈ widgets/ Your custom widgets β
β 11. πͺ¨ hooks/ Your custom hooks β
β 12. π aliases.zsh Your aliases (can override) β
β 13. π post.d/*.zsh Final tweaks (after everything) β
β β
ββ Shell ready βββββββββββββββββββββββββββββββββββββββββββββββ
π Environment Variables (env.zsh)ΒΆ
Machine-specific environment variables. This file is sourced with set -a (auto-export), so every variable is automatically exported.
# env.zsh β Machine-specific environment variables
EDITOR="nvim"
BROWSER="firefox"
MACHINE_ID="workstation"
# Framework controls
ZSHAND_DEBUG=0
ZSHAND_LAZY_LOAD=1
ZSHAND_AUTO_FULL=1
# Tool-specific
DOCKER_HOST="unix:///run/user/1000/docker.sock"
π¬ The framework also writes
ZSHAND="/path/to/zshand"into this file during setup. Don't remove it.
π Secrets (secrets.zsh)ΒΆ
API keys, tokens, and sensitive credentials. Automatically secured with chmod 600 during setup. Never commit this file.
# secrets.zsh β Sensitive credentials (chmod 600)
GITHUB_TOKEN="ghp_xxxxxxxxxxxx"
OPENAI_API_KEY="sk-xxxxxxxxxxxx"
AWS_ACCESS_KEY_ID="AKIAXXXXXXXX"
AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxx"
CLOUDFLARE_API_TOKEN="xxxxxxxxxxxxxxxx"
Security
This file is protected by chmod 600 (owner-read-only). The framework re-enforces permissions on every startup via core/06_engine.zsh. Gitleaks scans also check that secrets never leak into the repo.
π Aliases (aliases.zsh)ΒΆ
Custom aliases loaded after framework aliases, so they can override:
# aliases.zsh β Custom aliases
alias ll='eza -lahF --icons'
alias gst='git status'
alias dc='docker compose'
alias k='kubectl'
alias tf='terraform'
# Override a framework alias
alias ls='eza --icons --group-directories-first'
π€οΈ PATH (path.txt)ΒΆ
Additional directories to prepend to $PATH, one per line. Lines starting with # are ignored.
# path.txt β Additional PATH entries
/opt/custom/bin
~/.cargo/bin
~/.npm-global/bin
~/.local/share/mise/shims
Your bin/ directory is also automatically added to $PATH.
βοΈ Framework Settings (config.toml)ΒΆ
TOML-based configuration for framework behavior:
# config.toml β Framework behavior
[general]
lazy_load = true # Defer non-critical hooks
auto_full = true # Auto-compile full bundle
health_check = true # Validate deps at startup
perf_budget = 100 # Max ms per file before warning
[notices]
quiet = false # Suppress non-critical notices
show_tips = true # Show tips on startup
[keybindings]
# Custom keybindings (loaded by startup/17_az_bindkey_from_toml.zsh)
"ctrl+g" = "aicmit" # AI commit widget
"ctrl+d" = "aidebug" # AI debug widget
π Pre-Initialization (init.d/)ΒΆ
Scripts that run before the framework loads. Use this for:
- Machine-specific overrides that must be set early
- Environment variables that affect framework behavior
- Workarounds that need to run before core logic
# init.d/00_machine_overrides.zsh
export ZSHAND_LAZY_LOAD=1
export ZSHAND_AUTO_FULL=1
# init.d/10_work_vpn.zsh
[[ -f /etc/vpn-active ]] && export HTTP_PROXY="http://proxy:8080"
Naming: Use NN_ prefix for load order (00 = first, 99 = last).
π Post-Initialization (post.d/)ΒΆ
Scripts that run after everything else. Use this for:
- Final overrides that need all framework functions available
- Customizations that depend on hooks being loaded
- Prompt tweaks that should be the absolute last thing
# post.d/00_final_env.zsh
# Set variables that depend on framework functions
export MY_PROJECT_DIR="$(git rev-parse --show-toplevel 2>/dev/null)"
# post.d/99_prompt_tweaks.zsh
# Final prompt customization (after P10k loads)
typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(dir vcs)
βοΈ Core Overrides (rc.d/)ΒΆ
Replace any framework core file by placing a file with the same basename in rc.d/. The framework merges both directories and user files win:
Framework: $ZSHAND/core/14_aliases.zsh β default
User: ~/.config/zshand/rc.d/14_aliases.zsh β loaded INSTEAD
Framework: $ZSHAND/core/10_options.zsh β default
User: ~/.config/zshand/rc.d/11_my_options.zsh β loaded ALONGSIDE (odd number)
Rules:
- Same basename β replaces framework file entirely
- Different basename β adds to load sequence (use odd numbers)
π οΈ Custom Functions (functions/)ΒΆ
Drop .zsh files here to add your own functions:
These are loaded after framework functions and can override them.
β¨οΈ Custom Widgets (widgets/)ΒΆ
ZLE widgets for keyboard shortcuts:
Bind in config.toml or post.d/:
πͺ¨ Custom Hooks (hooks/)ΒΆ
Integration hooks for tools not covered by the framework:
π Personal Scripts (bin/)ΒΆ
Executable scripts placed here are automatically added to $PATH:
Make them executable: chmod +x ~/.config/zshand/bin/my_script
π Complete Override (zshrc.zsh)ΒΆ
For total control, create ~/.config/zshand/zshrc.zsh. This completely replaces the framework β nothing else loads:
# zshrc.zsh β Complete framework replacement
# WARNING: This disables ALL framework features
source ~/.config/zshand/env.zsh
source ~/.config/zshand/aliases.zsh
# ... your custom setup
Nuclear option
This is a full replacement β no framework code runs at all. Only use this if you need complete control over your shell setup.
π Key Environment VariablesΒΆ
| Variable | Default | Purpose |
|---|---|---|
ZSHAND_CONFIG_DIR | ~/.config/zshand | Your config directory |
ZSHAND_CACHE_DIR | ~/.cache/zshand | Compiled bundles and cache |
ZSHAND_LOG_DIR | ~/logs/zshand | Log files |
ZSHAND_DEV_MODE | 0 | Development mode (no bundles) |
ZSHAND_AUTO_FULL | 0 | Auto-compile full bundle |
ZSHAND_DEBUG | 0 | Verbose debug output |
ZSHAND_LAZY_LOAD | 0 | Defer non-critical hooks |
ZSHAND_SAFE_MODE | 0 | Emergency recovery |
ZSHAND_PERF_BUDGET | 100 | Max ms per file |
π Related DocumentsΒΆ
| Document | Purpose |
|---|---|
| ποΈ ARCHITECTURE.md | Override system and boot sequence |
| π¨ STYLE_GUIDE.md | Conventions for custom code |
| π HEADER_STANDARD.md | How to document your custom files |
| π samples/user-config/README.md | Example templates |