Machine-specific dotfiles managed with Chezmoi.
Cross-platform (works everywhere):
- Shell: Zsh framework with Starship prompt
- Editors: Neovim (LazyVim), Vim
- Terminal: Tmux, Yazi file manager
- CLI tools: Atuin shell history, Git configs
- AI coding: pi-agent skills + config (conditional — see pi-agent Configuration)
Linux desktop (GUI apps only):
- Window managers: i3, bspwm, Awesome
- Bars: Polybar, Waybar
- Launchers: Rofi, Fuzzel, Wofi
- Notifications: Dunst
- Themes: Gruvbox (light/dark), theme switching scripts
- Media: MPV, CMus
- Display: Autorandr (multi-monitor configs)
macOS (Mac-specific):
- Input: Karabiner key remapping
Terminal emulators (desktop only):
- Kitty, Ghostty, Alacritty, Wezterm
# Install chezmoi
# macOS
brew install chezmoi
# Arch Linux
pacman -S chezmoi
# Other Linux
cargo install chezmoi# Clone and install in one command
chezmoi init --apply git@github.com:floatingman/dotfiles.gitThat's it! Everything is configured automatically.
The chezmoi init --apply command:
- Clones this repository to
~/.local/share/chezmoi(chezmoi's source directory) - Executes
.chezmoi.toml.tmplto generate~/.config/chezmoi/chezmoi.toml(see Chezmoi Configuration below) - Checks out git submodules (Neovim, Tmux, bin)
- Generates all config files in your home directory
After installation:
# Reload shell
source ~/.zshrcChezmoi's own config lives at ~/.config/chezmoi/chezmoi.toml. It is not a
managed dotfile — chezmoi reads it on startup, so it can't be managed like the
rest. Instead it is generated from a template committed to this repo:
.chezmoi.toml.tmpl(in the source root)
When you run chezmoi init, chezmoi executes this template and writes the
result to ~/.config/chezmoi/chezmoi.toml. The template sets:
[merge],[diff],[add]— tool preferences (nvim for merges, thedeltapager, follow symlinks when adding)[data]— per-machine variables consumed by templates and conditional rules:anthropic.token/claude.api_timeout_ms— API credentials/timeouts (defined for reuse; no managed template currently consumes them)pi.enabled/pi.repo/pi.reviewerRepo— gate the pi-agent config and select the skills/reviewer repos to clone (prompted once per machine; whenenabled=falseall pi entries are skipped via.chezmoiignore)
The Anthropic token is never committed. The template resolves
data.anthropic.token at chezmoi init time, in this order:
ANTHROPIC_AUTH_TOKENenvironment variable (preferred), then- an interactive prompt, cached in chezmoi's local state
(
~/.config/chezmoi/chezmoistate.boltdb— local only, never in the repo)
Leaving it blank is harmless — no managed template currently consumes
data.anthropic.token (it's retained for reuse by any future template needing
an Anthropic API key).
promptStringOnce / promptBoolOnce only run during chezmoi init, so the
generated config only updates when you re-init:
# Write the regenerated config to ~/.config/chezmoi/chezmoi.toml
chezmoi init
# Re-prompt for a cached value (e.g. to rotate the token), then regenerate
chezmoi state delete --bucket=configState --key=anthropic.token
chezmoi init
# Re-prompt for pi.enabled / pi.repo, then regenerate
chezmoi state delete --bucket=configState --key=pi.enabled
chezmoi initOnly files named
.chezmoi.<format>.tmpl(e.g..chezmoi.toml.tmpl) are read bychezmoi init. A plain.chezmoi.tomlin the source root is ignored.
chezmoi init --apply generates ~/.config/chezmoi/chezmoi.toml from
.chezmoi.toml.tmpl automatically (see Chezmoi Configuration).
You should only need the manual fallback below if chezmoi apply fails with
template errors (e.g., map has no entry for key "pi"), meaning the config
data isn't being loaded:
# Option 1: Re-init (recommended)
chezmoi init --apply git@github.com:floatingman/dotfiles.git
# Option 2: Manually create config with data section
mkdir -p ~/.config/chezmoi
cat > ~/.config/chezmoi/chezmoi.toml << 'EOF'
[data]
[data.anthropic]
token = ""
[data.claude]
api_timeout_ms = "3000000"
[data.pi]
enabled = true # set false on machines that don't use pi-agent
repo = "git@github.com:floatingman/pi-dotfiles.git" # or your fork
reviewerRepo = "git@github.com:floatingman/pi-reviewer.git" # or your fork
EOF
chezmoi applyThe templates are defensive — they work with empty data and fall back to environment variables. But you need some config file for chezmoi to process templates correctly.
pi reads skills from
~/.pi/agent/skills/ and config from ~/.pi/agent/{settings,mcp}.json. Chezmoi
provisions all of it directly under ~/.pi/agent/ — no symlinks, no separate
~/dotfiles repo — but only on machines where you opt in.
chezmoi init prompts:
Enable pi-agent config (skills + settings via ~/dotfiles) on this machine?
pi-dotfiles repo URL (set to YOUR fork if you have one)?
- Yes →
chezmoi applyprovisions pi under~/.pi/agent/:run_once_install-pi-agent.shclones the skills repo in place to~/.pi/agent/skills/— a normal git checkout, so you edit and push right there (cd ~/.pi/agent/skills && git commit && git push).dot_pi/agent/create_settings.jsoncreates~/.pi/agent/settings.jsononce (chezmoicreate_— then pi owns it; pi addslastChangelogVersion, theme changes, etc. on top of the static config).dot_pi/agent/create_mcp.jsoncreates~/.pi/agent/mcp.jsononce (chezmoicreate_— then you own it; safe to add machine-local secrets like webhook URLs, since chezmoi never overwrites it).dot_pi/agent/run_onchange_install-pi-packages.sh.tmplreconciles the baseline extension list intosettings.json'spackagesarray (additive — never removes locally-added packages; re-runs when you edit the list).run_once_install-pi-reviewer.shclonespi-reviewerto~/git/pi-reviewer(so the settings.json package../../git/pi-reviewerresolves).
- No → chezmoi leaves pi untouched (all pi entries are skipped via
.chezmoiignore).
Each install script probes its repo and skips gracefully if unreachable (no
clone, chezmoi apply continues — nothing breaks). Forkers: set [data.pi] repo
/ reviewerRepo to your fork URLs in ~/.config/chezmoi/chezmoi.toml, then
re-run chezmoi init && chezmoi apply.
To change your answers later, edit pi.enabled / pi.repo in
~/.config/chezmoi/chezmoi.toml (or re-run chezmoi init) and chezmoi apply.
oh-my-pi (omp, successor to pi) keeps global
settings in ~/.omp/agent/config.yml. Unlike pi's settings.json, omp's config is
pure portable settings with no machine-local runtime state, and omp writes it in place
through a symlink — so it syncs with a symlink_ entry instead of create_:
files/omp/agent/config.yml— the real settings content (committed; never applied as a target —files/is ignored)dot_omp/agent/symlink_config.yml.tmpl— renders to{{ .chezmoi.sourceDir }}/files/omp/agent/config.yml, sochezmoi applyinstalls~/.omp/agent/config.ymlas a symlink into this repo
Workflow: /settings or omp config set edits land directly in the repo copy —
commit and push them. chezmoi update on other machines picks them up (live on the
next omp launch). chezmoi diff never reports drift (it compares the link target,
not the contents).
On a machine where omp already created a regular config.yml: merge any local
differences into files/omp/agent/config.yml first, then chezmoi apply replaces
the file with the symlink.
Never sync (machine-local state): agent.db, models.db, history.db, sessions/,
terminal-sessions/, last-changelog-version. Keep credentials out of config.yml
(env vars / auth store) — through the symlink they would sync straight into the repo.
Machine-local secrets live in ~/.omp/agent/.env (autoloaded by omp, e.g.
CLINE_API_KEY, HINDSIGHT_API_TOKEN); the file is neither managed nor committed
by chezmoi.
Future: models.yml, SYSTEM.md, RULES.md, keybindings.* follow the same
content + symlink_ pair; mcp.json should use create_ (may hold machine-local
secrets).
Configs are automatically filtered based on:
- Operating system: Linux, macOS, Windows
- Desktop environment: Desktop vs headless Linux
- Display detection: Checks for i3, gnome-shell, plasma-shell, Hyprland
| Config type | Desktop Linux | Headless Linux | macOS | Windows |
|---|---|---|---|---|
| CLI apps | ✅ | ✅ | ✅ | ✅ |
| Window managers | ✅ | ❌ | ❌ | ❌ |
| Terminal emulators | ✅ | ✅ | ✅ | ❌ |
| Media apps | ✅ | ❌ | ❌ | ❌ |
| Mac-specific | ❌ | ❌ | ✅ | ❌ |
CLI apps: Neovim, Tmux, Yazi, Zsh, Starship, Atuin, Git, etc.
Desktop apps: i3, Polybar, Rofi, Kitty, MPV, Autorandr, etc.
Toggle between Gruvbox dark/light themes:
~/.config/i3/scripts/toggle-theme.sh light
~/.config/i3/scripts/toggle-theme.sh darkUpdates all supported apps:
- Window manager (i3)
- Terminal emulators (Kitty, Wezterm)
- Editor (Neovim)
- Shell prompt (Starship)
- File manager (Yazi)
A few configs are pulled in as git submodules (see .gitmodules), pinned to the
commits recorded in this repo (so they advance only when this repo bumps the
pointer — there are no chezmoi external_ entries):
- Neovim: LazyVim →
dot_config/nvim - Tmux: .tmux config →
dot_config/tmux - Personal scripts: bin →
dot_local/bin
chezmoi init / chezmoi update (a.k.a. make update) checks each submodule out
at its recorded commit. To advance a submodule to its latest upstream and record it:
chezmoi cd
git submodule update --remote --merge
git add .gitmodules dot_config/nvim dot_config/tmux dot_local/bin
git commit -m "Bump submodules"~/.local/share/chezmoi/ # Source directory (your dotfiles repo)
├── dot_config/ # Application configs
│ ├── nvim/ # Neovim (submodule: lazyvim)
│ ├── tmux/ # Tmux (submodule: .tmux)
│ ├── i3/ # Linux desktop only
│ ├── karabiner/ # macOS only
│ └── ...
├── dot_local/bin/ # Personal scripts (submodule: bin)
├── dot_zshrc # Zsh configuration
├── dot_zsh/ # Zsh framework
├── dot_omp/agent/symlink_config.yml.tmpl # ~/.omp/agent/config.yml → symlink into this repo
├── files/omp/agent/config.yml # omp settings content (symlink target; ignored as target)
├── dot_pi/agent/ # pi-agent config (conditional on pi.enabled)
│ ├── create_settings.json # ~/.pi/agent/settings.json (create-once; pi owns runtime state)
│ ├── create_mcp.json # ~/.pi/agent/mcp.json (create-once; safe for machine-local secrets)
│ └── run_onchange_install-pi-packages.sh.tmpl # additive reconcile of baseline packages
├── run_once_install-pi-agent.sh.tmpl # Conditional: clones skills repo → ~/.pi/agent/skills
├── run_once_install-pi-reviewer.sh.tmpl# Conditional: clones pi-reviewer → ~/git/pi-reviewer
├── .chezmoi.toml.tmpl # Template → ~/.config/chezmoi/chezmoi.toml
├── .chezmoiignore # Per-target + conditional ignore rules
└── Makefile # Convenience targets (update, apply, add, …)
All chezmoi operations are available via make for convenience:
make update # Pull latest + apply (recommended workflow)
make apply # Apply templates without pulling
make status # See what's managed
make diff # Preview changes before applyingmake add FILE=~/.zshrc # Track new file
make edit FILE=~/.config/nvim/init.lua # Edit in chezmoi sourcemake doctor # Check chezmoi health
make verify # Check for uncommitted changes
make clean # Remove all managed files (with confirmation)If you prefer direct chezmoi commands (not required with Makefile):
# Apply all configs
chezmoi apply
# Apply specific config
chezmoi apply ~/.config/nvim
# Check what would change
chezmoi diff
# Edit a file
chezmoi edit ~/.config/nvim/init.lua
# See all managed files
chezmoi managed
# Enter source directory (for git operations)
chezmoi cd
git pull
git statusThe proper chezmoi workflow for development:
-
Edit source files:
chezmoi edit ~/.zshrc # Opens file in source directory # Make your changes, save and exit
-
Apply changes:
chezmoi apply ~/.zshrc # Apply single file chezmoi apply # Apply all changes
-
Commit and push:
make commit # Or: chezmoi cd && git commit make push # Or: chezmoi cd && git push
-
Pull on other machines:
chezmoi update # Pull + apply in one command
Desktop configs not installing on headless server?
- Expected behavior - GUI apps excluded from servers
- Check desktop detection:
ls /usr/bin/i3 /usr/bin/gnome-shell
Configs not updating?
- Run
chezmoi applyafter pulling changes - Check source directory:
chezmoi source-path
Theme switching broken?
- Ensure scripts have execute permissions
- Check state file:
cat ~/.cache/i3/current-theme
Submodules missing or empty (e.g. empty dot_config/nvim)?
- Init/update submodules:
chezmoi cd && git submodule update --init --recursive - Then
chezmoi apply
pi skills/config not present?
- Confirm
pi.enabled = truein~/.config/chezmoi/chezmoi.toml - Skills: check
~/.pi/agent/skills/.gitexists; if not, the clone script skipped (data.pi.repounreachable) — runchezmoi init && chezmoi apply - settings.json/mcp.json are create-once (chezmoi seeds them; you/pi own runtime
state after). The extension list lives in
run_onchange_install-pi-packages.sh.tmpl. chezmoi applyprompting "has changed since last wrote it"? Answer yes (or use--force) — it's chezmoi protecting a file modified outside chezmoi
Template errors on new machine (map has no entry for key)?
- Chezmoi needs config data before processing templates
- Re-run:
chezmoi init --apply git@github.com:floatingman/dotfiles.git - Or create
~/.config/chezmoi/chezmoi.tomlwith a[data]section (see New Machine Bootstrap above)
MIT © floatingman