My everyday terminal configuration: Zsh on macOS, Fish on Linux, and a shared set of terminal tools.
One folder per application. Manual symlinks. No installer or platform framework.
| Folder | Contents | Source setup |
|---|---|---|
zsh/ |
Zsh startup files and Powerlevel10k settings | Mac |
fish/ |
Fish configuration, functions, completions and colors | Linux notebook |
ghostty/ |
Fonts and cursor shaders | Mac |
git/ |
Global ignore rules and an optional Git config include | Mac |
herdr/ |
Agent panel preferences | Mac |
hunk/ |
Usage note; no configuration to link yet | Mac |
projects/ |
Local/SSH project picker, pinned Git submodule | Shared |
nvim/ |
My Neovim configuration, pinned as a Git submodule | Shared |
tmux/ |
Key bindings, status line and plugin declarations | Mac |
Install only the applications you use. Dependencies are installed separately:
- Zsh: Oh My Zsh in
~/.oh-my-zsh, Powerlevel10k in its custom themes directory, and thezsh-syntax-highlightingandzsh-autosuggestionscustom plugins. The configuration also uses Homebrew and zoxide. Thecolorizeplugin needs Pygments or Chroma. Bun, nvm, Cargo, Go, LM Studio and Obsidian paths reflect the source setup; their application files are not included. - Fish: fnm for Node.js version management and pnpm for
pn.gwlwrapsgit worktree list.codex-gcommitrequires the separately configuredgit-logical-commitCodex profile andgit-logical-commitsskill. It explicitly bypasses commit hooks and checks; review its function before using it. Neither dependency is provided by this repository. The Copilot completion file is a generated snapshot. - Ghostty: the configured FiraCode Nerd Font faces. Bundled cursor shaders retain their upstream license and README.
- tmux: a Nerd Font and TPM at
~/.tmux/plugins/tpm. The configuration declarestmux-sensible. Clipboard bindings currently use macOSpbcopy; adapt those commands before using tmux on Linux. - Neovim: follow the submodule's own README for dependencies.
macOS is the first target for the Zsh and terminal setup. Fish comes from the Linux setup. Configurations are shared where practical, but this repository does not claim identical behavior across operating systems.
Keep the checkout at ~/dotfiles for the examples below. If cloning it, use git clone --recurse-submodules <repository-url> ~/dotfiles.
For an existing checkout:
cd ~/dotfiles
git submodule update --init --recursiveThis only downloads the files. It does not activate any configuration.
Run these examples from Zsh or Bash. Select the applications you want. Before each ln -s, move the existing target to a backup outside this repository. The commands deliberately omit force flags and will fail if a target already exists.
For example, to back up a Zsh file before linking it:
mv "$HOME/.zshrc" "$HOME/.zshrc.backup-$(date +%Y%m%d-%H%M%S)"
ln -s "$HOME/dotfiles/zsh/.zshrc" "$HOME/.zshrc"Keep the backup until you have opened a new shell and verified the result. To undo a link, remove only that symlink and move its backup back to the original path.
Back up each existing destination first:
ln -s "$HOME/dotfiles/zsh/.zshrc" "$HOME/.zshrc"
ln -s "$HOME/dotfiles/zsh/.zprofile" "$HOME/.zprofile"
ln -s "$HOME/dotfiles/zsh/.zshenv" "$HOME/.zshenv"
ln -s "$HOME/dotfiles/zsh/.p10k.zsh" "$HOME/.p10k.zsh"Link individual files so local Fish state remains outside Git. Back up matching destination files first. Existing unrelated functions and completions remain in place.
mkdir -p "$HOME/.config/fish/conf.d" "$HOME/.config/fish/functions" "$HOME/.config/fish/completions"
ln -s "$HOME/dotfiles/fish/config.fish" "$HOME/.config/fish/config.fish"
for group in conf.d functions completions; do
for file in "$HOME/dotfiles/fish/$group/"*.fish; do
ln -s "$file" "$HOME/.config/fish/$group/$(basename "$file")"
done
doneColors and key bindings are exported as regular configuration in conf.d/appearance.fish. The mutable fish_variables file and history are not included. The source setup's ~/.local/bin path is configured in config.fish.
Back up the existing application directories before linking:
mkdir -p "$HOME/.config"
ln -s "$HOME/dotfiles/ghostty" "$HOME/.config/ghostty"
ln -s "$HOME/dotfiles/nvim" "$HOME/.config/nvim"Link only configuration files; keep sessions and logs local. Back up existing target files first:
mkdir -p "$HOME/.config/tmux" "$HOME/.config/herdr"
ln -s "$HOME/dotfiles/tmux/tmux.conf" "$HOME/.config/tmux/tmux.conf"
ln -s "$HOME/dotfiles/herdr/config.toml" "$HOME/.config/herdr/config.toml"Keep your identity, credentials, signing settings and machine-specific includes in your existing ~/.gitconfig. Back up the existing ignore file before linking:
mkdir -p "$HOME/.config/git"
ln -s "$HOME/dotfiles/git/ignore" "$HOME/.config/git/ignore"Git uses this ignore location by default unless core.excludesFile overrides it. To select it explicitly, add this include to ~/.gitconfig once:
[include]
path = ~/dotfiles/git/configNothing to link yet. See hunk/README.md.
The parent repository records an exact Neovim commit. It does not follow the latest upstream revision automatically.
After pulling dotfiles, run git submodule update --init --recursive to use its recorded revision. To intentionally select another version:
git -C ~/dotfiles/nvim fetch origin
git -C ~/dotfiles/nvim checkout <reviewed-commit-or-tag>
git -C ~/dotfiles add nvimReview and commit the updated submodule pointer in dotfiles. If you edit Neovim itself, create a branch in that submodule, commit and publish its changes first, then update the parent pointer.
Credentials, shell history, logs, caches, sessions, generated Fish state and Git identity are not part of this repository. Avoid linking an application's entire state directory when a file-level link is enough.
gwl lists worktrees, gw picks one and changes directory, and gwn picks one
and opens Neovim. gw /absolute/path and gwn /absolute/path skip the picker.
These functions need Git and fzf, and work without the projects CLI.
Zsh: link zsh/projects.zsh to ~/.config/zsh/projects.zsh; the tracked .zshrc
sources it. If keeping your own .zshrc, add that source line manually.
Fish: link fish/functions/gw.fish, gwn.fish, and gwl.fish individually into
~/.config/fish/functions/. Preserve unrelated machine configuration.
The separate projects submodule provides p: one local/SSH project picker
with recent history and optional Herdr opening. Initialize it, then link manually:
git submodule update --init projects
mkdir -p ~/.local/bin ~/.config/projects
ln -s "$HOME/dotfiles/projects/p" "$HOME/.local/bin/p"Configure each machine using the submodule README. Machine names, SSH aliases,
project roots, cache and history stay local and are not committed. The standalone
shell functions can be reused with the documented opener configuration.
The Zsh worktree picker has a real-terminal regression test (requires Python 3,
Zsh, Git and fzf): python3 -m unittest discover -s zsh/tests -v.
The Zsh projects.zsh file also defines p; Fish users should link
fish/functions/p.fish into ~/.config/fish/functions/. This wrapper keeps a
selected local directory in the calling shell after the editor exits. Reload
Zsh with source ~/.config/zsh/projects.zsh in existing terminals. The standalone
command p executable cannot change its parent shell directory.