Me dotfiles.
wget -qO- https://raw.githubusercontent.com/d-gubert/dotfiles/main/scripts/bootstrap.sh | bashThis clones the repo to ~/dev/dotfiles, installs git first if needed, and runs make all.
git clone https://github.com/d-gubert/dotfiles.git ~/dev/dotfiles
cd ~/dev/dotfiles# Install everything
make all
# Or install by category
make essential # core tools, shell, window manager
make development # dev runtimes and CLI tools
make utilities # optional quality-of-life toolsIndividual packages can also be installed on their own:
make install-neovim
make install-zsh # also installs oh-my-zsh and all pluginsStow would error out if directories already exist and are not owned by it, so we actually run stow as the first step in make.
If you add other configuration files, you can get stow to manage them as well by running make stow.
Stow is usually used by having one directory for each software you want to manage, with the internal structure of that directory being mirrored in the target. I didn't like that, so config files are grouped by operating system instead:
| Package | Contents |
|---|---|
common/ |
Everything OS-agnostic — zsh, tmux, wezterm, nvim, yazi, zellij, lazygit, herdr, kanata, starship, git, .claude/ |
ubuntu/ |
Linux only — i3, i3status, rofi, clipmenu, nushell, .Xresources, .xprofile |
mac/ |
macOS only |
make stows common plus the package matching uname, so a macOS machine never gets i3 or X11 config dropped into its home directory. The target uses stow -R, which also cleans up stale symlinks when a file moves between packages.
Prefer branching inside a shared config over copying it into both OS packages — most tools already have a mechanism for it (.zshrc checks uname, .tmux.conf has if-shell, .wezterm.lua has wezterm.target_triple). Only copy the whole file when the format has no conditionals, as with alacritty.toml.
For shell settings, each OS package ships a .zshrc.os fragment that common/.zshrc sources. That's where $OPEN_CMD (xdg-open vs open) and $CLIP_CMD (xclip vs pbcopy) are defined — use those variables rather than hardcoding either tool.
| Tool | Description | Installed via |
|---|---|---|
| homebrew | Package manager | install script |
| stow | Dotfiles symlink manager | brew |
| brave-browser | Brave Browser | install script |
| enpass | Password manager | apt (Linux) / brew cask (macOS) |
| zsh | Shell | brew |
| oh-my-zsh | Zsh framework | install script |
| powerlevel10k | Zsh theme and prompt | git |
| bat | cat with syntax highlighting |
brew |
| btop | System resource monitor | brew |
| docker | Container runtime | install script |
| ffmpeg | Audio/video processing | brew |
| fzf | Fuzzy finder | brew |
| glow | Markdown renderer for the terminal | brew |
| jq | JSON processor | brew |
| fd | Fast find replacement |
brew |
| kanata | Software keyboard remapper | brew (macOS also pulls in Karabiner-Elements for its VirtualHIDDevice driver, which needs manual approval) |
| neovim | Text editor | brew |
| ripgrep | Fast grep replacement (rg) |
brew |
| wezterm | GPU-accelerated terminal emulator | apt (Fury repo, Linux) / brew cask (macOS) |
| herdr | Terminal workspace manager for AI coding agents | brew |
| i3 (Linux only) | Tiling window manager, with dependencies below | apt |
| Dependency | Description |
|---|---|
| maim | Screenshot tool |
| pulseaudio | Audio control |
| playerctl | Media player control |
| xserver-xorg-input-libinput | X11 input driver |
| xinput | X11 input device utility |
| network-manager-applet | i3 tray icon (nm-applet) |
| blueman | Bluetooth manager |
| arandr | GUI front-end for xrandr (display configuration) |
| xclip | Clipboard CLI tool |
| slop | Screen area selector |
| rofi | General purpose menu selector |
| Name | Installed via |
|---|---|
| font-fira-code-nerd-font | brew |
| Plugin | Description |
|---|---|
| zsh-autosuggestions | Fish-like command suggestions |
| zsh-syntax-highlighting | Shell syntax highlighting |
| zsh-vi-mode | Better vi mode for zsh |
| zsh-autopair | Auto closes pairs of symbols |
| Tool | Description | Installed via |
|---|---|---|
| ast-grep | AST-based code search and rewrite (sg) |
brew |
| dvm | Deno version manager | install script |
| gh | GitHub CLI | brew |
| lazygit | Terminal UI for git | brew |
| meteor | Full-stack JavaScript framework | install script |
| node | JavaScript runtime | volta |
| tealdeer | Fast tldr client |
brew |
| vi-mongo | MongoDB TUI | brew |
| volta | Node.js toolchain manager | install script |
| Tool | Description | Installed via |
|---|---|---|
| carapace | Multi-shell completion generator | brew |
| jwt-ui | JWT TUI codec | brew |
| lazyjira | Jira TUI client | brew |
| tree-sitter | Parser generator and incremental parsing | brew |
| spotatui | Spotify TUI | prebuilt installer |
These have a make install-<tool> target but aren't pulled in by any aggregate target above — install them individually if you want them.
| Tool | Description | Installed via |
|---|---|---|
| alacritty | GPU-accelerated terminal emulator | apt (Linux) / brew cask (macOS) |
| tmux | The OG terminal multiplexer | brew |
| zellij | Terminal multiplexer | brew |
| rgx | Regex TUI | brew |
| sttr | String conversion CLI | brew |
| Plugin | Description |
|---|---|
| TPM | Tmux Plugin Manager |
| tmux-sensible | Sensible defaults for Tmux |
| tmux-resurrect | Save and restore sessions |
| tmux-yank | Better copy-mode |
| catppuccin | Catppuccin for Tmux |
containers/devcontainer/ holds one devcontainer definition for every checkout on the machine, and scripts/devbox runs it over whatever directory you're in:
cd ~/dev/RocketChat/worktrees/main
devbox up # build/start a container with this checkout mounted
devbox claude # Claude Code, --dangerously-skip-permissions, behind an egress firewall
devbox shell # zsh in thereNothing is added to the repo being worked on — no .devcontainer/, no committed compose file. The container is generic (Node/Yarn/pnpm through Volta); a repo's own setup lives in a profile under containers/devcontainer/projects/<name>/, picked by matching your path. Caches and logins (yarn, Claude Code, gh, Playwright browsers) are shared volumes, so you download and log in once for all checkouts.
Egress is default-deny, re-applied on every start, which is what makes --dangerously-skip-permissions a bounded risk. See containers/devcontainer/README.md.
A local Claude Code plugin that prefixes the current tmux window name with a glyph while Claude waits for you — ● when it finishes a turn, 🔔 when it needs permission — and clears it once you reply. Tweak the glyphs in the plugin's scripts/tmux-window-status.sh.
- Plugin:
common/.claude/skills/tmux-window-status/(hooks + script). It's dropped into the config dir'sskills/, so Claude Code auto-loads it astmux-window-status@skills-dir— no marketplace or install step, and nothing added to~/.claude/settings.json. - tmux side:
common/.tmux.confsplices a@status_glyphuser option into the catppuccin window label