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/ |
Debian/Ubuntu only — i3, i3status, rofi, clipmenu, nushell, .Xresources, .xprofile, and the Hyprland session (hypr, waybar, mako), plus the kanata login service. See docs/i3.md |
arch/ |
Arch/Omarchy only — Wayland clipboard, mise toolchain wiring, the kanata login service, and the Omarchy desktop config |
mac/ |
macOS only |
make stows common plus the package for this OS, so a macOS machine never gets i3 or X11 config dropped into its home directory. uname only separates Darwin from Linux, so the two Linux packages are split on /etc/os-release instead: ID or ID_LIKE naming arch selects arch/, anything else gets ubuntu/. Omarchy reports ID=omarchy with ID_LIKE=arch, which is why the match reads both fields. 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 on X11, wl-copy on Wayland, pbcopy on macOS) are defined, along with the per-OS toolchain wiring — Homebrew's shellenv on Ubuntu and macOS, mise activate on Arch — use those variables rather than hardcoding either tool.
| Doc | Contents |
|---|---|
| docs/kanata.md | kanata permissions, the login and boot services, the home-row mods |
| docs/i3.md | The i3 session on Ubuntu and its dependencies |
| docs/hyprland-ubuntu.md | The Hyprland session on Ubuntu |
| docs/omarchy.md | Arch packages and the Omarchy desktop preferences |
The "Installed via" column below describes Ubuntu and macOS, which both use
Homebrew. Arch does not. make picks a package backend from the OS family:
| Family | Backend | Bootstrap |
|---|---|---|
darwin, debian |
Homebrew, in mk/brew.mk |
installs brew first |
arch |
omarchy pkg add and omarchy pkg aur add, in mk/omarchy.mk |
none needed |
A backend defines PKG_PREREQ, PKG_INSTALL_CMD and any PKG_<tool> name
overrides. The main Makefile reads those names and never mentions a package
manager. To add a tool that is only a package name, put it in one of the
*_TOOLS lists and add a PKG_<tool> line wherever the name differs.
Some tools come from other sources on Arch. See docs/omarchy.md.
| Tool | Description | Installed via |
|---|---|---|
| homebrew | Package manager | install script |
| stow | Dotfiles symlink manager | brew |
| 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 | package on Arch, brew elsewhere (see docs/kanata.md) |
| neovim | Text editor | brew |
| ripgrep | Fast grep replacement (rg) |
brew |
| wezterm | GPU-accelerated terminal emulator | apt (Fury repo, Linux — the wezterm-nightly package, see wezterm on Wayland) / brew cask (macOS) |
| herdr | Terminal workspace manager for AI coding agents | brew |
| i3 (Linux only) | Tiling window manager, with the dependencies in docs/i3.md | apt |
| 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 |
common/.zshrc also loads the built-in copybuffer, copyfile, copypath,
gh and git plugins, and one of z or zoxide.
Both define a z command, so the file loads the plugin only when zoxide is
absent. Omarchy ships zoxide in omarchy-base.packages, so Arch gets zoxide
and the other platforms keep the plugin. Install zoxide anywhere to switch.
| 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 |
| hyprland (Ubuntu only) | Wayland compositor, with the dependencies in docs/hyprland-ubuntu.md | apt |
| 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