Personal macOS dotfiles and machine bootstrap for David Stosik.
This repository is intentionally small, local, and personal. It is meant to make a fresh personal Mac converge toward David's preferred development environment, not to become a general provisioning framework.
Out of scope for now:
- Linux support
- remote provisioning
- Ansible
- mitamae
Bootstrap a fresh machine with:
curl -fsSL https://raw.githubusercontent.com/davidstosik/dotfiles-next/main/bootstrap | bashFrom an already-cloned checkout, run the same full install with:
./bootstrapbootstrap has no subcommands or flags. It follows the canonical install path and logs commands as it goes.
bootstrap is the small shell part of the install. It:
- refuses to run when sourced
- ensures it is running on macOS
- requests sudo credentials with
sudo -v - installs/loads Homebrew if needed
- updates Homebrew when Homebrew was already installed
- installs minimal bootstrap tools with
brew install git mise - finds the current checkout, or clones/updates this repository at
~/.dotfiles - trusts and installs the repo Ruby from
.mise.tomlwith mise - runs the Ruby installer with
mise exec --cd "$ROOT" -- ruby "$ROOT/dotfiles"
The split is intentional: keep shell as small as practical, then use Ruby for the real installer logic.
dotfiles / lib/dotfiles/app.rb applies the managed machine state. It:
- installs packages/apps from
Brewfile - upgrades Homebrew packages
- links files from
home_symlinks/into$HOME - installs TPM for tmux plugin management
- installs tmux plugins declared in
~/.tmux.conf - installs vim-plug
- restores Vim plugins from
vim-plug-snapshot.vim - installs global mise tools from
mise-global-tools.txt - runs
mise reshim
home_symlinks/ is the source of truth for files that should appear under $HOME.
Each non-directory under home_symlinks/ is linked to the same relative path under $HOME:
home_symlinks/.gitconfig -> ~/.gitconfig
home_symlinks/.vimrc -> ~/.vimrc
home_symlinks/.zprofile -> ~/.zprofile
home_symlinks/.zshrc -> ~/.zshrc
home_symlinks/.tmux.conf -> ~/.tmux.conf
home_symlinks/.tmux.mac.conf -> ~/.tmux.mac.conf
home_symlinks/.config/gh/config.yml -> ~/.config/gh/config.yml
home_symlinks/.config/git/ignore -> ~/.config/git/ignore
home_symlinks/.config/ghostty/config.ghostty -> ~/.config/ghostty/config.ghostty
home_symlinks/.config/mise/config.toml -> ~/.config/mise/config.toml
home_symlinks/.config/nvim/init.vim -> ~/.config/nvim/init.vim
Symlinks inside home_symlinks/ are mirrored as symlinks in $HOME. The current compatibility example is:
home_symlinks/.gitignore -> .config/git/ignore
~/.gitignore -> .config/git/ignore
Existing files are never deleted. If a target already exists and is not the expected symlink, it is moved aside first:
~/.gitconfig -> ~/.gitconfig.backup.YYYYMMDD-HHMMSS
Stale managed symlinks are reported but left in place.
Brewfile is the target-machine package/app source of truth.
Current highlights:
- Git/GitHub:
git,gh - shell/editor/search tools:
bash,bat,fd,fzf,ripgrep,the_silver_searcher,tmux,vim,neovim,watch - runtime manager:
mise - Ruby build dependencies:
libyaml,openssl@3,readline,autoconf - casks:
1password-cli,ghostty,font-monaspice-nerd-font
Brewfile.dev is separate and contains tools needed to develop/test this repository, currently Tart.
.mise.tomlpins the Ruby used by this repository to Ruby 4.x.home_symlinks/.config/mise/config.tomlmanages global user runtimes, currently Ruby 4 and Node 24.mise-global-tools.txtlists global tools installed withmise use -g.- npm-backed global tools should use mise specs such as
npm:<package>@<version>rather thannpm install -g. npm:@mariozechner/pi-coding-agent@latestis currently installed this way.
The Ruby installer reads the global Node version from home_symlinks/.config/mise/config.toml before installing npm-backed mise tools, so the Node major version is not duplicated in Ruby code.
Managed Git config includes:
- David's GitHub noreply identity
~/.gitconfig.localinclude support- default branch
main - rebase-oriented pulls
- autosquash/autostash/updateRefs
rererezdiff3merge conflict style- pruning
pushf = push --force-with-lease- global ignore at
~/.config/git/ignore, with~/.gitignoreas a symlink for muscle memory
Managed shell preferences include:
EDITOR=vim- shared/incremental history
- emacs-style shell keybindings
- colored macOS/BSD
ls llandla='ll -a'vcs_infoprompt- mise activation when available
- automatic tmux attach/create when outside tmux
$HOME/.local/bininPATH
The current .zshrc is personal and includes machine/owner-specific details such as a VPN helper and /Users/sto/.opencode/bin.
Managed tmux preferences include:
- prefix
C-a - windows/panes numbered from 1
- mouse support
- vi copy mode
- large scrollback
- macOS clipboard integration through
.tmux.mac.conf - TPM plugins installed by the Ruby installer
- Tokyo Night status bar with glyphs, slanted separators, date/time, and battery
The Tokyo Night plugin is pinned:
set -g @plugin 'fabioluciano/tmux-tokyo-night#v1.11.0'This is deliberate. The upstream repository redirected/rebranded to tmux-powerkit, which broke fresh installs. Do not unpin casually.
TPM initialization should remain at the bottom of .tmux.conf:
run '~/.tmux/plugins/tpm/tpm'Ghostty/tmux modified-key support is enabled for TUIs such as Pi. If a terminal gets stuck sending weird sequences for modified keys, reset the current terminal connection with:
printf '\033[>4;0m'Vim is the default editor. Neovim intentionally sources the same Vim config instead of using LazyVim:
set runtimepath^=~/.vim runtimepath+=~/.vim/after
let &packpath = &runtimepath
source ~/.vimrcThe managed setup uses vim-plug, restores plugins from vim-plug-snapshot.vim, maps Ctrl-P to FZF :Files, and enables persistent undo for Vim/Neovim.
Ghostty config lives at:
~/.config/ghostty/config.ghostty
It currently uses TokyoNight, Monaspace Argon Var, font features, 13pt font, and 0.90 background opacity.
Run fast local tests:
./bin/testRun the full bootstrap flow in disposable Tart VMs:
./bin/tart-testThis is slow and best treated as an end-to-end smoke test for risky bootstrap changes.
Clone/start a manual Tart VM and SSH into it for debugging:
./bin/tart-shellManage existing local Tart VMs by stable number:
./bin/tart-vms list
./bin/tart-vms name 2
./bin/tart-vms ssh 2
./bin/tart-vms restart 2 --no-graphics # mounts the current directory by default
./bin/tart-vms restart 2 --graphics
./bin/tart-vms restart 2 --dir repo:/path/to/repo
./bin/tart-vms restart 2 --no-dir
./bin/tart-vms delete 2 --forceUseful Tart overrides:
TART_BASE_VM=clean-tahoe TART_KEEP_VM=1 ./bin/tart-testTart defaults include:
TART_BASE_VM=clean-tahoe
TART_BASE_IMAGE=ghcr.io/cirruslabs/macos-tahoe-vanilla:latest
TART_SSH_USER=admin
TART_SSH_PASSWORD=admin
bootstrap— curl-pipe friendly shell bootstrapdotfiles— Ruby installer entrypointlib/dotfiles/app.rb—Dotfiles::AppimplementationBrewfile— target macOS packages/appsBrewfile.dev— repo development/test packages.mise.toml— repo Ruby 4.x configurationmise-global-tools.txt— global mise toolsvim-plug-snapshot.vim— pinned Vim plugin snapshot restored by./dotfileshome_symlinks/— files and symlinks linked into$HOMEtest/— Minitest coveragetest/support/fake_bin/— fake commands used by bootstrap testsbin/test— fast test entrypointbin/tart-test— end-to-end Tart VM test suite runnerbin/tart-shell— manual Tart VM shell for debuggingbin/tart-vms— numbered Tart VM management helper
- SSH key generation/GitHub upload
- opt-in macOS defaults
- possible local override conventions such as
~/.zshrc.local - possible safer behavior for updating an existing
~/.dotfilescheckout with local changes - possible explicit directory-symlink support if whole config directories need to be managed later