Managed by chezmoi. On chezmoi init, you choose which modules to install. chezmoi then applies only the files and tools for those modules.
- Install the Xcode Command Line Tools. chezmoi needs git, and git is only a stub until this is done.
xcode-select --install - Install chezmoi and apply. Answer no to the 1Password question for now.
sh -c "$(curl -fsLS get.chezmoi.io)" -- -b ~/.local/bin init --apply codyjk - Open a new terminal.
The secrets module reads keys with the 1Password CLI (op) when chezmoi applies, so install op first. This needs the homebrew module.
- Install the 1Password app and sign in.
- Install the CLI. The cask downloads 1Password's signed zip and checks its SHA-256.
brew install --cask 1password-cli - Confirm the publisher. The output must be
TeamIdentifier=2BUA8C4S2C.codesign -dv /opt/homebrew/bin/op 2>&1 | grep TeamIdentifier - In the 1Password app, turn on Settings > Developer > Integrate with 1Password CLI. Run
op whoamito test it. On macOS 27, ifopcannot reach the app, allow your terminal under System Settings > Privacy & Security > Files and Folders. - Run
chezmoi init --prompt, answer yes to the 1Password question, then runchezmoi apply.
| Module | Default | What it installs |
|---|---|---|
prompt |
on | powerlevel10k and ~/.p10k.zsh |
fzf |
on | fzf binary and zsh key bindings |
nvim |
on | neovim, ripgrep, fd, nvim config, plugins from lazy-lock.json |
tmux |
on | tmux config. tmux itself is built from pinned source into ~/.local (about 2 minutes), with or without Homebrew |
terminal |
ghostty | Ghostty app, or none |
rust |
on | rustup and a pinned Rust toolchain (plus rust-analyzer with nvim) |
python |
on | uv and Python (plus pyright with nvim) |
node |
on | fnm and a pinned Node LTS (plus typescript with nvim) |
gh |
on | GitHub CLI |
vale |
off | vale prose linter |
lathe |
off | lathe tutorial CLI, plus its skills in ~/.claude/skills (reinstalled when the version changes) |
secrets |
off | ~/.config/zsh/secrets.zsh (mode 0600) from 1Password |
homebrew |
off | Homebrew in /opt/homebrew and ~/.Brewfile |
Binaries come from pinned GitHub releases (see .chezmoidata/versions.toml) into ~/.local/bin, not from Homebrew. To change a version, edit that file and run chezmoi apply.
theme switches the color theme and light/dark mode for Ghostty and Neovim in one step. Run theme for the list of themes. quiet is the default. Fourteen come from vim-no-color-collections. A theme with only one variant uses it in every mode.
themeshows the themes, marks the current one, and shows the mode.theme NAMEswitches to NAME.theme nextgoes to the next one.theme dark,theme light, ortheme autosets the mode.auto(the default) follows macOS.theme NAME MODEsets both.theme preview [NAME...] [MODE]draws a sample of each theme in the shell without switching. With no names, it shows all of them.theme renderis the same command. It loads each colorscheme in headless Neovim, so the colors are exact.- In Neovim,
:Themetakes the same arguments. Both have tab completion.
The themes are listed in ~/.config/theme/themes. The theme command and Neovim both read it. Your choice lives in ~/.local/state/theme, outside chezmoi, so switching does not change the repo. Ghostty includes the ghostty file from that folder and reloads at once. Open Neovim instances switch at once. In auto mode, Neovim checks the macOS appearance when it gets focus. Italics are off in every colorscheme. tmux and p10k use ANSI colors, so they follow the Ghostty palette.
To add a theme:
- Add a line to
dot_config/theme/themes. - If Ghostty has no built-in theme for it, generate one from the Neovim colorscheme with
~/.config/theme/ghostty-from-nvim.lua(usage is at the top of the file). - Add its Neovim plugin to
lua/plugins/colorscheme.lua. - Run
chezmoi apply.
Every download has an exact version and a SHA-256 in .chezmoidata/versions.toml. chezmoi and the install scripts stop on a mismatch. This covers the binaries, powerlevel10k (pinned to one commit), the tmux source tarballs, rustup, and the Homebrew installer. Node, Python, Rust, pyright and TypeScript are also pinned to exact versions. rustup does not update itself. Ghostty must carry a valid signature from the Team ID in versions.toml.
To bump a version:
- Change the version in
[versions]. - Get the new hash from the GitHub release page ("digest"), or run
curl -fsSL <url> | shasum -a 256. - Change the hash in
[checksums]. - Run
chezmoi apply.
When the homebrew module is on:
- Homebrew installs from its signed
.pkgat a pinned version, not from theHEADinstall script. ~/.homebrew/brew.envturns off analytics and auto-update, stopsbrew installfrom upgrading, forbids HTTPS-to-HTTP redirects, and requires a checksum for every cask. With theghmodule on, it also verifies bottle attestations. Homebrew runsghwith its own PATH, so the bundle script and thebrewwrapper pass the token fromgh auth tokeninHOMEBREW_GITHUB_API_TOKEN. The token is never written to disk.~/.Brewfileis the complete list.chezmoi applyinstalls it with--no-upgrade, then runsbrew bundle cleanup --force, which uninstalls anything not in it. The lists live in.chezmoidata/packages.toml. Each cask must have a checksum.brewfile add NAMEandbrewfile remove NAMEedit those lists, runchezmoi apply, and commit the change.brewfile addchecks that the package exists, uses the canonical name, and rejects casks with no checksum. Pass--formulaor--caskwhen a name is both.brewfile listshows the lists.- In zsh,
brew installrunsbrewfile add, andbrew uninstall/remove/rmrunbrewfile remove.brew tapandbrew fetchare blocked (fetchcaches bottles without an attestation check). Otherbrewcommands run unchanged. To bypass, runcommand brew .... /opt/homebrew/bingoes last onPATH, so Homebrew never shadows system tools or pinned tools.- A Brewfile pins names, not versions. Homebrew packages change only when you run
brew updateandbrew upgrade.
To change your module choices, run chezmoi init --prompt, then chezmoi apply. Turning a module off stops managing its files. It does not uninstall anything.