Modern shell, CLI tools, editor, terminal, and macOS configuration. One command from fresh Mac to full parity.
# 1. Clone (needs Xcode CLT — bootstrap.sh installs it if missing)
git clone https://github.com/idan-ben-ami/dotfiles.git ~/.dotfiles
cd ~/.dotfiles
# 2. Bootstrap: installs Xcode CLT, Homebrew, python, rich, then runs install.py --all
./bootstrap.sh # core install
./bootstrap.sh --with-apps # core + personal apps (Slack, Spotify, Office, Figma, Postman)bootstrap.sh is idempotent — safe to re-run on an already-set-up machine.
./preview.sh # show what install.py --all would change
./preview.sh --with-apps # also preview personal appsRead-only — touches nothing. Reports symlink status, missing brew packages, and iTerm2 / macOS-defaults effects.
| Tier | When | Contains |
|---|---|---|
| Core (always) | ./bootstrap.sh or ./install.py --all |
Shell + CLI tools, terminals, dev runtimes, embedded toolchain, cloud CLIs, fonts, browsers (Brave), Cursor, Claude, Docker, Raycast, iTerm2 |
| Personal (opt-in) | --with-apps |
Slack, Spotify, Telegram, WhatsApp, Zoom, Figma, Postman, Microsoft Office |
See Brewfile and Brewfile.personal for the exact lists.
install.py manages these components (each idempotent):
| # | Component | What it does |
|---|---|---|
| 1 | shell |
Symlinks zshrc, zshrc_benami, starship.toml; sets zsh as login shell |
| 2 | cli_tools |
brew bundle from Brewfile (core formulae + casks + fonts) |
| 3 | git |
Symlinks git config + global gitignore |
| 4 | nvim |
Symlinks neovim config |
| 5 | tmux |
Symlinks tmux.conf, installs TPM |
| 6 | iterm2 |
Points iTerm2 at config/iterm2/ (auto-syncs prefs both ways) |
| 7 | zsh_plugins |
Installs Oh My Zsh + asksh |
| 8 | rust |
Installs Rust toolchain via rustup |
| 9 | macos |
Runs scripts/macos-defaults.sh — Finder, Dock, hot corners, keyboard shortcuts, symbolic hotkeys plist restore |
| 10 | personal_apps (opt-in) |
brew bundle from Brewfile.personal |
./install.py # interactive wizard
./install.py --all # core only, non-interactive
./install.py --all --with-apps # core + personal apps
./install.py --uninstall # remove symlinks (Brewfile installs stay)dot-files/
├── bootstrap.sh # fresh-Mac entrypoint (Xcode CLT → brew → python → rich → install.py)
├── preview.sh # read-only dry-run: predicts what install.py would change
├── install.py # component wizard / non-interactive installer
├── Brewfile # core formulae + casks + fonts
├── Brewfile.personal # opt-in apps (chat, Office, design, etc.)
├── config/
│ ├── zsh/
│ │ ├── zshrc # main config
│ │ ├── zshrc_benami # personal env, aliases, update_system fn
│ │ ├── aliases.zsh
│ │ └── functions.zsh
│ ├── nvim/init.lua
│ ├── git/{config,ignore}
│ ├── tmux/tmux.conf
│ ├── starship.toml
│ ├── iterm2/com.googlecode.iterm2.plist # iTerm2 reads from here
│ ├── raycast/com.raycast.macos.plist # snapshot only (Raycast Pro cloud-sync is canonical restore)
│ └── macos/symbolichotkeys.plist # symbolic hotkeys (15 disabled — Spaces nav, Launchpad, etc.)
└── scripts/
└── macos-defaults.sh # Finder, Dock, hot corners, NSUserKeyEquivalents, hotkeys plist restore
If you previously installed apps manually (dragged into /Applications) and now want brew bundle to manage them, you'll hit "App already exists" errors on first run. One-time fix:
brew install --cask --adopt \
iterm2 raycast brave-browser cursor claude docker \
slack telegram whatsapp zoom spotify figma postman--adopt tells brew to take over the existing app in place (no re-download). After this, brew bundle install is happy and future updates flow through brew.
Fresh-Mac installs don't need this step — bootstrap.sh handles the cask installs cleanly because the apps don't exist yet.
| Item | Where it lives | Restore on new Mac |
|---|---|---|
SSH keys (~/.ssh/id_*) |
1Password / manual | Copy from 1Password → chmod 600 |
API key secrets (~/.zshrc.secrets, ~/.secrets/google-ai.env) |
1Password | Copy manually |
| pyenv-installed Python versions | Auto when needed | pyenv install 3.14.2 (referenced in zshrc_benami) |
| Raycast extensions/config | Raycast Pro cloud sync | Sign in to Raycast → extensions sync down |
| iCloud-synced app state | iCloud | Sign in to iCloud |
Project repos (~/projects/*) |
GitHub | git clone each |
The config/raycast/com.raycast.macos.plist snapshot is a "last-resort backup"; cloud sync is faster.
| Key | Action |
|---|---|
Ctrl+a | |
Split vertical |
Ctrl+a - |
Split horizontal |
Ctrl+h/j/k/l |
Navigate panes |
Ctrl+a c |
New window |
Ctrl+a n/p |
Next/prev window |
Ctrl+a d |
Detach |
Ctrl+a [ |
Copy mode |
Ctrl+a r |
Reload config |
After install, press Ctrl+a I to install tmux plugins.
# Navigation
.. # cd ..
z <query> # Jump to directory (zoxide)
gop # cd ~/projects
gopg # cd ~/projects/playground
# Modern replacements
ls / ll # eza with icons / git status
cat # bat with syntax highlighting
# Git
g / gs / lg # git / git status / lazygit
# Tmux
t / ta / tn # tmux / attach / new
# FZF
Ctrl+R # Fuzzy history
Ctrl+T # Fuzzy filecd ~/.dotfiles
git pull
./install.py --all # picks up new components
update_system # macOS + brew + OMZ in one shot (alias from zshrc_benami)- Local-only overrides:
~/.zshrc.local(sourced last by zshrc) - Secrets:
~/.zshrc.secrets(sourced before plugins; not in repo) - Per-project shell config: symlink
~/.zshrc_<project>→<repo>/shellrc.sh(pattern used by_personal,_ventures,_alora)