Cross-platform development environment configuration managed by chezmoi.
Platforms: macOS (Apple Silicon/Intel) | Arch Linux | CachyOS | Windows
Shell: zsh + antidote + Powerlevel10k
AI: OpenCode + Everything Claude Code (ECC)
- Prerequisites
- Installation
- What Happens During Install
- Configuration Prompts
- Post-Install Steps
- Repository Structure
- How Chezmoi Works
- Daily Workflow
- Making Changes
- Shell Architecture
- OpenCode + ECC Integration
- Homebrew (macOS)
- Troubleshooting
- Git (installed by default on macOS;
sudo pacman -S giton Arch; comes with Git for Windows) - zsh (default on macOS;
sudo pacman -S zshon Arch)
- Xcode Command Line Tools — required for git and compilers:
xcode-select --install
- Base development tools:
sudo pacman -S base-devel git zsh
- yay (AUR helper, recommended for antidote and other AUR packages):
# If not already installed (CachyOS ships yay by default) sudo pacman -S yay - antidote (zsh plugin manager):
yay -S zsh-antidote
- Chocolatey — package manager for Windows. Install from chocolatey.org
- Git for Windows:
choco install git
The fastest path — a single command installs chezmoi, clones this repo, prompts for your config, and deploys everything:
# Install Xcode CLT first (if not already done)
xcode-select --install
# Install chezmoi + clone + configure + apply (all in one)
sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply dmirtillo/dotfilesAlternative if you already have Homebrew:
brew install chezmoi
chezmoi init --apply dmirtillo/dotfiles# 1. Install chezmoi
sudo pacman -S chezmoi
# 2. Install antidote (from AUR)
yay -S zsh-antidote
# 3. Clone, configure, and apply
chezmoi init --apply dmirtillo/dotfiles
# 4. Set zsh as default shell (if not already)
chsh -s $(which zsh)# 1. Install chezmoi
choco install chezmoi
# OR: winget install twpayne.chezmoi
# 2. Clone, configure, and apply
chezmoi init --apply dmirtillo/dotfilesIf chezmoi is not packaged for your system, this works everywhere:
sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply dmirtillo/dotfilesThis downloads the chezmoi binary to ./bin/chezmoi and runs it.
When you run chezmoi init --apply, the following happens automatically in order:
- Clone — chezmoi clones this repo to
~/.local/share/chezmoi/ - Prompt — you are asked for your personal configuration (see Configuration Prompts)
- Render templates —
.tmplfiles are processed with your config + OS detection - Deploy files — rendered files are written to
$HOME:~/.zshrc,~/.zprofile,~/.p10k.zsh(shell config)~/.gitconfig(with your name/email)~/.vimrc(with platform-correct fzf path)~/.ssh/config(with private permissions)~/.config/opencode/(OpenCode agent config)~/.zsh_plugins.txt(antidote plugin list)
- Clone ECC — the Everything Claude Code repo is cloned to
~/.local/share/ecc/ - Run setup scripts (in order):
install-packages.sh— runsbrew bundle install(macOS) or prints guidance (Linux)setup-opencode.sh— creates symlinks from ECC into~/.config/opencode/, installs plugin SDKantidote-bundle.sh— regenerates the antidote static plugins file
On first run, chezmoi prompts for these values. They are stored in ~/.config/chezmoi/chezmoi.toml and not asked again unless you re-run chezmoi init.
| Prompt | Description | Example |
|---|---|---|
| Git user name | Used in ~/.gitconfig |
Davide Mirtillo |
| Git email address | Used in ~/.gitconfig |
davide.mirtillo@gmail.com |
| SSH key filenames | Keys in ~/.ssh/keys/ to auto-load on shell startup. Comma-separated. Leave empty for none. |
my-key.key,work-key.key |
| Google Cloud project | Sets GOOGLE_CLOUD_PROJECT env var. Leave empty to skip. |
my-gcp-project |
| Google Cloud location | Sets GOOGLE_CLOUD_LOCATION env var. Leave empty to skip. |
global |
| Enable Antigravity | Whether to add ~/.antigravity/antigravity/bin to PATH |
false |
To change your answers:
chezmoi initThis re-runs the prompts (pre-filled with your current values) and regenerates ~/.config/chezmoi/chezmoi.toml. Then apply:
chezmoi applyIf you want to automate setup without interactive prompts (e.g., in a script or CI), create ~/.config/chezmoi/chezmoi.toml before running chezmoi apply:
[data]
git_name = "Your Name"
git_email = "your.email@example.com"
ssh_keys = "key1.key,key2.key"
gcloud_project = ""
gcloud_location = ""
enable_antigravity = false- Open a new terminal to load the updated shell configuration.
- Run
p10k configureif the Powerlevel10k wizard appears. - Copy SSH keys to
~/.ssh/keys/(the directory is not created automatically — you need your actual key files).
- Review installed packages — the Brewfile installs ~110 formulae, ~58 casks, and ~35 VS Code extensions. Some casks may require manual approval in System Settings > Privacy & Security.
- Some apps require manual installation — see the Brewfile comments for apps not available via Homebrew.
-
Install additional packages — the automated package install for pacman/yay is not yet fully implemented. Key packages to install manually:
# Core CLI tools (match the macOS Brewfile) sudo pacman -S bat eza fd fzf ripgrep zoxide dust duf procs gping \ htop btop ncdu tree jq yq git lazygit docker docker-compose \ direnv thefuck tldr wget curl nmap # From AUR yay -S zsh-antidote powerlevel10k-git
- Shell config does not apply on Windows — the zsh/antidote/p10k configuration is for macOS and Linux only. Windows gets
.gitconfig,.vimrc, and.ssh/config. - Set up PowerShell profile (not yet included in this repo — future addition).
dotfiles/
├── .chezmoi.toml.tmpl Config template (prompts on init)
├── .chezmoiexternal.toml External deps (ECC git repo)
├── .chezmoiignore Platform-specific exclusions
│
├── dot_zshrc.tmpl ~/.zshrc (Go template)
├── dot_zsh_plugins.txt ~/.zsh_plugins.txt
├── dot_zprofile.tmpl ~/.zprofile (Go template)
├── dot_p10k.zsh ~/.p10k.zsh (generated by p10k configure)
├── dot_gitconfig.tmpl ~/.gitconfig (Go template)
├── dot_vimrc.tmpl ~/.vimrc (Go template)
│
├── private_dot_ssh/
│ └── config ~/.ssh/config (0600 permissions)
│
├── private_dot_config/
│ └── private_opencode/ ~/.config/opencode/
│ ├── AGENTS.md AI agent rules
│ ├── opencode.json Agent definitions + model routing
│ └── package.json Plugin SDK dependency
│
├── run_onchange_install-packages.sh.tmpl Runs brew/pacman when Brewfile changes
├── run_onchange_setup-opencode.sh.tmpl Creates ECC symlinks + npm install
├── run_onchange_antidote-bundle.sh.tmpl Regenerates antidote static plugins
│
├── Brewfile macOS Homebrew manifest
├── AGENTS.md AI agent instructions for this repo
└── README.md
Files in this repo use special prefixes that chezmoi interprets during deployment:
| Prefix/Suffix | Effect | Example |
|---|---|---|
dot_ |
Deployed as . |
dot_zshrc -> ~/.zshrc |
private_ |
Sets restrictive permissions (0700 for dirs, 0600 for files) |
private_dot_ssh/ -> ~/.ssh/ |
.tmpl |
Processed as a Go template before deploying | dot_zshrc.tmpl -> rendered ~/.zshrc |
run_onchange_ |
Script that runs when its tracked hash changes | run_onchange_install-packages.sh.tmpl |
Template files (.tmpl) use Go's text/template syntax. Chezmoi provides built-in variables:
| Variable | Description | Example Values |
|---|---|---|
.chezmoi.os |
Operating system | darwin, linux, windows |
.chezmoi.arch |
CPU architecture | amd64, arm64 |
.chezmoi.hostname |
Machine hostname | MacBook-Pro |
.chezmoi.username |
Current user | dmirtillo |
.chezmoi.sourceDir |
Path to chezmoi source | ~/.local/share/chezmoi |
User-defined data from .chezmoi.toml.tmpl is accessed as .git_name, .git_email, .ssh_keys, etc.
Example — OS-specific alias in dot_zshrc.tmpl:
{{ "{{" }} if eq .chezmoi.os "darwin" -{{ "}}" }}
alias localip='ipconfig getifaddr en0'
{{ "{{" }} else -{{ "}}" }}
alias localip='hostname -I | awk "{print \$1}"'
{{ "{{" }} end -{{ "}}" }}
Scripts prefixed with run_onchange_ execute automatically when a tracked dependency changes. The dependency is tracked via a hash comment in the script:
# chezmoi:template:hash {{ "{{" }} include "Brewfile" | sha256sum {{ "}}" }}This means the script re-runs only when the Brewfile content changes — not on every chezmoi apply.
| Task | Command |
|---|---|
| Preview what would change | chezmoi diff |
| Apply all changes | chezmoi apply |
| Dry run (no changes) | chezmoi apply --dry-run |
| Edit a managed file | chezmoi edit ~/.zshrc |
| Pull upstream + apply | chezmoi update |
| Re-add a locally modified file | chezmoi re-add ~/.zshrc |
| Check managed files | chezmoi managed |
| Verify deployed state | chezmoi verify |
| Health check | chezmoi doctor |
# 1. Edit the source template
chezmoi edit ~/.zshrc
# 2. Preview what would change
chezmoi diff
# 3. Apply
chezmoi apply
# 4. Commit the change
cd $(chezmoi source-path)
git add -A && git commit -m "feat: add new alias"
git pushchezmoi updateThis does git pull on the source repo and then chezmoi apply in one step.
# Add an existing file
chezmoi add ~/.some-config
# Add as a template (for cross-platform content)
chezmoi add --template ~/.some-config- Run
chezmoi edit ~/.zshrcto opendot_zshrc.tmpl - Add your alias in the appropriate section (under the matching
# ===banner) - If it's OS-specific, wrap it in a Go template conditional
- Run
chezmoi applyto deploy - Commit and push
# Install the package
brew install some-package
# Regenerate the Brewfile
brew bundle dump --file=$(chezmoi source-path)/Brewfile --force
# Commit
cd $(chezmoi source-path)
git add Brewfile && git commit -m "chore: add some-package to brewfile"
git push- Edit
dot_zsh_plugins.txt(in the source repo or viachezmoi edit ~/.zsh_plugins.txt) - Run
chezmoi apply— this triggersrun_onchange_antidote-bundle.shautomatically - Open a new terminal
Target: < 50ms shell startup time. Techniques used:
| Optimization | Savings | How |
|---|---|---|
| Powerlevel10k instant prompt | ~200ms | Renders prompt before plugins load |
Cached brew shellenv |
~80ms | Cached to ~/.cache/brew-shellenv.zsh, regenerates on brew update |
Cached fzf --zsh |
~30ms | Same caching pattern |
_cache_eval helper |
~50ms each | Caches zoxide init, direnv hook, thefuck --alias |
| Deferred compinit | ~250ms | Handled by use-omz plugin, not called twice |
| NVM lazy loading | ~400ms | zsh-nvm plugin with NVM_LAZY_LOAD=true |
| Background SSH key loading | ~11ms | load_default_ssh_keys &! |
Plugins are listed in dot_zsh_plugins.txt. When this file changes, run_onchange_antidote-bundle.sh regenerates the static file (~/.zsh_plugins.zsh) automatically during chezmoi apply.
The .zshrc template includes ~100 aliases organized by category. Highlights:
| Category | Examples |
|---|---|
| Modern CLI replacements | ls=eza, cat=bat, du=dust, df=duf, ping=gping, cd=zoxide |
| Git | gst, gco, gcb, glog, gwip, gundo, lg=lazygit |
| Docker | d, dc, dcu, dcd, dps, lzd=lazydocker |
| Terraform | tf, tfi, tfp, tfa, tfs |
| Ansible | ap, apv, apc, av, ave, avd, al |
| macOS only | showfiles, hidefiles, flushdns, emptytrash |
| Linux only | update (yay/pacman), flushdns (systemd-resolve) |
Everything Claude Code (ECC) provides AI agent prompts, commands, skills, and plugins for OpenCode.
.chezmoiexternal.tomltells chezmoi to clone ECC into~/.local/share/ecc/run_onchange_setup-opencode.shcreates symlinks from the clone into~/.config/opencode/:commands/-> ECC commandsprompts/-> ECC agent promptsinstructions/-> ECC instructionsskills/-> ECC skillsplugins/ecc-hooks.ts-> ECC plugin hooks
- Our customizations (
opencode.json,AGENTS.md,package.json) are deployed directly by chezmoi fromprivate_dot_config/private_opencode/
Agents are routed to different models based on task complexity:
| Model | Agents | Rationale |
|---|---|---|
| Gemini 3.1 Pro | planner, architect |
Deep reasoning for complex planning and architecture |
| Gemini 3.1 Pro (default) | code-reviewer, security-reviewer, tdd-guide, e2e-runner, refactor-cleaner, go-reviewer, database-reviewer |
Best balance for code generation and review |
| Gemini 3.1 Flash | build-error-resolver, doc-updater, go-build-resolver |
Mechanical fixes and documentation |
chezmoi updateThis pulls the latest ECC from GitHub and re-runs the setup script.
The Brewfile contains ~110 formulae, ~58 casks, and ~35 VS Code extensions. It runs automatically via run_onchange_install-packages.sh when the Brewfile changes.
| Category | Packages |
|---|---|
| Shell/Terminal | zsh, antidote, bat, eza, fd, fzf, dust, duf, gping, procs, zoxide, thefuck, tldr, btop |
| DevOps/IaC | ansible, packer, tfswitch |
| Cloud | awscli, azure-cli, gcloud-cli, localstack-cli |
| Containers/K8s | docker, helm, kubernetes-cli, minikube, skaffold, lazydocker |
| Python | python@3.13, python@3.14, black, ruff, mypy, pytest, uv |
| Java | openjdk, groovy, maven, jenv |
| Git | git, lazygit, gh, git-filter-repo |
| AI | opencode, gemini-cli |
chezmoi doctorThis checks for common issues: binary version, config file, source directory, required tools.
chezmoi diffchezmoi verifyExit code 0 means everything matches. Non-zero means files have drifted.
Test a template without deploying:
chezmoi execute-template < $(chezmoi source-path)/dot_zshrc.tmpl | head -20chezmoi apply --forceIf something goes wrong, you can re-initialize:
chezmoi init --apply dmirtillo/dotfilesThis re-clones the repo and re-prompts for configuration.
Measure startup time:
hyperfine 'zsh -i -c exit'Target is < 50ms. If it's slow, check that _cache_eval caches exist:
ls -la ~/.cache/{brew-shellenv,fzf,zoxide,direnv,thefuck}.zshIf missing, open a new terminal — they regenerate automatically on first load.