Personal dotfiles deployed via a small hand-rolled symlinker (see Layout). A single script installs everything, including configs and binaries, to ~/.local/ with no system-wide changes (beyond two narrow exceptions: mosh, and upgrading git itself via PPA when it's too old).
| Layer | Tool |
|---|---|
| Terminal | Alacritty |
| Shell | zsh |
| Prompt | Starship |
| Multiplexer | Zellij (+ zjstatus status bar) |
| Editor | Neovim (LazyVim) |
| Theme | Catppuccin Mocha everywhere |
| Font | JetBrainsMono Nerd Font (NF/NFM/NFP — not the NL/no-ligature variant) |
git clone https://github.com/myah-mitchell/dotfiles.git ~/dotfiles
cd ~/dotfiles
./install.shThat's it. The script downloads all binaries and symlinks configs into ~. On WSL2, opening this distro through Windows Alacritty also needs ./install.sh --windows to install Alacritty and the font on the Windows side — see What install.sh does.
Set GITHUB_TOKEN in your environment first if doing a fresh install — unauthenticated GitHub API requests are capped at 60/hr, and this script checks 25+ tools' latest releases.
- Downloads pre-built binaries for all tools into
bin/.local/bin/(gitignored) - Downloads Starship, Zellij, and Neovim binaries (zsh itself must be pre-installed — see Notes)
- Symlinks (or, with
--copy, copies) every package directory into~, one file at a time - Prunes any dangling symlinks left behind by files that were renamed/removed from the repo
- Downloads the zsh plugins (autosuggestions, syntax highlighting, alias reminders) and runs a headless
:Lazy syncfor Neovim - Generates init scripts for atuin, starship, zoxide, and carapace
- Installs Yazi's Catppuccin flavor and plugin set
- Downloads the Zellij plugins that back the status bar and navigation (
zjstatus,zellij-autolock,zellij-newtab-plus), buildszjstatus-hintsfrom source, and pre-approves their Zellij permissions - WSL2 +
--windows/--fullonly: installs Alacritty via winget, installs the Nerd Font per-user, and creates a Windows symlink for the Alacritty config — off by default even under WSL2, since not every WSL box is opened through Windows Alacritty. On native Linux/macOS it downloads the font directly instead (unconditionally, no flag needed).
Re-run at any time to apply config changes:
./install.sh # full run: binaries + config links
./install.sh --update # force re-download/rebuild all tools to latest, even if current
./install.sh --link # skip binary downloads, only re-link configs (fast)
./install.sh --copy # copy configs instead of symlinking (useful on NTFS or shared servers)
./install.sh --remove # remove all deployed symlinks from $HOME and exit (binaries untouched)
./install.sh --cargo # build Rust tools from source via cargo instead of downloading (slow)
./install.sh --windows # install Windows-side integration under WSL2: Alacritty, the font, and the
# config symlink — off by default, no-op outside WSL2; implied by --full
./install.sh --claude # install the Claude Code stack: Claude Code itself, Node.js, ccstatusline,
# and claude-swap — off by default, since none of them are useful without
# Claude Code; implied by --full
./install.sh --full # everything --claude and --windows install, plus PowerShell and Neovim's
# heavier LSPs (html/css/emmet/bash/php/pyright/powershell_es, ~460MB of
# Mason packages) — off by default; skip on servers that just need the shell
./install.sh --nushell # install Nushell (nu) as an optional secondary shell; zsh stays the
# default either way; off by default, not implied by --full
./install.sh --cleanup # remove rustup/cargo (unless --cargo), orphaned binaries/plugins/npm globals
# left behind by removed tools, node/ccstatusline if not --claude/--full,
# pwsh + full-only Mason LSPs if not --full, nu if not --nushell, and old
# Claude Code version binaries left by its own self-updaterEach top-level directory is a package. install.sh walks it and symlinks every file into the matching path under $HOME — files mirror the home directory layout:
dotfiles/
├── zsh/.zshenv, .zshrc → ~/.zshenv, ~/.zshrc
├── zsh/.config/zsh/ → ~/.config/zsh/
├── nushell/.config/nushell/ → ~/.config/nushell/ (only deployed with --nushell)
├── starship/.config/ → ~/.config/
├── zellij/.config/zellij/ → ~/.config/zellij/
├── nvim/.config/nvim/ → ~/.config/nvim/
├── alacritty/.config/alacritty/ → ~/.config/alacritty/
├── git/.gitconfig → ~/.gitconfig
├── bash/.bashrc, .bash_profile → ~/.bashrc, ~/.bash_profile
├── bin/.local/bin/ → ~/.local/bin/ (binaries)
└── ssh/.ssh/config → ~/.ssh/config
A package can drop a .linkignore file in its root (one glob per line) to exclude files that shouldn't be symlinked — e.g. yazi/.linkignore excludes the flavors/plugins directories, since those are managed at runtime by ya pkg instead.
| Keys | Action |
|---|---|
` |
Enter prefix (tmux) mode |
` + \ |
Vertical split |
` + - |
Horizontal split |
` + hjkl |
Navigate panes |
` + H/J/K/L |
Move pane |
` + c |
New tab (floating picker, zoxide-aware) |
` + p / n |
Prev / next tab |
` + [ / ] |
Prev / next tab (alternate) |
` + 1-9 |
Jump to tab N |
` + x |
Close pane |
` + z |
Fullscreen pane |
` + f |
Toggle floating panes |
` + , |
Rename tab |
` + d |
Detach session |
` + s |
Scroll mode |
` + / |
Search |
` + o |
Session mode |
Ctrl+hjkl |
Move between Zellij panes (or Neovim splits) |
Ctrl+g |
Emergency toggle into/out of locked (full-passthrough) mode |
The status bar (bottom, via zjstatus) shows the current mode, open tabs, weather/system/battery info, and the datetime; a second bar shows context-sensitive keybinding hints (via zjstatus-hints).
Ctrl+h/j/k/l navigates seamlessly between Zellij panes and Neovim splits:
- In a normal terminal pane,
Ctrl+hjklmoves Zellij focus - When
nvim,fzf,zoxide, oratuinis focused, zellij-autolock switches Zellij to passthrough mode so the app receives the keys - In Neovim, zellij-nav.nvim handles the keys: navigates splits internally, and moves to adjacent Zellij panes at the edge
- Mouse highlight → auto-copies to Windows clipboard
- In Zellij scroll mode,
Ctrl+Ccopies the selection - Neovim uses
unnamedplusclipboard (WSL2 →clip.exe) - Alacritty
Ctrl+Shift+C/Vfor explicit copy/paste
starship · zellij · nvim · nu (only with --nushell; optional secondary shell, zsh stays the default)
| Tool | Alias | Purpose |
|---|---|---|
| bat | cat |
Syntax-highlighted cat |
| fd | find |
Fast find |
| ripgrep | grep |
Fast grep |
| ripgrep-all | — | Grep PDFs, archives, etc. |
| eza | ls |
Modern ls replacement, Catppuccin Mocha themed |
| fzf | — | Fuzzy finder (also bound to Ctrl+F in zsh) |
| zoxide | cd |
Smart cd |
| yazi | y |
File manager |
| lazygit | lg |
Git TUI |
| serpl | — | TUI search-and-replace across files |
| difftastic | diff |
Structural diff (also GIT_EXTERNAL_DIFF) |
| delta | gdd/gdds |
Syntax-highlighting pager for unified diffs (git dd; difftastic stays the default) |
| diffnav | gdn/gdns |
File-tree diff browser built on delta (git dn) |
| procs | ps |
Process viewer |
| tailspin | tail |
Syntax-highlighted tail |
| viddy | watch |
Watch TUI |
| bottom | btm |
System monitor TUI (standalone, not a top alias) |
| gping | gping |
Graphical ping |
| trippy | trip |
Network tracer |
| sd | — | Intuitive find-and-replace (standalone, not a sed alias) |
| choose | — | Human-friendly cut/awk alternative |
| ouch | — | Universal archive tool |
| 7-Zip (7zz) | — | Archive tool |
| jq | — | JSON processor |
| glow | — | Markdown renderer in the terminal |
| mdcat | — | Markdown renderer with inline images and clickable links (x86_64 only) |
| carapace | — | Multi-shell argument completions |
| atuin | — | Shell history (SQLite-backed, per-session isolated) |
| tealdeer | tldr |
Fast tldr pages |
| presenterm | — | Terminal slideshows |
| asciinema | — | Terminal recording |
When you need the original system tool instead of the Rust replacement, zsh's builtin command bypasses an alias:
command ls -la # /usr/bin/ls
command du -sh . # /usr/bin/du
command rm -rf /tmp/x # /usr/bin/rm- zsh: must be pre-installed (
apt install zsh/brew install zsh) —install.shchecks for it and exits with instructions rather than installing it, since (unlikemoshbelow) it's not treated as an exception to the "no system package installs" rule. - Nushell: optional, via
--nushell. zsh is always the default shell;nuis just made available to launch manually. Its config package (~/.config/nushell/) is only deployed when the flag is passed, so a plaininstall.shnever leaves anything nu-related behind. - mosh: installed via
apt-getif available (no GitHub release binaries). Usually pre-installed on servers. - git: on Ubuntu/Debian with git older than 2.35,
install.shupgrades it via the git-core PPA — the only other exception to the "no system package installs" rule, needed forzdiff3merge style. - zjstatus-hints: has no usable upstream release, so
install.shbuilds it from source (requires cargo + thewasm32-wasip1target) off a temporary community fork branch rather than upstreammain. Seeinstall.shfor details if the hints bar stops building. - SSH sockets:
~/.ssh/sockets/is created by install.sh but not tracked in git.
install.sh already runs a headless :Lazy sync automatically. If plugins didn't finish installing (e.g. it timed out on a slow connection), finish manually:
nvim
# Inside nvim:
# :Lazy syncPlugins include LazyVim base, Catppuccin theme, zellij-nav.nvim (unified navigation), and opencode.nvim (<leader>ao to toggle).
Copyright (c) 2026 Myah Mitchell. Licensed under the MIT License.