Personal config for shells, editors, tmux, and git. Bash, zsh, vim, and nvim
are all first-class peers — no aliasing, no preferred fork. Installed via
symlinks declared in manifest.txt. macOS and Linux supported.
git clone <this-repo> ~/dotfiles
cd ~/dotfiles
./packages.sh --bootstrap-brew # one-time on macOS w/o brew
./packages.sh --install-all # install required + optional packages
./setup.sh --apply # link rc files into $HOME
./vim-plugins.sh # bootstrap Vundle + run :PluginInstall
./doctor.sh # health check (links, syntax, perf budgets)Conflicting real files are moved to ~/.dotfiles-backup/<timestamp>/ before
linking. Conflicting symlinks abort with a warning unless --force is passed.
On a fresh macOS box with no package manager, packages.sh automatically
bootstraps Homebrew under ~/.local/homebrew using the "untar anywhere"
install from https://docs.brew.sh/Installation so brew never needs sudo.
| Path | Purpose |
|---|---|
manifest.txt |
Declares every link (<os> link <src> <target>) and husk slot. |
packages.txt |
Expected commands and their package names per OS / package manager. |
setup.sh |
Idempotent linkfarm installer (--dry-run/--apply/--check/--uninstall). |
packages.sh |
Verifies / installs commands declared in packages.txt. |
doctor.sh |
Health check (commands, links, rc syntax, editors, perf budgets). |
vim-plugins.sh |
Bootstraps Vundle and runs :PluginInstall. |
lib/common.sh |
Helpers for the install/check scripts (NOT sourced by shells). |
lib/sh/common.sh |
Shared interactive-shell layer (PATH/env/aliases/fzf) — see below. |
vim/shared/common.vim |
Shared vim+nvim base (options/mappings/colors/autocmds). |
tests/snapshot.sh |
Prompt format snapshot tests; fixtures in tests/fixtures/. |
bin/ |
Personal scripts; auto-added to PATH by lib/sh/common.sh. |
linux/ |
Linux-only files (.xinitrc, .Xresources, .Xmodmap). |
local_dotfiles/ |
Per-machine overrides. Git-ignored. Never commit. |
.bashrc, .zshrc, .vimrc, nvim/init.lua |
The four first-class entry points. |
Both shells share lib/sh/common.sh for PATH, environment, aliases, dircolors,
and fzf integration. Native sections live in each rc file:
.bashrc: history options, prompt (\[\e[..m\]escapes — zero subshells per redraw),set -o vi, bash-completion@2 (sourced from Homebrew when present), bash-4-onlyshoptflags gated by$BASH_VERSINFO..zshrc: history options, prompt (%F{...}%f/%m/%~/%*/%!— zero subshells per redraw), compinit with daily cache, completion menu, vi-mode keymap.
Cold-start budgets (verified each doctor.sh run): bash <150ms, zsh <150ms.
Both editors share vim/shared/common.vim for options, mappings, colors, and
filetype tweaks. Native sections live in each entry point:
.vimrc: Vundle plugin block, YouCompleteMe, NERDTree, Mundo, latex-suite, airline tabline, cscope.nvim/init.lua: lazy.nvim spec (require("config.lazy")), nvim-cmp completion, nvim-only wrap-width shading via decoration provider.
Cold-open budgets: vim <100ms, nvim <100ms. The g:dotfiles_user global
(default $USER) parametrizes the <leader>o TODO map.
.ideavimrc silent! sources vim/shared/common.vim so JetBrains IDEs get
the same mappings (minus features IdeaVim doesn't implement).
Each first-class entry point ends with a guarded source of a matching file
under ~/.local/dotfiles/:
# .bashrc
[ -r ~/.local/dotfiles/.bashrc ] && source ~/.local/dotfiles/.bashrc" .vimrc
if filereadable(expand('~/.local/dotfiles/.vimrc'))
source ~/.local/dotfiles/.vimrc
endif-- nvim/init.lua
local husk = vim.fn.expand('~/.local/dotfiles/init.lua')
if vim.fn.filereadable(husk) == 1 then dofile(husk) end.gitconfig follows the same pattern via [include] path = ~/.local/dotfiles/.gitconfig,
which is where per-machine user.name / user.email live.
setup.sh creates these as empty husks so the source is a no-op on a fresh
machine. Drop machine-specific tweaks (work env vars, identity, host paths,
GUI font sizes) into the husk; nothing under local_dotfiles/ is committed.
Example husk contents:
# ~/.local/dotfiles/.zshrc
export PATH="${PATH}:${HOME}/.bun/bin"
[ -s "${HOME}/.bun/_bun" ] && source "${HOME}/.bun/_bun"# ~/.local/dotfiles/.gitconfig
[user]
name = Your Name
email = you@example.comtests/snapshot.shcaptures the literalPS1/PROMPT/RPROMPTstrings after each shell sources its rc files and diffs againsttests/fixtures/. Runtests/snapshot.sh --updateafter an intentional prompt change.doctor.shmeasures cold-start times for bash/zsh/vim/nvim, fails if any exceeds its budget, and appends each measurement to~/.local/dotfiles/perf.logastimestamp,tool,msso regressions become visible over time.
- Drop the file in the repo.
- Add a row to
manifest.txt:Useall link .my-new-rc .my-new-rcmacosorlinuxinstead ofallfor OS-gated files. For directories, pointtargetat the destination (e.g.nvim→.config/nvim). ./setup.sh --apply.
If the file should support per-machine overrides, also add all husk - .my-new-rc and source ~/.local/dotfiles/.my-new-rc at the bottom of the
file itself.
setup.shworks from any cwd (uses its own location, notpwd).- Default
setup.shmode is dry-run — no flag, no changes. - Solarized is the single source of truth for colors:
dircolors/dircolors.ansi-darkfor ls,vim-colors-solarized(Vundle) for vim,solarized.nvim(lazy) for nvim, hand-rolled palette in.tmux.conf.