Personal configuration files and bootstrap scripts for macOS. Managed with GNU Stow.
# Full new machine setup
./install.sh
# Re-stow dotfiles only (no installs)
stow -t ~ -d ~/Projects/_whaleen/dotfiles --restow zsh bash git npm \
ghostty cursor gh yabai skhd sketchybar btop yazi lazygit opencode lsd \
warp claude gemini scriptsEach top-level directory is a stow package. The tree inside each package mirrors $HOME.
dotfiles/
zsh/ .zshrc, .zshenv, .config/whaleen-fzf.sh
bash/ .bash_profile, .profile
git/ .gitconfig, .config/git/ignore
npm/ .npmrc
ghostty/ .config/ghostty/config
cursor/ .config/cursor/settings.json, .cursor/extensions/whaleen/
gh/ .config/gh/config.yml
yabai/ .config/yabai/yabairc
skhd/ .config/skhd/skhdrc
sketchybar/ .config/sketchybar/sketchybarrc, items/, helpers/
lazygit/ .config/lazygit/config.yml
btop/ .config/btop/btop.conf, .config/btop/themes/whaleen.theme
yazi/ .config/yazi/yazi.toml, .config/yazi/theme.toml
lsd/ .config/lsd/colors.yaml
opencode/ .config/opencode/package.json
warp/ .warp/themes/Whaleen.yaml
claude/ .claude/CLAUDE.md, .claude/settings.json
gemini/ .gemini/settings.json
scripts/ .local/bin/cert
Palettes live in theme/palettes/dark.sh and theme/palettes/light.sh. Running generate.sh stamps every tool config from templates using {{VAR}} substitution.
# Edit a palette
$EDITOR theme/palettes/dark.sh
# Regenerate all tool configs
./theme/generate.sh --palette=dark # or light / all
# Auto-switch based on current macOS appearance
./theme/generate.sh --palette=autoGenerated configs: Ghostty, Warp, Yazi, btop, fzf, lsd, Cursor, Sketchybar, Lazygit, Pemguin, Den.
Ghostty and Den configs are written directly to their live locations (not stowed). All other generated configs are in their stow packages and pick up on the next stow run.
A LaunchAgent watches ~/Library/Preferences/.GlobalPreferences.plist and calls theme-switch.sh auto whenever macOS appearance changes. State is tracked at ~/.local/state/whaleen-theme to debounce spurious triggers.
# Install or reinstall the LaunchAgent
./theme/generate.sh --palette=allpm (pemguin) and den both detect changes to their theme config files within ~250ms and update live without restart. The workflow:
- Edit
theme/palettes/dark.sh - Run
./theme/generate.sh --palette=dark - Watch both TUIs update immediately
yabai + skhd with partial SIP disabled for instant space switching.
ctrl + 1-9— switch spaces instantly (no animation) viayabai -m space --focus- yabai scripting addition loaded via sudoers entry at
/private/etc/sudoers.d/yabai
After a fresh install, set up the sudoers entry:
echo "$(whoami) ALL=(root) NOPASSWD: sha256:$(shasum -a 256 $(which yabai) | cut -d ' ' -f 1) $(which yabai) --load-sa" | sudo tee /private/etc/sudoers.d/yabai- Create a directory for the package.
- Inside it, recreate the path from
$HOMEto the config file. - Add the package name to
STOW_PACKAGESininstall.sh. - Run
stow -t ~ <package>.
install.sh— Full bootstrap (Homebrew, nvm, Rust, Solana, stow, macOS defaults)theme/generate.sh— Regenerate all tool themes fromtheme/palettes/theme/theme-switch.sh— Called by LaunchAgent on appearance change
Brewfile— Homebrew packages (taps, formulae, casks, vscode extensions, cargo crates)npm-global.txt— npm global packagescargo-global.txt— Cargo crates