Dotfiles Management skill for Claude Code

One command to a working machine, zero secrets in git. Claude builds a chezmoi (or Stow or yadm) repo where machine differences live in templates and prompted data, secrets come from 1Password, Bitwarden or age encryption at apply time, and run_onchange scripts reinstall packages when the Brewfile changes. Every apply is previewed with a diff, the repo is secret-scanned before pushing, and a fresh container proves the one-line bootstrap.

  • namedotfiles-management
  • size200 lines · ≈1,900 tokens
  • files1
  • licenseMIT
  • updated

What Claude sees before loading it

This description is the only part of the skill that sits in context all the time. Claude reads it to decide when to load the full playbook below.

description: Manages dotfiles across machines with chezmoi (default), GNU Stow, or yadm — repository layout, per-OS and per-machine templates, secrets from password managers or age encryption instead of plaintext, run-once and on-change bootstrap scripts, Brewfile and package lists, safe first apply with diffs, and keeping drift under control. Use when setting up a dotfiles repo, syncing shell, git, editor, or terminal config between a work laptop, personal Mac, and Linux servers, migrating from symlink scripts, bootstrapping a new machine, or removing secrets from dotfiles.

Try asking

  • Set up chezmoi for my zsh, git and Neovim config across my work Mac and personal Linux box
  • My dotfiles repo has API tokens in it, move them to 1Password and clean up
  • Write a bootstrap so a new MacBook is fully set up with one command

Use it when

  • Syncing shell, git and editor config across machines
  • Bootstrapping a new laptop or server
  • Dotfiles contain secrets that need to come out
  • Migrating from a symlink script to chezmoi or Stow

Skip it when

How to check the result

A fresh container or VM reaches a working shell with the one-line init, chezmoi diff is empty after apply on each machine, and a secret scan of the source directory is clean.

The common mistake it prevents

Editing ~/.zshrc directly and then running chezmoi apply, which silently overwrites the change with the source version.

~/.claude/skills/dotfiles-management/SKILL.md

--- name: dotfiles-management description: Manages dotfiles across machines with chezmoi (default), GNU Stow, or yadm — repository layout, per-OS and per-machine templates, secrets… license: MIT ---

Done well means: a new machine goes from a fresh OS to a working environment with one command, every machine-specific difference is a template variable rather than a branch, no secret ever lands in the repository, and diff before apply means nothing gets overwritten by surprise.

Hard rules

  • No secrets in the repo, even a private one: API tokens, SSH private keys, .npmrc auth tokens, .netrc, cloud credentials, kube configs with tokens. Pull them from a password manager at apply time or encrypt them (age/gpg). Scan before the first push.
  • Always preview before applying on a machine with existing config: chezmoi diff, stow -n -v. Back up the home directory files that will be replaced.
  • Bootstrap scripts are idempotent: check before installing, safe to rerun.
  • Do not use stow --adopt or chezmoi apply --force without confirming with the user; both overwrite something silently (adopt replaces repo files with the machine’s).
  • Keep work and personal identities separate where policy requires (git email, signing keys, SSH hosts) using templates and machine data, not by committing work config to a personal public repo.

Pick the tool

NeedTool
Multiple OSes/machines, templating, secrets integrationchezmoi (default)
Same files everywhere, simplest possibleGNU Stow (symlink farm)
Plain git workflow in $HOME with alternate files per OS/hostyadm
Fully declarative system + user configNix Home Manager (see nix-environments)

Workflow (chezmoi)

- [ ] 1. Inventory: list config files worth managing; mark machine-specific ones and secrets
- [ ] 2. chezmoi init; add files; convert differences to templates
- [ ] 3. Move secrets to password manager lookups or age encryption
- [ ] 4. Write bootstrap: packages (Brewfile/apt list), run_once_ / run_onchange_ scripts
- [ ] 5. Scan for secrets; push
- [ ] 6. Test on a clean machine or container; apply on others with diff first
- [ ] 7. Routine: edit via chezmoi, re-add drift, update on each machine

2. Setup and daily commands

sh -c "$(curl -fsLS get.chezmoi.io)" -- -b ~/.local/bin   # or: brew install chezmoi
chezmoi init                       # creates ~/.local/share/chezmoi (git repo)
chezmoi add ~/.zshrc ~/.gitconfig ~/.config/starship.toml
chezmoi add --template ~/.gitconfig          # becomes dot_gitconfig.tmpl
chezmoi edit ~/.zshrc              # edit the source, not the target
chezmoi diff                       # what apply would change
chezmoi apply -v
chezmoi cd                         # shell in the source repo; git add/commit/push
chezmoi re-add                     # pull edits made directly to targets back into source
chezmoi managed / chezmoi unmanaged ~/.config   # what is and is not tracked
chezmoi doctor                     # environment check
# new machine, one command:
sh -c "$(curl -fsLS get.chezmoi.io)" -- init --apply <github-username>

Source state naming: dot_ -> leading dot, private_ -> mode 600/700, executable_ -> +x, .tmpl suffix -> template, symlink_ -> symlink, run_once_ / run_onchange_ scripts, before_/after_ ordering, exact_ for directories that should contain only managed files. .chezmoiignore (itself templated) skips files per machine.

Machine data and templates

# .chezmoi.toml.tmpl  — evaluated on `chezmoi init`, prompts once per machine
{{- $email := promptStringOnce . "email" "Git email for this machine" -}}
{{- $work := promptBoolOnce . "work" "Is this a work machine" -}}
[data]
  email = {{ $email | quote }}
  work = {{ $work }}
encryption = "age"
[age]
  identity = "~/.config/chezmoi/key.txt"
  recipient = "age1...publickey"
# dot_gitconfig.tmpl
[user]
  name = Ada Example
  email = {{ .email }}
{{- if eq .chezmoi.os "darwin" }}
[credential]
  helper = osxkeychain
{{- end }}
{{- if .work }}
[includeIf "gitdir:~/work/"]
  path = ~/.gitconfig-work
{{- end }}
# .chezmoiignore
README.md
{{- if ne .chezmoi.os "darwin" }}
.config/karabiner
Library
{{- end }}
{{- if not .work }}
.gitconfig-work
{{- end }}

Test templates without applying: chezmoi execute-template < dot_gitconfig.tmpl, chezmoi data to see variables (.chezmoi.os, .chezmoi.arch, .chezmoi.hostname).

3. Secrets

Password manager lookups at apply time (the CLI must be installed and signed in):

# private_dot_npmrc.tmpl
//registry.npmjs.org/:_authToken={{ onepasswordRead "op://Private/npm/token" }}

Equivalent functions exist for Bitwarden, pass, gopass, KeePassXC, LastPass, Vault, AWS Secrets Manager, and the OS keychain (keyring). Check chezmoi help docs for the exact function name for your manager.

For files you want in the repo but encrypted (SSH config with hostnames, certs):

age-keygen -o ~/.config/chezmoi/key.txt                    # age CLI; back the key up in your password manager
chezmoi add --encrypt ~/.ssh/config                        # stored as an encrypted_*.age file in the source

Never commit SSH private keys even encrypted unless you accept that risk; generating a new key per machine is usually better.

Scan before pushing:

gitleaks dir ~/.local/share/chezmoi --redact      # or: trufflehog filesystem ~/.local/share/chezmoi

4. Bootstrap scripts

# run_onchange_before_install-packages-darwin.sh.tmpl
{{ if eq .chezmoi.os "darwin" -}}
#!/bin/bash
set -euo pipefail
command -v brew >/dev/null || { echo "Install Homebrew first: https://brew.sh"; exit 1; }
# Brewfile hash: {{ include "Brewfile" | sha256sum }}
brew bundle --file={{ joinPath .chezmoi.sourceDir "Brewfile" | quote }}
{{ end -}}
  • run_onchange_ reruns when the rendered script content changes; embedding the hash of Brewfile makes it rerun when the package list changes. Add Brewfile to .chezmoiignore so it is not copied to $HOME.
  • run_once_ runs once per machine per content version (tracked in chezmoi state).
  • Keep scripts idempotent anyway; state can be reset.
  • Linux servers: a minimal profile (shell, git, editor) and an apt/dnf package list; skip GUI apps via templates.

Stow alternative

# ~/dotfiles/zsh/.zshrc, ~/dotfiles/git/.gitconfig, ~/dotfiles/nvim/.config/nvim/init.lua
cd ~/dotfiles
stow -n -v -t ~ zsh git nvim     # simulate first: shows links and conflicts
stow -v -t ~ zsh git nvim
stow -D -t ~ nvim                # remove links for a package

Stow has no templating or secrets support; handle machine differences with sourced local files ([ -f ~/.zshrc.local ] && source ~/.zshrc.local) that are not in the repo.

Verification

  • Fresh environment test: a container or VM (docker run -it --rm ubuntu:24.04, or a macOS VM), run the one-line init, and confirm shell, git identity, and editor work.
  • chezmoi diff is empty on every machine after apply (no drift).
  • chezmoi verify exits 0.
  • Secret scan of the source directory reports nothing; git log -p | grep -i token as a final sanity check.

Gotchas

  • Editing a target file directly (~/.zshrc) and then running chezmoi apply overwrites the edit; use chezmoi edit or chezmoi re-add first.
  • Apps that rewrite their own config (VS Code settings, some GUI apps) cause constant drift; manage a stable subset or use the app’s own settings sync.
  • Templates fail when a password manager is locked; sign in first or the apply aborts midway.
  • .chezmoi.toml.tmpl changes take effect only after chezmoi init is rerun.
  • Shell startup gets slow as dotfiles grow; measure with time zsh -i -c exit (see terminal-productivity).

About this skill

Written and maintained by the Claude Skills editors to the same editorial standard as every skill in the directory: a selective trigger, a real workflow, known pitfalls and a verification step. First published , last updated . MIT licensed.

esc