Skip to content

Latest commit

 

History

310 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Personal Dotfiles (Chezmoi)

Opinionated macOS dotfiles managed by chezmoi for a single daily-driver Mac. Repo is the declarative source of truth for the shell, editor, package list, and other authored configs. AI tool state (~/.claude, ~/.codex, ~/.cursor) is captured from the machine on demand because skills/agents/plugins churn too fast to hand-maintain.

Fair Warning: highly personal setup. Fork and adapt — don't expect a plug-and-play experience on a different person's workflow.

Quick start (new machine)

Prereqs — see SETUP.md for detail:

  1. macOS installed; user account created
  2. xcode-select --install
  3. 1Password app installed + signed in (enable SSH agent + CLI integration)
  4. 1Password CLI installed + authenticated (op signin)

Then:

bash <(curl -fsSL https://raw.githubusercontent.com/jarodtaylor/dotfiles/main/bootstrap.sh)

⚠️ Use bash <(curl …) — process substitution — not curl … | bash. The pipe form gives bash a non-TTY stdin, which makes Homebrew's installer fail with a misleading "Need sudo access" error because it can't prompt for your password.

That:

  • Installs Homebrew, chezmoi, and age (needed at init time)
  • Clones this repo + runs chezmoi apply
  • Brewfile installs ~100 entries (58 brew + 33 casks + 8 MAS + 3 taps)
  • Configs are rendered with secrets pulled from 1Password

Total: ~5 minutes of active clicks + 30–60 minutes walkaway.

After bootstrap finishes, walk through SETUP.md §6 Post-bootstrap setup — sign into apps, grant macOS permissions (Accessibility, Screen Recording, kext approvals), and install the handful of packages excluded from the Brewfile (ExpressVPN).

Daily workflow

# Adding a package: edit the Brewfile, then apply.
$EDITOR $(chezmoi source-path)/Brewfile     # add `brew "foo"` / `cask "foo"`
dots apply                                  # install it

# Removing a package: edit the Brewfile, then apply.
$EDITOR $(chezmoi source-path)/Brewfile     # delete the line
dots apply                                  # `brew bundle cleanup` uninstalls it

# Adding an AI tool customization (skill, agent, plugin):
# ... edit ~/.claude/skills/whatever.md ...
dots sync                                   # capture drift into source repo
cd "$(chezmoi source-path)/.."              # navigate to the repo
git diff                                    # review what was captured
git add . && git commit -m "..." && git push

The Brewfile is hand-edited on purpose — the friction prevents package sprawl. dots sync only captures AI tool state (~/.claude, ~/.codex, ~/.cursor), where machine churn is real.

⚠️ Don't brew uninstall foo directly when foo is in the Brewfile. The next dots apply will reinstall it (Brewfile is the source of truth for installed packages). Edit the Brewfile first; cleanup will then remove it.

Key commands

Command Purpose
dots sync Capture AI tool drift into the source repo (no auto-commit)
dots apply Reconcile machine with repo (chezmoi apply + brew bundle)
dots doctor Multi-layer health check
dots edit Open the source repo in $EDITOR

What is dots?

dots (home/bin/executable_dots) is a thin Bash wrapper around chezmoi + brew bundle + git. If you're already a chezmoi user, the mapping is:

dots command Equivalent chezmoi/brew/git invocations
dots apply chezmoi apply (which triggers brew bundle install via run_onchange_before_10-install-packages.sh.tmpl when the Brewfile changes) + brew bundle cleanup --force
dots sync chezmoi re-add ~/.claude ~/.codex ~/.cursor, with up-front detection of .tmpl drift (which chezmoi re-add silently skips)
dots doctor chezmoi doctor + brew bundle check + repo cleanliness + op whoami + AI tool dir presence
dots edit $EDITOR $(chezmoi source-path)/..

Plain chezmoi apply, chezmoi diff, chezmoi re-add <path>, etc. still work directly. The wrapper exists for keystroke economy and to bake in the combinations that are easy to get wrong — especially surfacing template drift on sync so you don't get false-success ("nothing changed") when chezmoi re-add quietly refused to touch a .tmpl file.

Architecture

flowchart LR
    subgraph repo["📁 Repo (this directory)"]
        direction TB
        BF["Brewfile<br/>(hand-edited)"]
        DC["dot_config/<br/>nvim, zsh, ghostty,<br/>git, ssh, ..."]
        AT["dot_claude/<br/>dot_codex/<br/>dot_cursor/"]
    end

    subgraph machine["💻 Machine"]
        direction TB
        BREW["Homebrew<br/>/Applications"]
        CFG["~/.config<br/>~/.zshenv<br/>~/.ssh<br/>..."]
        AI["~/.claude<br/>~/.codex<br/>~/.cursor"]
    end

    subgraph op["🔐 1Password vault"]
        AGE["Age key<br/>tokens<br/>env files"]
    end

    BF ==>|dots apply| BREW
    DC ==>|dots apply| CFG
    AT ==>|dots apply| AI
    AI -.->|dots sync| AT
    op -.->|onepasswordRead<br/>at template render| DC
Loading

Solid arrows = dots apply (repo → machine, the dominant flow). Dashed = dots sync (machine → repo, AI tool state only) and 1Password reads (vault → templates at apply time).

Short version:

  • Repo is source of truth for declared state. Brewfile, SSH config, nvim, ghostty, starship, zsh, git, etc. Hand-edit in the repo; dots apply reconciles to the machine.
  • Machine is source of truth for AI tool state. ~/.claude, ~/.codex, ~/.cursor skills/agents/plugins churn too fast to hand-maintain. dots sync re-adds them into the repo.
  • Runtime state never syncs. Logs, caches, session history, sqlite DBs — filtered in .chezmoiignore.
  • Secrets via 1Password. SSH keys, work git email, age decryption key, Claude/Codex auth tokens. No secrets on disk outside of 1Password-served templates.

Key layout

bootstrap.sh                                        one-liner entry
home/                                                chezmoi source root
├── Brewfile                                         package manifest (tap/brew/cask)
├── bin/executable_dots                              the `dots` CLI
├── dot_claude/, dot_codex/, dot_cursor/             captured AI tool state
├── dot_config/                                      authored configs (nvim, ghostty, etc.)
├── private_dot_ssh/                                 SSH config (1Password-backed)
├── .chezmoiscripts/                                 runtime scripts (packages, pam, etc.)
└── .chezmoi.toml.tmpl                               per-machine config, 1Password integration

Validating changes

Fast-feedback checks before committing:

chezmoi execute-template < home/path/to/file.tmpl   # render a template
shellcheck home/bin/executable_dots bootstrap.sh    # lint shell
chezmoi apply --dry-run -v                          # preview apply (read-only)
dots doctor                                         # multi-layer health check

Related docs

  • SETUP.md — detailed new-machine walkthrough
  • docs/ADD_AI_TOOL.md — onboarding a new AI tool: classify, ignore, secrets
  • docs/GOTCHAS.md — rough edges and workarounds (password prompts, manual installs, etc.)

Inspiration

About

My personal dotfiles system built using Chezmoi with automation, multi-identity support, and macOS-specific optimizations.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages