A reproducible macOS development environment. Clone this repo onto a fresh Mac, run one script, and you get:
- A modern terminal (Ghostty) with an actively-maintained cross-shell prompt (Starship)
- Language runtimes pinned per-project via asdf
- Rust-based CLI replacements for classic Unix tools, all aliased transparently
- Code editors, containers, cloud tools, and developer utilities
- Everything wired together via
.zshrc, Starship, Antidote, and a bootstrap script
See docs/inventory.md for the complete list of every installed tool, app, and runtime — including how each one is installed.
- Quick Start
- What the Setup Gives You
- Manual Steps (After
./setup.sh)- 1. Grant Accessibility Permissions
- 2. Configure Git Identity
- 3. Authenticate GitHub CLI
- 4. Launch Docker Desktop Once
- 5. Sign Into GUI Apps
- 6. Authenticate Cloud CLIs
- 7. Authenticate Claude Code
- 8. Optional: Enable Atuin History Sync
- 9. Optional: Set Ghostty as Default Terminal
- 10. Optional: Personalize Theme & Font
- Daily Usage: Keybindings & Commands
- Customization
- Updating Your Setup
- Troubleshooting
./setup.shreturns "permission denied"- "command not found" on a tool that should exist
- Icons show as squares in Starship / eza
- Ghostty Quick Terminal doesn't respond
- SSH session looks broken
- asdf says "No version is set"
brew bundlefails with permissions errors- Atuin doesn't import my old history
- Starting over on a single tool
- Cheat Sheets & References
- Repository Layout
- Design Decisions (Short Version)
On a fresh Mac:
# 1. macOS prompts for Xcode Command Line Tools on first `git` invocation.
# If you want to trigger it explicitly:
xcode-select --install
# 2. Clone and bootstrap
git clone https://github.com/lakshyads/dotfiles.git ~/dotfiles
cd ~/dotfiles
chmod +x setup.sh # first time only; see note below
./setup.shsetup.sh launches an interactive wizard — it walks you through each category (core tools, CLI tools, editors, apps, fonts, etc.) and asks whether to install all, customize, or skip. Pass --full to skip all prompts and install everything non-interactively:
./setup.sh --fullThe script is idempotent and safe to re-run at any time. Already-installed packages are detected and skipped.
When it finishes, open Ghostty, run exec zsh, and you're in the new environment.
To verify everything installed correctly in a new shell:
./verify.shThis runs non-destructive smoke tests: checks every CLI tool resolves, every symlink is in place, every GUI app installed, language runtimes match .tool-versions, fonts are detected, and Git is configured. Exit 0 on success, 1 with a failure summary otherwise.
Why
chmod +x? Depending on how you cloned or downloaded the repo, the executable bit onsetup.shmay not be preserved (macOS Gatekeeper strips it for quarantined files, and some git configs do too). Runningchmod +x setup.shonce fixes it permanently. If you cloned via plaingit cloneinto a trusted directory, it may already be executable and this step is a no-op.
See docs/inventory.md for a full list of what's provisioned automatically. Everything below is set up for you—no manual configuration required after running ./setup.sh.
- Ghostty with JetBrains Mono Nerd Font, Catppuccin theme (auto light/dark switching), 25M-line scrollback, and split keybindings
- Quick Terminal (Quake-style dropdown) bound to
Ctrl+`; see manual steps for required permission - Zsh with Antidote — see Zsh plugins
- Starship prompt showing directory, git branch + status, active language version, and command duration
- Atuin replacing
Ctrl+Rwith a full-screen SQLite-backed history search
Classic commands (ls, cat, top, du, git) are aliased to their modern replacements. Shell key bindings for history search and fuzzy file/directory picking are wired up. See docs/modern-cli-cheatsheet.md for the full alias map, key bindings, and usage reference.
Versions are pinned in .tool-versions — that is the only place versions are defined. See docs/inventory.md for the list of managed languages and docs/asdf-cheatsheet.md for version management commands.
See docs/inventory.md for the full list of GUI apps, CLI tools, and language runtimes.
These genuinely require human action, either because they need you to sign in, grant macOS permissions, or make personal choices. Work through them in order:
Some apps need Accessibility permission to function. macOS will prompt on first launch, but you can pre-approve them:
System Settings > Privacy & Security > Accessibility, add:
- Maccy: required to read clipboard (app won't work without it)
- Ghostty: required only if you use the Quake-style Quick Terminal global hotkey (`Ctrl+``)
- Rectangle: required for window snapping to work
- LinearMouse: required to intercept mouse events (side buttons, scroll customization)
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
# Optional but useful
git config --global pull.rebase true
git config --global rebase.autoStash trueFull recommended git config and a workflow reference are in docs/git-cheatsheet.md.
gh auth login # follow prompts, choose SSH or HTTPSFirst launch triggers a macOS prompt to install a privileged helper. Click through it. After that, docker and docker compose work from any terminal.
Docker Desktop also appends a CLI completions block to ~/.zshrc on first launch. This is already committed to the dotfiles .zshrc; don't let it get added a second time if you re-run setup on a machine where Docker Desktop has already launched.
- 1Password: sign into your account so the CLI (
op) can authenticate later - Chrome: sign in, set as default browser if desired
- Cursor / VS Code: sign in for settings sync
- Maccy: no account, but enable "Launch at Login" in its preferences
- See the inventory section for other GUI apps that may require login or manual setup after first launch
# Google Cloud
gcloud auth login
gcloud config set project <your-project-id>
gcloud auth application-default login # for SDK libraries
# Skip if you don't use GCPclaude # first run opens browser for OAuth login
claude doctor # verifies installation + authRequires a paid Anthropic account (Pro, Max, Team, Enterprise, or Console with API credits).
Atuin works fully offline by default. To sync history across machines (end-to-end encrypted):
atuin register -u <username> -e <email>
atuin key # save this somewhere safe (you'll need it on other machines)Skip if you only use one Mac or don't want cloud sync.
If you want open . and similar commands to open Ghostty instead of Terminal.app:
System Settings > Desktop & Dock > Scroll to "Default web browser / Default terminal". (Ghostty will offer to set itself as default on first launch.)
The defaults are chosen carefully, but if you want to customize:
- Ghostty theme: edit
ghostty-config, change thetheme = ...line. Preview options withghostty +list-themes. - Font: the Brewfile installs both
font-jetbrains-mono-nerd-font(default) andfont-fira-code-nerd-font. Changefont-familyinghostty-config. - Starship prompt: edit
starship.toml. See starship.rs/presets for ready-made layouts. - Shell aliases: edit
.zshrcand runreload.
All configs live in this repo and are symlinked, so changes are preserved in git.
| What you need | Where to look |
|---|---|
| Terminal keybindings (tabs, splits, Quick Terminal) | docs/ghostty-cheatsheet.md |
| Shell history search, fuzzy file/dir picker, autosuggestions | docs/modern-cli-cheatsheet.md |
Directory jumping (z) |
docs/modern-cli-cheatsheet.md |
rg, fd, bat, eza, dust, btop usage |
docs/modern-cli-cheatsheet.md |
Git TUI (lg) |
docs/lazygit-cheatsheet.md |
Language runtime commands (asdf current, asdf install) |
docs/asdf-cheatsheet.md |
On Mac,
Alt=Option(⌥). fzf and readline docs use "Alt" historically.
This repo is meant to be forked and personalized. The files worth editing:
| File | What it controls |
|---|---|
Brewfile |
What gets installed via Homebrew. Add/remove lines, run brew bundle. |
.tool-versions |
Language runtime versions. Edit and run asdf install. |
.zshrc |
Aliases, env vars, tool integration. Run reload after editing. |
.zsh_plugins.txt |
Zsh plugins loaded by Antidote. |
starship.toml |
Prompt appearance. |
ghostty-config |
Terminal appearance and keybindings. Reload with Cmd+Shift+,. |
linearmouse.json |
Mouse settings (side buttons, scroll direction, acceleration). Edit via the LinearMouse GUI; changes write back to the file automatically. |
setup.sh |
Bootstrap steps. Only touch if you add new tools needing custom setup. |
After any changes, commit them to your dotfiles repo. Other machines pick up changes with git pull && ./setup.sh.
./update.shupdate.sh runs each updater in sequence — Homebrew (formulae + casks + cleanup), Zsh plugins (antidote), asdf plugins, and Claude Code. A failure in one section does not abort the rest.
After pulling dotfiles changes, also run:
git pull
brew bundle # install any new Brewfile entries
asdf install # install any new .tool-versions runtimesNote:
brew bundleinstalls everything inBrewfileunconditionally — including anything you chose to skip in an earlier interactive./setup.shrun. See Homebrew cheat sheet — Common Pitfalls.
brew update && brew upgrade && brew upgrade --cask
brew cleanupclaude updateasdf plugin update --allThe executable bit wasn't preserved. One-time fix:
chmod +x setup.sh
./setup.shTo make the fix permanent so other machines cloning the repo don't hit this, commit the mode change:
chmod +x setup.sh
git add setup.sh # `git status` should show "mode change 100644 → 100755"
git commit -m "chore: make setup.sh executable"
git push- Did you run
./setup.sh? It symlinks.zshrc; without that, aliases and PATH aren't set. - Did you restart your shell after install? Run
exec zshor open a new tab. - Check the tool is actually installed:
brew list | grep <tool>. - For language tools (node, python, go): run
asdf currentto verify the active version is installed.
Your terminal isn't using a Nerd Font. Check ghostty-config:
font-family = JetBrainsMono Nerd Font
The spaces matter. See docs/ghostty-cheatsheet.md for details.
Accessibility permission not granted. Go to System Settings > Privacy & Security > Accessibility, add Ghostty, restart Ghostty.
The remote host doesn't have Ghostty's terminfo. Your ghostty-config already sets term = xterm-256color to avoid this, but if you removed that line, re-add it.
The .tool-versions file references a version that isn't installed yet:
asdf installSee docs/asdf-cheatsheet.md for deeper issues.
Usually means Homebrew itself needs repair:
sudo chown -R $(whoami) /opt/homebrew
brew doctorSee docs/homebrew-cheatsheet.md.
See Shell History (atuin) in the modern CLI cheat sheet.
# Reinstall a Homebrew cask
brew reinstall --cask <app-name>
# Reset an asdf tool (version from .tool-versions)
asdf uninstall <language> <version>
asdf install
# Reload shell config without restarting
reloadFull command references for the tools that get the most daily use. These live in docs/ so you can open them in-repo rather than fishing through web docs:
- Software inventory: full list of GUI apps, CLI tools, and language runtimes — update this whenever
Brewfileor.tool-versionschanges - Homebrew cheat sheet: install, daily commands, Brewfile workflows, FAQ, common pitfalls
- asdf cheat sheet: plugin management, version commands,
.tool-versionsformat, CI integration, troubleshooting - Ghostty cheat sheet: default keybindings, config syntax, action reference, SSH terminfo fixes, themes and fonts
- Git cheat sheet: daily workflow commands, branching, rebasing, undoing mistakes, stash, tags,
.gitignoreessentials, troubleshooting - Lazygit cheat sheet: panel navigation, default keybindings, line-staging, interactive rebase workflows, custom commands
- Modern CLI tools cheat sheet: ripgrep, fd, bat, eza, zoxide, fzf, atuin, delta, dust, btop, tldr. Usage per tool plus composition examples
- Docker cheat sheet: images, containers, volumes, networks, Docker Compose, Dockerfile basics, disk cleanup, troubleshooting
- Claude Code cheat sheet: CLI flags, slash commands, keyboard shortcuts, permission modes, CLAUDE.md, hooks, MCP, subagents, models and cost
- Cursor CLI cheat sheet: agent modes (Agent/Plan/Ask), slash commands, cloud handoff, MCP integration, rules and skills, subagents
Each is written as a skimmable reference, not a tutorial. Use them when you need to look something up.
dotfiles/
├── README.md # this file (orientation + daily reference)
├── setup.sh # one-command bootstrap (idempotent)
├── update.sh # update all package managers and tools
├── verify.sh # end-to-end smoke test (run after setup.sh)
├── Brewfile # Homebrew packages (formulae + casks)
├── .tool-versions # asdf runtime versions (Node, Python, Go)
├── .zshrc # shell config (aliases, tool integration)
├── .zsh_plugins.txt # Antidote plugin list
├── starship.toml # Starship prompt config
├── ghostty-config # Ghostty terminal config
├── linearmouse.json # Mouse customization (side buttons, acceleration)
└── docs/
├── inventory.md # full list of installed apps, tools, and runtimes
├── homebrew-cheatsheet.md
├── asdf-cheatsheet.md
├── ghostty-cheatsheet.md
├── git-cheatsheet.md
├── lazygit-cheatsheet.md
├── modern-cli-cheatsheet.md
├── claude-code-cheatsheet.md
└── cursor-cli-cheatsheet.md
A few choices that drive the rest of the setup. Full context is in the cheat sheets, but the gist:
- Ghostty over iTerm2: GPU-accelerated native macOS rendering, ~3× faster than iTerm2, zero-config
- Starship over Powerlevel10k: P10k is on life support; Starship is actively maintained and cross-shell
- Antidote over Oh My Zsh: faster startup, picks exactly what you need (OMZ is also defensible if you want its catalog)
- asdf over pyenv/nvm/rbenv: one tool replaces all of them; single
.tool-versionsfile per project - Zsh kept as login shell: POSIX-compatible (unlike Fish) and already the macOS default
- Claude Code via native installer, not Homebrew: the native installer auto-updates; the brew cask does not
- Docker for all local databases: Brewfile intentionally omits
postgresql/redisso they don't conflict with container-based local environments