This repository contains my personal dotfiles managed by chezmoi, supporting both Linux and macOS. Zsh is the shell.
- 🦓 Zsh configuration with cross-platform support
- 🍎 macOS-specific optimizations (Homebrew, Ghostty, etc.)
- 🐧 Linux compatibility
- 🛠️ Useful shell functions and utilities
- 📦 Template-based configuration for different environments
- 🔧 mise for language runtimes (Node.js always; Go, Java, Python opt-in per machine; bun self-managed)
- 🌊 mise aqua backend for the modern CLI suite (bat, fd, eza, kubectl, gh, etc.)
- 🔍 Modern CLI tools: Complete suite of replacements for traditional Unix tools
- ⭐ Starship cross-shell prompt with git integration
- 📜 Atuin for enhanced shell history (better search, statistics, deduplication)
- 🧹 Automatic cleanup of old tool installations
- Git
- curl or wget
- sudo access (for package installation)
# One-liner installation
curl -fsLS https://github.com/ghraw/ryanlewis/dotfiles/main/install.sh | bashOr if you prefer using wget:
wget -qO- https://github.com/ghraw/ryanlewis/dotfiles/main/install.sh | bashIf you already have chezmoi installed:
chezmoi init --apply ryanlewis/dotfilesControl installation behavior with these environment variables:
# Skip all mise-managed tools (runtimes + CLI suite) for a fast, minimal bootstrap
QUICK_INSTALL=true curl -fsLS https://github.com/ghraw/ryanlewis/dotfiles/main/install.sh | bash
# Provide git config to avoid prompts
CHEZMOI_USER_NAME="Your Name" CHEZMOI_USER_EMAIL="you@example.com" \
curl -fsLS https://github.com/ghraw/ryanlewis/dotfiles/main/install.sh | bashThe installation process automatically sets up:
- Zsh - Interactive shell with plugins and abbreviations
- chezmoi - Dotfiles manager
- mise - Universal version manager
- Modern CLI tools:
eza- Betterlsbat- Bettercatwith syntax highlightingripgrep- Bettergrepfd- Betterfindfzf- Fuzzy finderzoxide- Smartercdstarship- Cross-shell promptbtop- Bettertopduf- Betterdfdust- Betterdugum- Pretty shell scriptskubectl- Kubernetes CLIkubectx- K8s context switcherkubens- K8s namespace switcher- And more...
- Language runtimes:
- Node.js — always installed via mise (the dotfiles tooling depends on it)
- Bun — installed by its official installer and self-updated via
bun upgrade(deliberately not pinned in mise) - Go, Java (Temurin), Python/Miniconda — opt-in per machine, chosen at
chezmoi init(default off, so VMs/CI stay lean). See Per-machine language runtimes.
chezmoi diff- See what changes would be madechezmoi apply- Apply the configurationchezmoi add ~/.config/zsh/functions/foo.zsh- Add a new filechezmoi edit ~/.zshrc- Edit a managed filechezmoi update- Pull latest changes and apply
This configuration includes several useful Zsh functions:
mkcd <dir>- Create a directory and cd into itbackup <file>- Create a timestamped backup of a fileextract <archive>- Extract various archive formatsupdate- Update mise tools, system packages (brew/apt/dnf/pacman) and bun, then restart the shellports- Show listening portsmyip- Display local and public IP addressesyank- Copy text to clipboard via OSC 52 (works over SSH)mise-setup- Show configured mise tools and install hintsmise-install-latest- Install latest stable versions of all toolsmise-update- Update mise and all pluginsask <question>- One-shot question to Claude, answer straight to stdout (askhfor Haiku,askofor Opus,-wto allow web search)
The yank function enables clipboard access from anywhere, even over SSH:
# Copy command output
git diff | yank
cat ~/.ssh/id_rsa.pub | yank
echo "some text" | yank
# Works over SSH - copies to your local clipboard!
ssh server "cat /var/log/nginx/error.log | yank"Why use yank?
- Works over SSH without X11 forwarding
- Universal solution across different terminals (iTerm2, Terminal.app, Alacritty, Windows Terminal, etc.)
- No need for platform-specific tools (pbcopy/xclip)
fcd- Fuzzy change directory with previewfopen- Fuzzy find and open file in editorfkill- Fuzzy find and kill processesfgrep <term>- Fuzzy grep with file previewfgit <cmd>- Interactive git operations:fgit add- Stage files interactivelyfgit checkout- Checkout branches with previewfgit log- Browse git log with commit previewfgit diff- View file diffs interactively
When running on macOS, additional features are enabled:
- Homebrew integration
- GNU coreutils in PATH (if installed)
- macOS-specific aliases:
flushdns- Flush DNS cacheql <file>- Quick Look previewshowfiles/hidefiles- Toggle hidden files in Findershowdesktop/hidedesktop- Toggle desktop iconsafk- Start the screensaver (lock away from keyboard)cleanup- Remove .DS_Store files
This configuration includes a comprehensive suite of modern CLI tools:
- eza - Modern
lsreplacement- Icons, colors, git integration, tree view
- Aliased to replace all
lsvariants
- fd - Better
find- Simple syntax, respects .gitignore
- Default file finder for fzf
- ripgrep - Better
grep- Extremely fast, respects .gitignore
- Powers the
fgrepfunction
- bat - Better
cat- Syntax highlighting, line numbers
- File previews in fzf
- zoxide - Smarter
cd- Learns your most used directories
- Jump with partial names
- btop - Better
top/htop- Beautiful terminal UI
- Aliased to replace
topandhtop
- duf - Better
df- User-friendly disk usage display
- dust - Better
du- Intuitive disk usage analyzer
- lazygit - Terminal UI for git
- Interactive staging, branching, merging
- Launch with
lg
- delta - Better git diffs
- Syntax highlighting, side-by-side view
- Auto-configured in gitconfig
- hunk - Diff viewer/pager
- npm package
hunkdiff; run ashunk
- npm package
- gh - GitHub CLI
- Manage PRs, issues from terminal
- cf - Cloudflare CLI
- Manage DNS, zones, Workers deploys from terminal
- httpie - Better
curl- Human-friendly HTTP client
httpsalias for HTTPS requests
- jq - JSON processor
- Query and manipulate JSON data
- just - Modern
make- Simpler command runner
- worktrunk - Git worktree manager
- Create/switch worktrees fast; launched via
wt(and thewscabbreviation)
- Create/switch worktrees fast; launched via
- biome - JS/TS formatter and linter
- Single fast toolchain for formatting and linting
- uv - Fast Python package/project manager
- Neovim - Hyperextensible modal editor; resolved as
$EDITOR- Launched via
nvim(also aliased tovim); personal config lives in~/.config/nvim
- Launched via
- ktlint - Kotlin linter/formatter (macOS only)
- md-tui - Markdown viewer (TUI)
- Render and browse markdown in the terminal; launched via
mdt
- Render and browse markdown in the terminal; launched via
- golangci-lint - Go linter aggregator (Go machines only)
- Runs dozens of Go linters in one pass
- goreleaser - Go release automation (Go machines only)
- Builds, packages, and publishes Go project releases
- fzf - Fuzzy finder
- Ctrl+R (history), Ctrl+T (files), Alt+C (directories)
- Powers many custom functions
- starship - Cross-shell prompt
- Fast, customizable, git-aware
- Pre-configured with icons
- atuin - Better shell history
- Advanced fuzzy search, statistics, intelligent deduplication
- broot - Better
tree- Navigate directories efficiently
- tldr - Simplified man pages
- Quick command examples
- kubectl - Kubernetes CLI
- Manage Kubernetes clusters
- kubectx - Context switcher
- Quickly switch between Kubernetes contexts
- kubens - Namespace switcher
- Quickly switch between Kubernetes namespaces
This configuration includes mise for managing:
Language Runtimes:
- Node.js (LTS) — always installed (via mise)
- Go — opt-in per machine
- Java (Eclipse Temurin LTS) — opt-in per machine
- Python (via Miniconda) — opt-in per machine
(Bun is installed separately by its official installer and self-updates via
bun upgrade — it is not managed by mise.)
node is always installed via mise because the dotfiles tooling depends on it;
bun is installed by its own official installer and self-updates via bun upgrade (not mise-managed). The heavier, project-specific runtimes — Go,
Java, Python/Miniconda — are off by default and opted into per machine:
- Choosing at setup:
chezmoi initprompts "Install Go/Java/Python on this machine?" (default no). In CI or any non-interactive bootstrap the prompts are skipped and nothing extra is installed, so VMs stay lean. - Changing later: re-run
chezmoi init --prompt(or edit~/.config/chezmoi/chezmoi.toml), thenchezmoi apply. mise picks up the new selection from the managed~/.config/mise/conf.d/runtimes.toml. - Version pins for these runtimes live in
private_dot_config/mise/conf.d/runtimes.toml.tmpland are still bumped by Renovate. - Go brings its tooling with it: selecting Go also installs
gopls,golangci-lint, andgoreleaserfrom that same file. They're deliberately mise-owned rather than Homebrew-installed — a brewgoonPATHshadows mise's duringgo:-backend builds and breaks them as soon as the two versions drift apart. - Removing a runtime: turn it off and apply; mise leaves the old install in
place. Every
chezmoi applyprints the current selection and flags any runtime that's installed but no longer selected, with themise uninstall/mise prunecommand to reclaim the disk. Nothing is deleted automatically. - Ad-hoc, machine-only tools that shouldn't be tracked in this repo go in
the unmanaged
~/.config/mise/conf.d/local.toml.
CLI Tools via the mise aqua backend:
- Modern CLI replacements: bat, fd, eza, ripgrep, zoxide, duf, dust
- Development tools: fzf, starship, atuin, delta, lazygit, gh, jq, just, gum, uv
- Editor: neovim (
nvim), tree-sitter CLI (builds nvim-treesitter parsers) - Kubernetes tools: kubectl, kubectx, kubens
- AWS tools: granted (
assume) - Fuzzy finder TUI: television (
tv, via a mise plugin)
Tools outside the aqua registry (btop, httpie, broot, tldr, pinentry, md-tui,
worktrunk, biome, and macOS-only eza/ktlint) are installed by
.chezmoiscripts/run_onchange_after_05-install-tools.sh.tmpl.
The exact tool list and versions track
private_dot_config/mise/config.toml.tmpl, which is the source of truth.
All tools are automatically installed via ~/.config/mise/config.toml when you run:
# Install all configured tools (languages + CLI tools)
mise install
# Or install latest stable versions
mise-install-latestNote: Old installations (Homebrew, apt packages, binaries) are automatically cleaned up after mise aqua setup.
After installation, your dotfiles are managed by chezmoi. To make changes:
# Go to chezmoi's source directory
chezmoi cd
# Edit files directly
vim dot_zshrc.tmpl
# Preview changes
chezmoi diff
# Apply changes locally
chezmoi apply
# Commit and push
git add -A
git commit -m "Update zsh config"
git pushOr use the included dotfiles function:
dotfiles edit # Go to source directory
dotfiles diff # Preview changes
dotfiles apply # Apply locally
dotfiles push # Commit and push all changes
dotfiles pull # Pull latest from GitHubOn any machine with your dotfiles installed:
chezmoi update # Pull latest changes and apply themCreate ~/.config/zsh/config.local.zsh for machine-specific configuration that won't be managed by chezmoi.
Zsh is the login shell (the provisioning script recommends it on fresh machines; chsh -s $(which zsh) to switch an existing one). It comprises:
~/.zshenv— PATH/environment for all shells (the non-interactive half).~/.zshrc— interactive config: vi mode, completions, abbreviations, tool inits (mise, zoxide, fzf, atuin, starship, broot), aliases, and the MOTD/greeting.~/.config/zsh/functions/*.zsh— one function per file (mkcd,extract,fcd,fgit,crpr,tools, …).~/.config/zsh/conf.d/*.zsh— fzf options, macOS extras, greeting, and MOTD. The greeting is a template: macOS gets a two-line welcome; Linux hosts get a server dashboard (banner, kernel/uptime/load, CPU temp, memory, LAN + tailnet IPs, disk bars, docker container health, Pi-hole stats, and a needs-attention list: pending reboot, failed units, apt updates). Live reads run in parallel; the slow ones (Pi-hole exporter, apt count) are cached in~/.cache/zshand refreshed in the background. Inside tmux only the one-line header prints.~/.config/zsh/completions/— completion functions generated at apply time (currently_rgfor ripgrep; mise/aqua binaries don't ship completion files the way brew does). Onfpath, autoloaded by compinit — zero startup cost.
The prompt (starship) and history (atuin) round out the setup.
Plugins (autosuggestions, syntax highlighting) are fetched by chezmoi into ~/.config/zsh/plugins (see .chezmoiexternal.toml) — there is no separate plugin manager. Both are deferred: sourced at the first idle moment after the prompt is drawn rather than at rc time, keeping ~13% off time-to-first-prompt while staying behaviourally identical within a frame of an interactive prompt. Abbreviations (git shortcuts, wsc, cca) are a small built-in ZLE widget (ZABBR in .zshrc), not a plugin — typing one at command position then space/enter expands it inline to the full command.
Try it without switching: just run zsh. To make it your login shell when ready: chsh -s "$(command -v zsh)".
This repository uses chezmoi templates to handle OS-specific differences. Key template variables:
{{ .chezmoi.os }}- "darwin" or "linux"{{ .chezmoi.arch }}- System architecture{{ .brewPrefix }}- Homebrew prefix path{{ .packageManager }}- System package manager
The chezmoi source directory lives at ~/.local/share/chezmoi (run chezmoi cd
to jump there; ~/dev/dotfiles is only the mount path used by docker-test.sh).
chezmoi source/
├── .chezmoi.toml.tmpl # Per-machine data (prompts, .isWork, etc.)
├── .chezmoiignore # Files present in the repo but not deployed
├── .chezmoiremove # Files chezmoi deletes from $HOME on apply
├── .chezmoiexternal.toml.tmpl # Externally-fetched files (zsh plugins)
├── .chezmoiscripts/ # Ordered run_once / run_onchange install scripts
├── dot_zshenv.tmpl # Zsh env for all shells (PATH, etc.)
├── dot_zshrc.tmpl # Main Zsh interactive config
├── dot_gitconfig.tmpl # Git config (delta, etc.)
├── dot_inputrc / dot_lesskey # readline / less key bindings
├── dot_claude/ # Claude Code config (agents, commands, hooks, statusline)
├── private_dot_config/
│ ├── zsh/ # functions/, conf.d/
│ ├── mise/ # mise config (tool + runtime source of truth)
│ ├── ghostty/ lazygit/ # per-tool configs
│ ├── starship.toml.tmpl # shared prompt
│ └── herdr/ worktrunk/ # herdr (multiplexer) and worktrunk configs
├── private_dot_gnupg/ # gpg-agent config
├── install.sh test.sh docker-test.sh setup-aliases.sh
├── Dockerfile renovate.json
└── README.md CLAUDE.md SCRIPTS.md # docs (not deployed)
You can test the dotfiles installation in a clean Docker environment:
# Run the interactive test script
./docker-test.sh
# Inside the container:
cd /home/testuser/dev/dotfiles
./install.sh
zsh
./test.sh # Run the test suite# Build the test image
docker build -f Dockerfile -t dotfiles-test .
# Run container with mounted dotfiles
docker run -it --rm -v "$(pwd):/home/testuser/dev/dotfiles:ro" dotfiles-test
./docker-test.sh(and./docker-test.sh --ci) wrap these commands; there is noDockerfile.testor docker-compose file.
The test.sh script verifies all tools are installed correctly:
- Checks all CLI tools are available
- Verifies Zsh functions exist
- Confirms configurations are in place
- Tests mise tools
This repository includes automated testing via GitHub Actions:
- Tests on Ubuntu 24.04 and macOS 26 (GitHub-hosted runners)
- Full tool installation verification (
install.sh+test.sh) - Docker-based testing is available locally via
./docker-test.sh
install.sh takes no CLI flags; CI behaviour is driven by environment variables.
# Non-interactive install (skips prompts and the login-shell step)
CI=true ./install.sh
# Non-interactive and skip language runtimes
CI=true QUICK_INSTALL=true ./install.shEnvironment variables for CI:
CI=true- Automatically detected by GitHub ActionsCHEZMOI_USER_NAME- Name for git configCHEZMOI_USER_EMAIL- Email for git config
Only relevant if you opted Python in for this machine (see Per-machine language runtimes). Miniconda requires ToS acceptance; this is a known issue and the install continues without Python. To fix:
# Accept Conda ToS manually
~/.local/share/mise/installs/python/miniconda3-latest/bin/conda init
~/.local/share/mise/installs/python/miniconda3-latest/bin/conda config --set auto_activate_base falseEnsure ~/.local/bin is in your PATH:
export PATH="$HOME/.local/bin:$PATH"# Re-apply dotfiles
chezmoi apply -vThere is no plugin manager to set up: fzf key bindings come from conf.d/fzf.zsh
(and fzf --zsh in ~/.zshrc), and every CLI tool is provided by mise. If a tool
is missing, ensure mise is active (eval "$(mise activate bash)") and re-run
chezmoi apply -v.
# Activate mise in current shell
eval "$(mise activate bash)"
# Or start a new Zsh shell
zshThis repository uses Renovate Bot to automatically keep dependencies up-to-date. Renovate creates pull requests when new versions are available.
- Node.js version (core, in
config.toml.tmpl) - All the pinned aqua/npm-backend CLI tools in
config.toml.tmpl(fzf, ripgrep, atuin, …) - Go plus its tooling — gopls, golangci-lint, goreleaser (opt-in runtimes, in
conf.d/runtimes.toml.tmpl; Java and Python/Miniconda pins are not tracked)
Both files are Go templates, which the built-in Renovate mise manager cannot
parse — the pins are matched by regex customManagers in renovate.json
instead.
(Bun is self-managed via bun upgrade, so Renovate does not track it.)
- Action versions (e.g.,
actions/checkout) - GitHub-hosted runner versions
3. Pinned binaries in the install-tools script (.chezmoiscripts/run_onchange_after_05-install-tools.sh.tmpl)
- btop (system monitor)
- tealdeer / tldr (man-page examples)
- broot (tree navigation)
- md-tui (markdown viewer)
These are the only tools pinned outside the mise config; everything else is pinned in the mise config above. install.sh itself is a flagless bootstrap and contains no version pins.
- Automated Runs: the shared preset (
github>ryanlewis/renovate-config) batches non-major updates into one PR, opened Monday mornings after a 5-day release soak - Pull Requests: conventional commits, one grouped non-major PR plus separate PRs per major
- Auto-merge: limited to GitHub Actions minor/patch and dev-dependency patches; everything else gets a human
- Major Updates: parked on the Dependency Dashboard until manually approved
- Main config:
renovate.json - Managed by: GitHub Renovate App (no additional setup needed)
If you fork this repo, you'll need to:
- Install the Renovate GitHub App on your fork
- Renovate will automatically detect the configuration and start creating PRs
Feel free to fork and customize for your own use!
Last updated: June 2026
The authoritative version list is private_dot_config/mise/config.toml.tmpl; Renovate keeps it current. CLI tools track latest, so only the pinned language runtimes are listed here.
- chezmoi: latest from official installer
- mise: latest
Node.js is always installed (pinned in private_dot_config/mise/config.toml.tmpl). Go, Java (Eclipse Temurin LTS), and Python (miniconda3) are opt-in per machine (pinned in private_dot_config/mise/conf.d/runtimes.toml.tmpl); see Per-machine language runtimes. Both files are the source of truth and are bumped automatically by Renovate. Bun is installed by its official installer and self-updates via bun upgrade (not mise-managed).
MIT