https://github.com/jarodtaylor/dotfiles
My personal dotfiles system built using Chezmoi with extensive automation, multi-identity support, and macOS-specific optimizations.
https://github.com/jarodtaylor/dotfiles
1password aerospace chezmoi cursor dotfiles homebrew macos nvim starship vscode
Last synced: about 2 months ago
JSON representation
My personal dotfiles system built using Chezmoi with extensive automation, multi-identity support, and macOS-specific optimizations.
- Host: GitHub
- URL: https://github.com/jarodtaylor/dotfiles
- Owner: jarodtaylor
- Created: 2025-01-06T19:45:49.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2026-04-30T21:13:36.000Z (2 months ago)
- Last Synced: 2026-04-30T23:10:05.004Z (2 months ago)
- Topics: 1password, aerospace, chezmoi, cursor, dotfiles, homebrew, macos, nvim, starship, vscode
- Language: Shell
- Homepage:
- Size: 6.13 MB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Personal Dotfiles (Chezmoi)
Opinionated macOS dotfiles managed by [chezmoi](https://www.chezmoi.io/) 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`](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
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](SETUP.md#6-post-bootstrap-setup-manual) — sign
into apps, grant macOS permissions (Accessibility, Screen Recording,
kext approvals), and install the handful of packages excluded from the
Brewfile (ExpressVPN).
## Daily workflow
```bash
# 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 `, 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
```mermaid
flowchart LR
subgraph repo["📁 Repo (this directory)"]
direction TB
BF["Brewfile
(hand-edited)"]
DC["dot_config/
nvim, zsh, ghostty,
git, ssh, ..."]
AT["dot_claude/
dot_codex/
dot_cursor/"]
end
subgraph machine["💻 Machine"]
direction TB
BREW["Homebrew
/Applications"]
CFG["~/.config
~/.zshenv
~/.ssh
..."]
AI["~/.claude
~/.codex
~/.cursor"]
end
subgraph op["🔐 1Password vault"]
AGE["Age key
tokens
env files"]
end
BF ==>|dots apply| BREW
DC ==>|dots apply| CFG
AT ==>|dots apply| AI
AI -.->|dots sync| AT
op -.->|onepasswordRead
at template render| DC
```
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:
```bash
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`](SETUP.md) — detailed new-machine walkthrough
- [`docs/ADD_AI_TOOL.md`](docs/ADD_AI_TOOL.md) — onboarding a new AI tool: classify, ignore, secrets
- [`docs/GOTCHAS.md`](docs/GOTCHAS.md) — rough edges and workarounds (password prompts, manual installs, etc.)
## Inspiration
- [Tom Payne's dotfiles](https://github.com/twpayne/dotfiles) — chezmoi's creator, clean reference implementation
- [Chezmoi documentation](https://www.chezmoi.io/)