Personal dotfiles, managed with stow.
Each top-level directory is a stow package mirroring the layout it should have
under $HOME (e.g. stow zsh symlinks zsh/.zshrc to ~/.zshrc).
A Brewfile at the repo root pins the Homebrew packages I rely on; run
brew bundle from this directory to install everything.
Browsable docs (one page per package, auto-generated from each subdir's README): https://jacob-delgado.github.io/dotfiles/.
- Apply pending changes
- Subdirectories
- Modern CLI tools — zoxide, atuin, eza, git-delta, yq, kubectx, stern, lazygit, dust, btop, watchexec, hyperfine, tldr
- Installing on a new machine
- Updating
Manual steps to activate recent edits on this machine (config is committed; these are the local actions to make each take effect). Tick off and prune as you go.
-
Open a new shell (
exec zsh) — activates the fzf Dracula colors, the git shell aliases (gst/gco/…), the ezalsfunction +ltrh, the~/.local/binPATH, and the p10k rainbow+Dracula prompt (best viewed in a kitty terminal). -
vim — install the plugin set (
~/.vim/pluggedmay be empty):vim +"PlugInstall --sync" +qallCovers
vim-snippets,vim-unimpaired,vim-eunuch; molokai was dropped, so nothing to clean. Open a.gofile to confirm dracula. -
nvim (new package — vim stays the default
$EDITOR) — stow, then install plugins headlessly:stow nvim nvim --headless "+Lazy! sync" +qaTreesitter compiles parsers on first run (needs a C compiler). For the optional Go tools, open a
.gofile and run:GoInstallBinaries(go.nvim is filetype-loaded, so it isn't available on an empty buffer). Commit the generatednvim/.config/nvim/lazy-lock.jsonto pin plugin versions. See nvim/README.md. -
lazygit — restart
lazygitand eyeball the official theme. -
ripgrep / gh — no action needed; apply on next invocation.
On a fresh machine, also recreate
~/.gitconfig.local(untracked) with the gh credential helper — see git/README.md.
Each is a stow package; see its README for what's non-default.
| Package | Stows to | README |
|---|---|---|
atuin/ |
~/.config/atuin/config.toml |
atuin/README.md |
bat/ |
~/.config/bat/ |
bat/README.md |
btop/ |
~/.config/btop/ |
btop/README.md |
direnv/ |
~/.config/direnv/ |
direnv/README.md |
editorconfig/ |
~/.editorconfig |
editorconfig/README.md |
fzf/ |
~/.fzf.zsh |
fzf/README.md |
gh/ |
~/.config/gh/config.yml |
gh/README.md |
git/ |
~/.gitconfig |
git/README.md |
gitignore_global/ |
~/.gitignore_global |
gitignore_global/README.md |
hadolint/ |
~/.config/hadolint/ |
hadolint/README.md |
lazygit/ |
~/.config/lazygit/ |
lazygit/README.md |
lefthook/ |
(template, not stowed) | lefthook/README.md |
mise/ |
~/.config/mise/config.toml |
mise/README.md |
nvim/ |
~/.config/nvim/ |
nvim/README.md |
p10k/ |
~/.p10k.zsh |
p10k/README.md |
ripgrep/ |
~/.config/ripgrep/ |
ripgrep/README.md |
shellcheck/ |
~/.shellcheckrc |
shellcheck/README.md |
task/ |
~/.config/task/ |
task/README.md |
tig/ |
~/.tigrc |
tig/README.md |
tmux/ |
~/.tmux.conf |
tmux/README.md |
vim/ |
~/.vimrc, ~/.vim/ |
vim/README.md |
yamllint/ |
~/.config/yamllint/ |
yamllint/README.md |
zsh/ |
~/.zshrc |
zsh/README.md |
Conventions for the whole repo: CLAUDE.md.
The tools below are the recent additions on top of the standard fzf/bat/fd/ripgrep set. Each section covers what it does, the everyday usage, and any extra configuration needed.
A cd replacement that remembers directories you visit and jumps to them by
substring.
z dotfiles # jump to the most-recent dir matching "dotfiles"
z dot files # multi-keyword: match dirs scoring on both
zi # interactive fzf picker over your historyConfig: eval "$(zoxide init zsh)" (already wired up in zsh/.zshrc).
The init hook is what records every cd.
SQLite-backed history with full-text search, per-directory context, and
optional E2E-encrypted sync across machines. Replaces Ctrl+R.
atuin import auto # one-time import of existing zsh history
atuin search kubectl # CLI search
atuin stats # top commands, busiest hours, etc.
# Ctrl+R opens the atuin TUI; Up arrow keeps prefix-search behaviorConfig: eval "$(atuin init zsh)" (already wired up). Run
atuin register if you want sync; otherwise everything stays local.
Coloured ls with git status, tree view, and saner defaults.
ls # alias -> eza
ll # eza -l --git --group-directories-first
la # ll + hidden
lt # tree view, depth 2, respects .gitignoreConfig: aliases live in zsh/.zshrc and are only set when eza is on
PATH, so other machines aren't broken by their absence.
Pager that renders git diff/show/log/blame with syntax highlighting and optional side-by-side view.
git diff # now uses delta
git show HEAD
git log -pConfig: delta is installed but not yet wired into git. Add to
~/.gitconfig:
[core]
pager = delta
[interactive]
diffFilter = delta --color-only
[delta]
navigate = true # n / N jump between hunks
side-by-side = true
line-numbers = true
[merge]
conflictstyle = zdiff3Same query language as jq, applied to YAML (and TOML, XML, etc.). Essential for k8s manifests and helm values.
yq '.spec.template.spec.containers[].image' deploy.yaml
yq -i '.image.tag = "v1.2.3"' values.yaml # in-place edit
kubectl get pod foo -o yaml | yq '.status.conditions'Config: none.
Bundled together in the kubectx formula. Pairs with kube-ps1 so your
prompt always shows the current target.
kubectx # list contexts, current marked
kubectx prod # switch to "prod"
kubectx - # toggle back to previous
kubens kube-system # switch namespace in current context
kubens - # toggle previous namespaceIf fzf is on PATH the bare kubectx/kubens commands become interactive
pickers. Config: none.
Tails logs across pods matching a label/selector/name regex with colour-coded
output per pod. Vastly nicer than kubectl logs -f for anything multi-replica.
stern api # tail every pod whose name matches "api"
stern -l app=api --tail 100 # last 100 lines, then follow
stern api --since 5m
stern api -n stagingConfig: none. Completions auto-load via oh-my-zsh's kubectl plugin.
Interactive terminal UI for staging hunks, rebasing, cherry-picking, branch
ops. Complements tig (which is read-focused for log/blame).
lazygit # launch in current repoInside: space stage, c commit, P push, p pull, r rebase menu,
? for keybindings. Config: none required; per-user settings live at
~/.config/lazygit/config.yml if you want to customize.
Sorted, colourised disk usage breakdown that's easier to scan than
du -sh *.
dust # current dir
dust -d 3 ~/dotfiles # depth 3
dust -r # reverse (largest at bottom)Config: none.
Process / CPU / memory / network monitor with mouse support and themes.
btopPress q to quit, m for memory view, p to cycle themes. Config:
none; per-user config at ~/.config/btop/btop.conf.
Generic file watcher. Drop-in for ad-hoc dev loops without writing Makefile glue.
watchexec -- go test ./...
watchexec -e go,proto -- make build # only .go and .proto
watchexec --restart -- ./server # restart long-running processConfig: none.
Statistically rigorous timing of CLI commands with warmup, multiple runs, and comparisons.
hyperfine 'fd .' 'find .'
hyperfine --warmup 3 './build.sh'
hyperfine -n new -n old 'cmd-v2' 'cmd-v1' --export-markdown bench.mdConfig: none.
Crowdsourced quick-reference pages — usually faster than man when you just
need example invocations.
tldr --update # one-time: fetch the pages cache
tldr tar
tldr git rebaseConfig: none.
git clone <this-repo> ~/dotfiles
cd ~/dotfiles
./bootstrap-debian.sh # apt + mise + OMZ + plugins + stowNo Homebrew on Linux — apt covers the base system and mise
manages the language runtimes and modern CLIs. The script is idempotent
(safe to re-run) and fully non-interactive, so the same entry point is what
the headless devbox VM flow runs
over SSH after cloning this repo to ~/.dotfiles.
Pushing dotfiles changes back from a dev VM. The bootstrap routes GitHub pushes over SSH (fetch stays HTTPS) and pre-trusts GitHub's host key, so a dev VM can push commits back with no key or token stored on it — auth rides your forwarded SSH agent. Agent forwarding is a client-side setting the script can't do, so set the laptop side up once:
-
Confirm your GitHub key is in the local agent:
ssh-add -l(add it withssh-add ~/.ssh/id_ed25519if it's missing). -
Connect with agent forwarding:
ssh -A <vm>, or make it the default in~/.ssh/config:Host devvm-* # or the specific host/IP ForwardAgent yes -
On the VM:
cd ~/.dotfiles && git commit -am "…" && git push. The push authenticates through the forwarded key; nothing lands on the VM. -
Security: only forward your agent to hosts you trust — while you're connected, a root user on the box can use the socket. Fine for your own dev VMs; don't
-Ainto shared machines.
git clone <this-repo> ~/dotfiles
cd ~/dotfiles
./bootstrap-macos.sh # brew bundle + stow (incl. mise) + mise lint tools + OMZHomebrew is the source of truth for the runtimes and CLIs (brew bundle from
the Brewfile). The repo's lint tools — the ones lefthook
and CI run — are the exception: bootstrap-macos.sh stows mise and installs
just those via mise, so they match CI and so editorconfig-checker provides
the ec binary Homebrew's formula lacks (see mise/README.md).
Idempotent; installing Homebrew itself on a bare machine may prompt for the
Xcode Command Line Tools.
Two follow-ups the script prints as next steps:
task mise— installsmdformat(+ itsgfm/tablesplugins) viapipx. It can't be a mise tool, so the step above skips it, but the Markdown pre-commit needs it.lefthook install(optional) — enables the git pre-commit hook that runs the linters. Commit from an interactive shell so mise's tools are onPATH.
git clone <this-repo> ~/dotfiles
cd ~/dotfiles
brew bundle # runtimes, CLIs, casks
stow zsh vim tmux fzf mise # symlink whichever packages you want
# Lint tools aren't in the Brewfile — install them via mise (matches CI):
mise install actionlint editorconfig-checker lefthook shellcheck shfmt taplo \
npm:markdownlint-cli2 pipx:yamllint
task mise # mdformat (+plugins) via pipxThe .zshrc conditionally loads these third-party plugins if their
directories exist under ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/.
Install with:
ZSH_CUSTOM="${ZSH_CUSTOM:-$HOME/.oh-my-zsh/custom}"
git clone --depth=1 https://github.com/zsh-users/zsh-autosuggestions "$ZSH_CUSTOM/plugins/zsh-autosuggestions"
git clone --depth=1 https://github.com/Aloxaf/fzf-tab "$ZSH_CUSTOM/plugins/fzf-tab"
git clone --depth=1 https://github.com/MichaelAquilina/zsh-you-should-use "$ZSH_CUSTOM/plugins/you-should-use"What they do:
- zsh-autosuggestions — suggests the rest of a command in grey as you type, based on history. Right-arrow accepts.
- fzf-tab — replaces zsh's default completion menu with an fzf picker with preview. Tab → interactive list.
- you-should-use — reminds you when you typed the long form of a command you've aliased.
Keep the toolchain current with go-task — the bare
task command (see task/README.md) — from anywhere inside
this repo:
task update # everything: mise tools, then vim & nvim plugins
task mise # mise upgrade + pipx mdformat (markdown lint/format tooling)
task vim # just vim-plug: :PlugUpdate (headless, via tmux)
task nvim # just lazy.nvim: :Lazy sync (rewrites lazy-lock.json)task mise runs mise upgrade, which moves the latest/lts pins forward —
unlike mise install, which only fetches what's missing. It also refreshes the
markdown tooling that gates commits: markdownlint-cli2 (a mise tool) rides
along in mise upgrade, while mdformat + its gfm/tables plugins are
installed-or-upgraded via pipx (they can't live in mise). After task nvim,
commit the regenerated nvim/.config/nvim/lazy-lock.json to pin the new
plugin versions.