A complete macOS development environment β Ruby/Rails, Elixir/Phoenix, Node/React, Python, Go, Rust, Zig β with your choice of Tokyo Night, Aura Dark, or Catppuccin Mocha theme applied everywhere.
This setup is inspired by DHH's Omakub and his "Everything in one place, everything just works" philosophy. The goal is simple:
- One Command Installation - From zero to productive development environment in minutes
- Unified Theme - Choose Tokyo Night, Aura Dark, or Catppuccin Mocha β applied across 17 apps for visual consistency. If theme apply fails partway, all configs are automatically restored.
- Keyboard-First - Vim motions, tiling windows, keyboard shortcuts for everything
- Modular Configuration - Clean, organized configs that are easy to understand and modify
- Developer Ergonomics - Tools chosen for speed, reliability, and joy of use
Like DHH's Omakub for Ubuntu, this setup provides:
- Opinionated but customizable - Great defaults, easy to change
- Battle-tested tools - Used daily for professional Rails and Elixir development
- Productivity-focused - Everything configured to minimize friction
- Beautiful aesthetics - Because we stare at code all day
| Tool | Why |
|---|---|
| Aerospace | i3-style tiling for macOS β keyboard-driven, distraction-free |
| Ghostty | GPU-accelerated terminal, native macOS, modern |
| Neovim + Zed | Neovim with AstroNvim for terminal, Zed for GUI β both with full LSP |
| zsh + Starship | Fast, reliable shell with a beautiful prompt |
| Zellij | Rust-based terminal multiplexer with on-screen hints + Rails/Phoenix/Work layouts |
| Tokyo Night / Aura / Catppuccin | Choose your theme β applied consistently across all tools |
Choose your theme during install β it's applied across 22 apps:
| Auto-configured (16) | Manual (links provided) |
|---|---|
| Neovim, Ghostty, Zellij, Starship, Zed, VS Code, Warp, bat, git-delta, fzf, lazygit, borders, sketchybar, yazi, gitui, lsd | Slack, Chrome, Firefox, Telegram, Raycast |
Switch anytime: dotfiles theme tokyo-night, dotfiles theme aura, or dotfiles theme catppuccin
- Ruby/Rails - mise, ruby-lsp, RuboCop, RSpec tasks, ERB formatting
- Elixir/Phoenix - mise, elixir-ls, mix integration, tailwindcss-language-server
- TypeScript/React - prettier, eslint, auto-imports, inlay hints
- Zig - zls (auto-installed), build/test/run tasks, 26 snippets
- Node.js - mise, npm/yarn
- Git - lazygit, fugitive, gitsigns
- Aerospace - i3-like tiling window manager for macOS
- Neovim + AstroNvim - Modern Vim distribution with LSP, Treesitter, Telescope
- Zellij - Rust-based terminal multiplexer with on-screen hints
- Starship - Fast, customizable prompt (16x faster than Spaceship)
- Harpoon - Quick file navigation (ThePrimeagen workflow)
- Smart CLI -
cduses zoxide (frecency),lsβeza,catβbat,grepβripgrep,findβfd - Shell UX - syntax-highlight as you type, ghost-text autosuggestions, fzf-tab fuzzy completion
- Claude Code - Anthropic's coding agent (cask + VS Code extension)
- GitHub Copilot CLI -
ghcs(suggest) andghce(explain), built intogh β₯ 2.49 - llm (Simon Willison) - Pipe-friendly LLM CLI (
cat err.log \| llm "explain"); plugins for OpenAI, Anthropic, Ollama, Gemini - ollama - Local LLM runtime for offline / private workflows
- Gemini CLI - Google's terminal LLM
- Perplexity (Mac App Store) - AI-powered search
- Window Manager: Aerospace (i3-like tiling for macOS)
- Terminal: Ghostty (GPU-accelerated)
- Shell: zsh with modular configuration
- Prompt: Starship
- Multiplexer: Zellij
- Editors: Neovim with AstroNvim, Zed (settings, snippets, tasks)
- Version Control: Git (smart defaults, respects
$EDITOR, per-directory identity), lazygit, GitHub CLI - Work Management: work-setup, work-nuke, work-switch, work-status, repos-clone
- Dotfiles CLI:
dotfiles update,dotfiles sync,dotfiles health,dotfiles theme,dotfiles add-theme,dotfiles cleanup,dotfiles doctor,dotfiles backup,dotfiles profile,dotfiles export - Custom Scripts:
~/bin(erb-lint-formatter, etc.)
- mise - Modern version manager (replaces rbenv, nvm, asdf)
- Ruby (latest via mise)
- Elixir + Erlang (latest)
- Node.js (latest)
- Python 3
- Zig
- Go, Rust (latest)
- PostgreSQL 14
- MySQL
- Redis
- SQLite (via litecli)
- ripgrep, fd, fzf, fzf-tab (fuzzy finding + completion)
- bat (cat with syntax highlighting)
- eza (modern ls)
- jq (JSON pipeline tooling)
- tree (directory visualization)
- yazi (terminal file manager)
- lnav, tailspin (log viewers)
- zsh-autosuggestions, zsh-syntax-highlighting (shell UX)
These are quick one-time things macOS itself needs before install.sh can work:
| # | What | Why |
|---|---|---|
| 1 | Sign in to Apple ID (System Settings β Sign In) | Required for the 12 Mac App Store apps in the Brewfile (1Password Safari extension, Xcode, Kindle, Numbers, Perplexity, etc.) |
| 2 | Open the App Store app and sign in once | The mas CLI uses an active App Store session |
| 3 | Xcode Command Line Tools | install.sh triggers the install dialog automatically if missing β just click Install and re-run when done (~3 min) |
That's it. Everything else (Homebrew, all packages, 1Password app + CLI, all configs, theme, AI tooling) is automated.
bash <(curl -fsSL https://raw.githubusercontent.com/AnjanJ/dotfiles/main/install.sh)Or if you prefer to clone first:
git clone https://github.com/AnjanJ/dotfiles.git ~/dotfiles && bash ~/dotfiles/install.shBoth are fully non-interactive with sensible defaults. No prompts, no questions β just run it.
bash install.sh --name "AJ" --email "aj@example.com" --theme aura
bash install.sh --work-email "aj@corp.com" --ssh generate
bash install.sh --interactive # Prompt for every choice (old behavior)
bash install.sh --groups "core,editors,databases" # Install only specific package groups
bash install.sh --no-macos-defaults # Skip macOS system preferences
bash install.sh --force # Force reinstall everything
bash install.sh --help # Show all options- β Install Homebrew (if not installed)
- β
Install packages from Brewfile (interactive mode lets you select package groups;
--groupsto pick specific ones) - β Create symlinks to dotfiles (shell, Neovim, Zed, ~/bin, etc.)
- β Apply your chosen theme (Tokyo Night, Aura Dark, or Catppuccin)
- β
Wire
llm-ollamaplugin (sollmcan talk to local Ollama models) - β Configure Neovim
- β Configure Zed (settings, snippets, tasks)
- β Set up shell environment, Git defaults, and SSH keys
- β Apply macOS defaults
- β Run health check
π‘ Truly idempotent β run it 10 times, get the same result. No backup clutter, no duplicate configs, no re-prompting.
π Want the full package list? Read Brewfile directly β it's organized into @group sections (core, editors, work, databases, etc.) with comments explaining each tool.
Installed Tools:
βββ Window Management
β βββ Aerospace (i3-like tiling)
βββ Terminals
β βββ Ghostty (GPU-accelerated)
βββ Shell
β βββ zsh (modular configuration)
β βββ Starship prompt
βββ Multiplexers
β βββ Zellij (with rails/phoenix/work layouts)
β βββ Zellij
βββ Editors
β βββ Neovim + AstroNvim
β βββ Zed
βββ Languages
β βββ Ruby + mise
β βββ Elixir + Erlang
β βββ Node.js
β βββ Python
β βββ Zig
β βββ Go
β βββ Rust
βββ Databases
β βββ PostgreSQL 14
β βββ MySQL
β βββ Redis
β βββ SQLite (litecli)
βββ Dotfiles CLI
β βββ dotfiles update (upgrade & sync)
β βββ dotfiles sync (quick refresh β pull, relink, theme)
β βββ dotfiles health (verify setup)
β βββ dotfiles theme (switch theme)
β βββ dotfiles add-theme (scaffold new theme)
β βββ dotfiles cleanup (remove unlisted Homebrew packages)
β βββ dotfiles doctor (auto-fix issues)
β βββ dotfiles backup (snapshot/restore)
β βββ dotfiles profile (shell startup timing)
β βββ dotfiles export (portable setup snapshot)
β βββ dotfiles install (run installer)
β βββ dotfiles uninstall (remove symlinks)
β βββ dotfiles edit / dir (open editor / print path)
βββ Work Management
β βββ work-setup (configure work identity)
β βββ work-nuke (remove work config)
β βββ work-switch (change employer)
β βββ work-status (show current setup)
β βββ repos-clone (clone from GitHub/GitLab/Bitbucket/Codeberg)
βββ Custom Scripts
β βββ ~/bin (erb-lint-formatter, etc.)
βββ Tools
βββ Git + lazygit
βββ ripgrep, fd, fzf
βββ bat, eza, tree
The .zshrc is organized into focused, modular files:
~/.zshrc # Main config (loads everything)
~/.zshrc-dhh-additions # DHH-inspired workflows
~/.zshrc-elixir-additions # Elixir/Phoenix tools
~/.zshrc-terminal-enhancements # Zellij + Neovim aliases, fzf-tab, zsh-autosuggestions, llm/AI tooling
~/.zshrc-work # Work-specific settings (optional)
Why modular?
- Easy to understand and modify
- Enable/disable features by commenting one line
- Share common configs, keep private ones separate
- Inspired by DHH's clean, organized approach
dotfiles/
βββ .zshrc # Core: prompt, PATH, tool initialization
βββ .zshrc-dhh-additions # Rails workflows, aliases, functions
βββ .zshrc-elixir-additions # Elixir/Phoenix development
βββ .zshrc-terminal-enhancements # Terminal multiplexers, editors
βββ .zshrc-work # Work-specific (created by work-setup, not in repo)
βββ .config/
β βββ aerospace/ # Window management: layouts, keybindings
β βββ ghostty/ # Terminal: theme, fonts
β βββ mise/ # Version manager (Ruby, Node, Elixir, etc.)
β βββ nvim/ # Editor: LSP, plugins, keymaps
β βββ zed/ # Zed: settings, snippets, tasks
β βββ zellij/ # Multiplexer: config, theme, layouts (rails, phoenix, work)
β βββ lazygit/ # Git UI: config + theme
β βββ borders/ # JankyBorders: active window highlighting
β βββ sketchybar/ # Menu bar: config + plugins
β βββ starship.toml # Prompt: git, languages, colors
βββ bin/ # Custom scripts (erb-lint-formatter, etc.)
βββ Brewfile # Declarative package management
Each file has a single responsibility. Want to change your Rails workflow? Edit .zshrc-dhh-additions. Need different terminal aliases? Modify .zshrc-terminal-enhancements.
-
Restart your terminal
source ~/.zshrc
-
Open Neovim (plugins auto-install)
nvim
-
Start Aerospace
aerospace reload # Or: logout and login again
Note: Uses Ctrl+Shift for international keyboard compatibility (DHH-inspired)
| Key | Action |
|---|---|
Ctrl+Shift+H/J/K/L |
Focus window (vim-style navigation) |
Ctrl+Alt+H/J/K/L |
Move window |
Ctrl+Shift+1-9 |
Switch to workspace 1-9 |
Ctrl+Alt+1-9 |
Move window to workspace 1-9 |
Ctrl+Shift+Tab |
Toggle between last two workspaces |
Ctrl+Shift+/ |
Toggle layout (tiles/accordion) |
Ctrl+Shift+- |
Decrease window size |
Ctrl+Shift+= |
Increase window size |
App Launchers (Workspace-aware):
| Key | App | Workspace |
|---|---|---|
Ctrl+Shift+C |
Chrome (work) | 1 |
Ctrl+Shift+Z |
Zed editor | 2 |
Ctrl+Shift+W |
Ghostty terminal | 3 |
Ctrl+Shift+F |
Firefox (personal) | 5 |
Ctrl+Shift+G |
Ghostty terminal | 7 |
Ctrl+Shift+O |
Obsidian (PKM) | 8 |
Ctrl+Shift+P |
1Password | 9 |
Browser Window Cycling (across all workspaces):
| Key | Action |
|---|---|
Ctrl+Shift+N |
Next Chrome window |
Ctrl+Shift+B |
Previous Chrome window |
Alt+Shift+N |
Next Firefox window |
Alt+Shift+B |
Previous Firefox window |
| Key | Action |
|---|---|
<Leader>ff |
Find files (Telescope) |
<Leader>fw |
Find word (grep) |
<Leader>fb |
Find buffers |
<Leader>ha |
Harpoon: add file |
Ctrl+H/J/K/L |
Harpoon: jump to marks 1-4 |
gd |
Go to definition |
gr |
Find references |
<Leader>la |
Code actions |
K |
Hover documentation |
<Leader>rc |
Rails: controller |
<Leader>rm |
Rails: model |
<Leader>rv |
Rails: view |
<Leader>rs |
Rails: spec |
| Key | Action |
|---|---|
Ctrl+O |
Enter command mode (shows options) |
Ctrl+G |
Lock mode (pass keys to terminal) |
Alt+H/J/K/L |
Navigate panes |
Ctrl+Q |
Quit Zellij |
Zellij Layouts (pre-configured via aliases):
| Alias | Layout | Tabs |
|---|---|---|
zr |
Rails | editor, server (rails s + console), tests, terminal |
zp |
Phoenix | editor, server (phx.server + iex), tests, terminal |
zw |
Work | editor, server (two panes), terminal |
A modern shell needs AI tooling. This setup wires LLMs into your daily flow without taking over the terminal.
| Tool | What it does | Trigger |
|---|---|---|
| Claude Code | Full coding agent (CLI + IDE) | claude or VS Code/Zed extension |
| GitHub Copilot CLI | Suggests/explains shell commands | ghcs / ghce |
| llm | Pipe text β LLM, get text back | | llm "..." |
| ollama | Run LLMs locally (offline + private) | ollama run <model> or via llm |
| Gemini CLI | Google's terminal LLM | gemini |
| explain-last | "What did that command do?" | explain-last |
Why
llmand notmods? Earlier versions of this setup usedmods, but Charmbracelet sunset that project on 2026-03-09 (issues like #561 β Ollama streaming output looping β were never patched). Simon Willison'sllmis the modern replacement: actively maintained, plugin-based, works cleanly with Ollama viallm-ollama.
llm reads stdin and prints the model's response. The unix way to use AI.
# Diagnose an error
cat /var/log/system.log | tail -50 | llm "anything concerning here?"
# Generate a commit message from your diff
git diff --staged | llm -s "write a concise conventional-commit message; output only the message"
# Convert a CSV to JSON
cat data.csv | llm "convert to JSON, keep types"
# Refactor on the fly
cat script.sh | llm "rewrite this in idiomatic Python 3.12, keep behavior identical"
# One-shot questions
llm "explain SIGPIPE in 3 sentences"The -s flag sends the next argument as a system prompt. The -m flag picks a specific model. -c continues the previous conversation. --no-stream returns the full response at once instead of streaming.
First-time setup (already done in this dotfiles setup):
# Plugin for local Ollama models (one-time)
llm install llm-ollama
# Optional: add hosted-API plugins
llm install llm-anthropic llm-gemini
# Set a default model β qwen2.5-coder:7b is local, fast, private
llm models default qwen2.5-coder:7b
# Set API keys when you want hosted models
llm keys set openai # paste key when prompted
llm keys set anthropicSwitch models per call:
llm -m qwen2.5-coder:7b "..." # local, default
llm -m gpt-4o "..." # OpenAI, hosted
llm -m claude-sonnet-4-5 "..." # Anthropic, hosted
llm models # list everything availableTemplates save reusable system prompts:
llm "you are a senior engineer reviewing code; be terse" --save reviewer
cat src/auth.rb | llm -t reviewer
llm 'you write conventional-commit messages: subject under 72 chars, body explains why' --save commit
git diff --staged | llm -t commitTemplates live at ~/Library/Application Support/io.datasette.llm/templates/.
Continue a conversation:
llm "what is currying?"
llm -c "give me a JS example" # remembers the previous answer
llm logs # see historyAlready comes with gh β₯ 2.49. Two subcommands, two aliases:
ghcs "find files larger than 100MB modified in the last week"
# Suggests: find . -type f -size +100M -mtime -7
ghce "git rebase --interactive HEAD~5"
# Explains the command in plain EnglishRequires an active GitHub Copilot subscription. First run prompts for auth.
Local models run on your Mac. No data leaves your machine. Great for:
- Sensitive code/logs you can't send to a hosted API
- Offline work (flight, travel)
- Cheap experimentation without per-token costs
- Latency-sensitive use cases
Architecture:
- Ollama is the server β it loads
.ggufweights, runs them on Apple Silicon GPU, exposes an HTTP API atlocalhost:11434. Internally it uses llama.cpp. llm+llm-ollamaplugin is the client β pipes stdin to the server. You don't talk to ollama directly except topull/list/rmmodels.
Setup:
# Pull a coding-tuned model (~5GB) β recommended default
ollama pull qwen2.5-coder:7b
# List what you have on disk
ollama list
# Tell `llm` about it (rescan plugin registrations)
llm models | grep -i ollamaUse directly (occasional):
ollama run qwen2.5-coder:7b "write a bash function that retries N times"Use via llm (the daily driver):
cat error.log | llm -m qwen2.5-coder:7b "explain"
# Or, if you set qwen2.5-coder:7b as default:
cat error.log | llm "explain"Recommended models (download once, use forever):
| Model | Size | Best for |
|---|---|---|
qwen2.5-coder:7b |
4.7 GB | Recommended default. Code review, refactoring, explanations |
qwen2.5-coder:14b |
9 GB | Stronger code reasoning, larger refactors |
qwen3:14b |
~9 GB | General reasoning / harder thinking tasks (when 7b isn't enough) |
llama3.1:8b |
4.7 GB | General Q&A, summarization |
nomic-embed-text |
274 MB | Embeddings (RAG, semantic search) |
Two-model strategy: keep
qwen2.5-coder:7bas default for fast coding tasks, and a larger general model (e.g.qwen3:14b) for harder reasoning. Switch per call:llm -m qwen3:14b "...".
Custom function in .zshrc-terminal-enhancements. Pipes your last shell command through llm:
$ find . -name "*.rb" -mtime -1 -exec rg -l "TODO" {} \;
# ...output...
$ explain-last
# llm explains what the find/rg pipeline does- "I need a coding agent that can edit my files." β Claude Code
- "I forgot the syntax for X." β
ghcs "X" - "What does this regex/pipe do?" β
ghce "..."orexplain-last - "Process this stdin and give me text back." β
llm - "I'm offline / this is sensitive." β ollama via
llm(default), orollama rundirectly - "I want a chat-style interface." β Claude Code app, Perplexity, or
llm chat
Three plugins make typing in zsh feel like an editor:
Colors commands as you type. Valid commands turn green, unknown ones stay red β catch typos before pressing enter. Quoted strings, paths, options, and operators are all distinctly colored.
Fish-style ghost text. As you type, your shell shows the rest of a previous command in dim gray. Press β (right arrow) or End to accept. The strategy is (history completion) β history first, then tab-completion-aware fallbacks.
$ git cβ # ghost text shows "git commit -m"
$ git commit -m # press β to acceptReplaces zsh's default tab-complete menu with an fzf fuzzy picker. Every <Tab> becomes a searchable menu with previews:
cd <Tab> # fuzzy-pick a directory
git checkout <Tab> # fuzzy-pick a branch (preview shows git log)
kill <Tab> # fuzzy-pick a process
ssh <Tab> # fuzzy-pick a host from ~/.ssh/configType to filter. Enter to select. Esc to cancel.
Choose between Tokyo Night (dark blue), Aura Dark (deep purple), or Catppuccin Mocha (warm pastels) during install. The theme is applied across 22 apps (17 auto-configured + 5 with manual instructions).
Browse the full galleries: Tokyo Night | Aura Dark | Catppuccin
| Tokyo Night | Aura Dark | Catppuccin Mocha | |
|---|---|---|---|
| Background | #1a1b26 |
#15141b |
#1e1e2e |
| Foreground | #c0caf5 |
#edecee |
#cdd6f4 |
| Primary accent | #7aa2f7 blue |
#a277ff purple |
#b4befe lavender |
| Secondary | #bb9af7 purple |
#61ffca green |
#94e2d5 teal |
| Success | #9ece6a green |
#61ffca green |
#a6e3a1 green |
| Error | #f7768e red |
#ff6767 red |
#f38ba8 red |
| Warning | #e0af68 yellow |
#ffca85 orange |
#f9e2af yellow |
Switch anytime: dotfiles theme tokyo-night, dotfiles theme aura, or dotfiles theme catppuccin
dotfiles/
βββ install.sh # Main installation script (idempotent)
βββ update.sh # Update script for syncing changes
βββ Brewfile # Homebrew packages (auto-generated)
βββ README.md # This file
βββ QUICK_REFERENCE.md # Cheat sheet
βββ .zshrc # Main shell config
βββ .zshrc-dhh-additions # Rails workflows
βββ .zshrc-elixir-additions # Elixir/Phoenix
βββ .zshrc-terminal-enhancements # Terminal tools
βββ .config/
β βββ aerospace/ # Window manager config
β βββ ghostty/ # Terminal config
β βββ mise/ # Version manager (Ruby, Node, Elixir, etc.)
β βββ nvim/ # Neovim config (AstroNvim)
β βββ zed/ # Zed editor config
β β βββ settings.json # Language, LSP, formatter, extension settings
β β βββ tasks.json # RSpec, Rails, Elixir, Zig, npm tasks
β β βββ snippets/ # ruby.json, erb.json, zig.json
β βββ zellij/ # Zellij config, theme, layouts
β βββ lazygit/ # Lazygit config + theme
β βββ borders/ # JankyBorders window highlighting
β βββ sketchybar/ # Menu bar config + plugins
β βββ starship.toml # Prompt configuration
βββ themes/ # Theme assets (each has a theme.conf registry)
β βββ tokyo-night/ # Tokyo Night configs per app
β βββ aura/ # Aura Dark configs per app
β βββ catppuccin/ # Catppuccin Mocha configs per app
βββ bin/ # CLI commands (all available globally via ~/bin)
β βββ dotfiles # Main CLI dispatcher
β βββ dotfiles-update # Upgrade system & sync repo
β βββ dotfiles-sync # Quick refresh: pull, relink, reapply theme
β βββ dotfiles-health # Verify all tools are installed
β βββ dotfiles-theme # Switch theme
β βββ dotfiles-install # Run full installer
β βββ dotfiles-uninstall # Remove all dotfiles symlinks
β βββ dotfiles-backup # Snapshot/restore dotfiles state
β βββ dotfiles-doctor # Auto-fix common issues + SSH key audit
β βββ dotfiles-profile # Measure shell startup time
β βββ dotfiles-export # Export setup as portable snapshot
β βββ dotfiles-add-theme # Scaffold new theme directory
β βββ dotfiles-cleanup # Remove unlisted Homebrew packages
β βββ work-setup # Configure work identity
β βββ work-nuke # Remove all work config
β βββ work-switch # Change employer
β βββ work-status # Show current work setup
β βββ repos-clone # Clone repos from GitHub/GitLab/Bitbucket/Codeberg
β βββ erb-lint-formatter # ERB lint wrapper for Zed
β βββ _work-helpers # Shared utilities for work scripts
βββ scripts/
β βββ _helpers.sh # Shared colors & print functions
β βββ setup-git.sh # Git identity & defaults setup
β βββ setup-ssh.sh # SSH key & config setup
β βββ health-check.sh # Verify installation
β βββ theme-utils.sh # Theme utility functions
β βββ apply-theme.sh # Apply theme across all apps
βββ tests/ # 300 tests across 11 suites, run via GitHub Actions CI
β βββ test-idempotency.sh # Idempotency tests (sandboxed)
β βββ test-work-nuke.sh # Work-nuke edge case tests
β βββ test-repos-clone.sh # Repos-clone logic tests
β βββ test-ssh-adversarial.sh # SSH adversarial input tests
β βββ test-update.sh # Update symlink tests
β βββ test-theme.sh # Theme system tests (apply, rollback, scaffold)
β βββ test-doctor.sh # Doctor auto-fix tests
β βββ test-setup-git.sh # Git identity setup tests
β βββ test-work-status.sh # Work status diagnostic tests
β βββ test-backup.sh # Backup/restore cycle tests
β βββ test-sync.sh # Sync symlink refresh tests
βββ .github/workflows/test.yml # CI: shellcheck + all test suites
βββ docs/ # Additional documentation
All commands work from anywhere β no need to cd ~/dotfiles first.
dotfiles update # Upgrade system & sync repo (pull β brew upgrade β snapshot β push)
dotfiles sync # Quick refresh: pull, relink, reapply theme (no upgrades)
dotfiles health # Verify all tools are installed and configured
dotfiles theme aura # Switch theme (tokyo-night | aura | catppuccin)
dotfiles add-theme x # Scaffold a new theme directory with all required files
dotfiles cleanup # Find/remove Homebrew packages not in Brewfile (--force)
dotfiles doctor # Auto-fix common issues (symlinks, permissions, SSH keys)
dotfiles backup # Snapshot dotfiles state (--list, --restore <name>)
dotfiles profile # Measure shell startup time (--detailed for per-component)
dotfiles export # Export setup snapshot (--json for machine-readable)
dotfiles install # Re-run full installer (idempotent)
dotfiles edit # Open dotfiles in your editor
dotfiles dir # Print dotfiles directory pathAll commands support tab-completion. Shorthand also works: dotfiles-update, dotfiles-sync, etc.
- Pull latest changes from git
brew update+brew upgrade+brew cleanup- Snapshot installed packages and show diff against Brewfile (without overwriting the organized Brewfile)
- Refresh all symlinks
- Upgrade mise tools
- Reload live configs (aerospace)
- Commit & push changes back to repo
Your Brewfile stays organized. The Brewfile is organized into @group sections (core, editors, work, databases, etc.) and dotfiles update never overwrites it. The snapshot step shows you what's new or missing compared to your system.
Two layers of safety net:
1. Git history is the source of truth. Every previous Brewfile is one command away:
# View the previous version
git -C ~/dotfiles show HEAD~1:Brewfile
# Restore the previous version to a working file
git -C ~/dotfiles show HEAD~1:Brewfile > /tmp/Brewfile.previous
# Roll the tracked Brewfile back N commits and reinstall
git -C ~/dotfiles checkout HEAD~1 -- Brewfile
brew bundle install --file=~/dotfiles/Brewfile2. Brewfile.backup (local only, gitignored). dotfiles update writes a copy of the previous Brewfile to ~/dotfiles/Brewfile.backup before each upgrade. It's not tracked in git (no commit noise) but lives on disk for one-step rollback if an upgrade breaks your machine:
# Quick rollback after a bad `dotfiles update`
cp ~/dotfiles/Brewfile.backup ~/dotfiles/Brewfile
brew bundle install --file=~/dotfiles/Brewfile
# Inspect what changed before rolling back
diff ~/dotfiles/Brewfile.backup ~/dotfiles/BrewfileThe on-disk backup only holds the most recent previous state. For older versions, use git log -- Brewfile to find the commit and git show <sha>:Brewfile to restore.
dotfiles healthVerifies: core tools, config symlinks, language runtimes, running services (PostgreSQL, MySQL, Redis), shell integrations, and work identity.
The install script is truly idempotent β run it any number of times with identical results:
dotfiles install # Non-interactive, skips what's done
dotfiles install --interactive # Prompt for every choice
dotfiles install --force # Force reinstall everythingSwitch between Tokyo Night, Aura Dark, and Catppuccin Mocha from anywhere:
dotfiles theme tokyo-night # Switch to Tokyo Night
dotfiles theme aura # Switch to Aura Dark
dotfiles theme catppuccin # Switch to Catppuccin MochaThis updates 16 apps automatically (Neovim, Ghostty, Zellij, Starship, Zed, VS Code, Warp, bat, git-delta, fzf, lazygit, borders, sketchybar, yazi, gitui, lsd), then prints instructions for Slack, browsers, Telegram, and Raycast. If the apply fails partway through, all configs are automatically rolled back.
Adding a new theme is just creating a themes/<name>/ directory with a theme.conf β themes are auto-discovered, no code changes needed. Use dotfiles add-theme <name> to scaffold the full directory structure.
Edit Brewfile and run:
brew bundle install- Settings:
.config/zed/settings.json(languages, LSP, formatters) - Tasks:
.config/zed/tasks.json(RSpec, Rails, Zig, etc.) - Snippets:
.config/zed/snippets/(ruby.json, erb.json, zig.json)
- Aerospace:
.config/aerospace/aerospace.toml - Zellij:
.config/zellij/config.kdl - Neovim:
.config/nvim/lua/plugins/*.lua
The Brewfile installs the 1Password app + CLI automatically. SSH-agent wiring auto-detects:
- If 1Password is signed in and the SSH Agent is enabled before you run
install.sh, the install wires~/.ssh/configto the agent socket automatically. - If 1Password isn't ready yet, install skips SSH setup and prints a hint.
Fresh-Mac flow (the order I personally recommend):
- Run
install.shβ this lands the 1Password app along with everything else. - Open 1Password.app β sign in to your account β vaults sync from cloud.
- 1Password β Settings β Developer β "Set Up SSH Agent" (toggle ON).
- Run the SSH wiring step now that the agent is alive:
bash ~/dotfiles/scripts/setup-ssh.sh - Test with Touch ID:
ssh -T git@github.com.
Where do my SSH keys come from? They must already be in your 1Password vault (synced automatically from a previous machine), or added manually via 1Password β New Item β SSH Key (generate or paste). The dotfiles only configure the agent, not the keys.
What this gives you:
- Touch ID for git push β each new terminal session requires biometric approval before SSH operations
- No key files on disk β keys live in your 1Password vault, encrypted and synced
- Works everywhere β GitHub, GitLab, Bitbucket, Codeberg, self-hosted Git
How it works under the hood:
- Install symlinks
~/.1password/agent.sockto 1Password's actual socket ~/.ssh/configsetsIdentityAgent ~/.1password/agent.sock- When you
git pushin a new terminal session, 1Password prompts for Touch ID - Approval lasts until 1Password locks (configurable timeout)
Recommended 1Password settings for maximum security:
| Setting | Value | Why |
|---|---|---|
| Ask approval for each new | application and terminal session |
Per-tab approval, not global |
| Remember key approval | until 1Password locks |
Approval expires on lock |
| Auto-lock after idle | 1 minute (or shortest you're comfortable with) |
Frequent re-authentication |
Note: This is per-session, not per-push. Once you approve in a terminal tab, subsequent pushes in that tab go through until 1Password locks. For additional protection, consider a short auto-lock timeout.
See QUICK_REFERENCE.md for SSH troubleshooting and testing commands.
# Apple Silicon
eval "$(/opt/homebrew/bin/brew shellenv)"
# Intel
eval "$(/usr/local/bin/brew shellenv)"# Clear cache and reinstall
rm -rf ~/.local/share/nvim
rm -rf ~/.cache/nvim
nvim # Will reinstall everything# Reload configuration
aerospace reload
# Or restart
killall Aerospace
open -a AerospaceDetailed guides for each tool:
- Brewfile - Full package list, grouped by purpose
- Quick Reference - Print this!
- Neovim Guide
- Daily Workflows
300 automated tests across 11 suites run on every push and PR via GitHub Actions:
- Shellcheck β lints all shell scripts (auto-discovers via glob patterns)
- Idempotency β verifies install/setup can run multiple times safely (84 assertions)
- Work identity β tests setup, nuke, switch lifecycle, and status diagnostics
- SSH config β adversarial inputs and edge cases
- Repo cloner β SSH alias detection and URL rewriting
- Update flow β symlink creation and refresh
- Theme system β apply, rollback, idempotency, scaffolding (39 assertions)
- Doctor β auto-fix symlinks, permissions, dry-run mode
- Git setup β identity configuration, work/personal split, smart defaults
- Backup β create, list, restore, prune cycle
- Sync β symlink refresh, broken link repair, dry-run mode
Run locally: bash tests/test-idempotency.sh (or any test file in tests/)
- DHH - Omakub philosophy, Rails workflows, clean configurations
- Omakub - "Everything in one place" installation concept
- ThePrimeagen - Harpoon workflow, tmux setup, vim-first development
- JosΓ© Valim - Elixir tooling and workflows
- AstroNvim - Neovim distribution
- Tokyo Night - Dark blue theme by folke
- Aura Theme - Deep purple theme by daltonmenezes
- Starship - Fast prompt
- Aerospace - Window manager
- Ghostty - Modern terminal
MIT License - Feel free to use and modify!
Found a bug or have a suggestion? Open an issue or PR!
If this setup saves you time, consider sponsoring the project. It keeps development going and lets me know people are finding it useful.
Made with β€οΈ by Anjan
Inspired by DHH's Omakub and ThePrimeagen's workflows
Last updated: 2026-03-14











