Personal dotfiles managed with chezmoi. Currently macOS only, but structured for eventual Linux support.
New Mac setup is documented in MACOS_SETUP.md.
# Apply changes
chezmoi apply
# Edit a file (automatically re-applies)
chezmoi edit ~/.zshrc
# Add new file to tracking
chezmoi add ~/.config/foo/bar.conf
# cd into chezmoi config directory
chezmoi cd
# See what would change
chezmoi diff
# Update external dependencies (fzf-tab)
chezmoi update
# Install packages from Brewfile
brew bundle --global
# Remove Brew packages that are not in Brewfile
brew bundle cleanup --force --global
# One-line updates for everything
topgrade~/.local/share/chezmoi/
├── .chezmoiroot # Points chezmoi to home/
├── home/ # Source files → ~/
│ ├── .chezmoiexternal.toml # External git repos (fzf-tab)
│ ├── .chezmoiignore # Platform-specific ignores
│ ├── .chezmoiscripts/ # Scripts run on `chezmoi apply`
│ ├── dot_* # Files → ~/.*
│ ├── dot_config/ # → ~/.config/
│ └── Library/ # → ~/Library/ (macOS)
├── espanso/ # Symlink target (shared config)
├── vscode/ # Symlink target (shared config)
├── macos/ # Declarative macOS preferences
│ ├── defaults/ # App preferences (TOML)
│ ├── default-apps.toml # File associations
│ └── fs-flags.toml # Filesystem flags
└── utils/ # Python scripts for applying configs
Create ~/.config/chezmoi/chezmoi.yaml:
data:
user:
name: "Your Name"
email: "you@example.com"
machine:
role: "personal" # or "work"
type: "laptop" # or "desktop"Required for templates. The Brewfile and other .tmpl files use these values.
The Problem: VSCode/Cursor/Espanso live in different locations per platform:
- macOS:
~/Library/Application Support/ - Linux:
~/.config/
The Solution: Source files live at repo root (vscode/, espanso/), with platform-conditional symlinks in home/.
vscode/settings.json ← Single source of truth
↓
macOS: ~/Library/Application Support/Code/User/settings.json → symlink
Linux: ~/.config/Code/User/settings.json → symlink
To edit VSCode/Cursor/Espanso configs: Edit files in vscode/ or espanso/, not the symlinks.
Preferences are defined declaratively in TOML and applied automatically on chezmoi apply.
macos/defaults/*.toml → utils/macos-apply-defaults.py → defaults write
Each TOML file defines preferences for one app:
description = "Finder"
kill = ["Finder"] # Restart after applying
[data."com.apple.finder"]
AppleShowAllFiles = true
FXDefaultSearchScope = "SCcf"- Create
macos/defaults/myapp.toml - Find the domain:
defaults domains | tr ',' '\n' | grep -i myapp - Read current settings:
defaults read com.example.myapp - Run
chezmoi apply
# Dry-run (see what would change)
python3 utils/macos-apply-defaults.py macos/defaults/ --dry-run -v
# Verbose apply
python3 utils/macos-apply-defaults.py macos/defaults/ -vvFile: home/dot_Brewfile.tmpl
Organized by platform/role/machine type with chezmoi conditionals.
brew bundle --global # Install all
brew bundle cleanup --global # Remove unlistedFile: home/dot_config/mise/config.toml
mise install # Install all tools
mise use -g node # Add new tool
mise current # Check versionsFile: home/.chezmoiexternal.toml
Currently manages fzf-tab. Update with chezmoi update.
- Edit or create
macos/defaults/<app>.toml chezmoi apply
chezmoi add ~/.config/some-app/config.yaml- Edit
vscode/settings.jsonorvscode/keybindings.json chezmoi apply
Edit espanso/match/base.yml or espanso/match/utils.yml, then chezmoi apply.
Note: Local machine-specific snippets are currently broken.
j→z(zoxide jump)t→trash(safe delete)rm→ disabled (usedelor/bin/rmfor hard delete)
- fzf: Fuzzy finder (Ctrl+R for history)
- zoxide: Smart directory jumping (
z <partial>) - mise: Dev tool version management
- eza: Modern
lsreplacement
topgrade # Updates everything (Homebrew, mise, system, etc.)