This is a portable, GNU Stow-managed shell and terminal environment. It
deploys the common package plus a host package when one exists (macos,
linux, or wsl). It is intentionally broad enough to make a new machine
feel like this one, while excluding credentials, generated files, and mutable
application state.
- Zsh with platform detection, completions, Antidote plugins, Starship, Ghostty
integration, zoxide and Sesh completion, optional tmux auto-attach, and a
dotscommand with native Tab completion. - A pinned Neovim submodule and pinned TPM submodule. Their known-good commits travel with this repository; Neovim remains independently maintainable.
- Portable configuration for terminal tools such as Starship, btop, tmux, OpenCode, 1Password CLI aliases, and Stylua.
- On macOS, Homebrew packages and casks declared in
macos/Brewfile.
It does not deploy secrets, authentication state, plugin caches, game saves, Raycast, Fish, Zed, Wireshark, or retired SwiftBar state.
Clone with the pinned Neovim and TPM submodules, then run bootstrap. The
bootstrap script detects the host by default; --platform macos|linux|wsl
overrides detection.
git clone --recurse-submodules git@github.com:johnseth97/.dotfiles.git ~/.dotfiles
~/.dotfiles/common/.bin/bootstrapOn macOS, bootstrap uses Homebrew (installing it after Command Line Tools if
needed) and applies macos/Brewfile. On Linux and WSL it
uses Homebrew when present, otherwise apt, dnf, or pacman to install the core
shell/deployment dependencies. Use --dry-run to show the Stow deployment,
--skip-packages when dependencies are already installed, or
--skip-tmux-plugins to defer TPM's initial plugin installation.
Run bootstrap-git-signing separately after installing 1Password if you want
Git SSH signing configured through its agent.
Stow and Git solve different problems:
.gitignorecontrols what is committed..stow-local-ignorecontrols what is deployed to$HOME.- Application-owned caches, secrets, and plugin checkouts stay local even if an application writes them below a configuration directory.
Do not invoke stow .. Use the wrapper so platform selection and ignore
policy stay consistent:
~/.dotfiles/common/.bin/dotfiles-stow --dry-run
~/.dotfiles/common/.bin/dotfiles-stow --restowNeovim is an independently versioned project and remains a pinned submodule. Bootstrap and sync check out the revision pinned by this repository; they do not pull its branch. TPM is likewise a pinned submodule, while its installed plugins are mutable local state managed by TPM.
The .bin tools are the portable interface. Zsh also provides a dots
dispatcher and short aliases for interactive use. Type dots <Tab> to list
actions, then Tab again for the selected action's options.
dots status # see local changes before syncing
dots plan # preview a restow; changes nothing
dots deploy # restow this host's packages
dots sync # update the root repo and pinned submodules
dots bootstrap # prepare a new host
dots shell-update # update Homebrew Antidote and declared plugin bundles
# Equivalent short aliases: dots-plan, dots-deploy, dots-sync,
# dots-bootstrap, and antidote-update.dots shell-update is intentionally explicit: it performs package/Git
updates and should not run at every shell startup.
Zsh initializes zoxide, so directories visited through z automatically
become Sesh project candidates. Tmux starts a login Zsh when it is installed,
which ensures that the same shell integrations apply inside every pane.
prefix+sopens the Sesh project/session picker.prefix+padds the current pane directory to zoxide manually, then refreshes Sesh's optional cache. This is useful for a project you want in the picker before it has accumulated zoxide history.prefix+mprompts for an index and inserts the current window there, shifting the intervening windows as needed.prefix+Mprompts for an existing tmux/Sesh session target and moves the current window to its end. Press Tab for tmux session/window completion.prefix+Lswitches to Sesh's most recently used session.
Outside tmux, an interactive local Zsh attaches to (or creates) the default
tmux session. Set NO_TMUX=1 or DISABLE_AUTO_TMUX=1 before starting Zsh to
skip that behavior; SSH, editor-integrated terminals, and existing tmux panes
are already excluded.
sync-dotfiles updates only the dotfiles repository and its pinned
submodules, then restows the selected packages. It refuses a dirty worktree
or dirty submodule rather than overwriting local work.
sync-dotfilesPackage updates remain the responsibility of the platform package manager
(Homebrew and brew-watchtower on macOS). Antidote is loaded from a packaged
installation when one is available; a legacy ~/.antidote clone is only a
fallback for hosts without one.
macos/.config/brew-watchtower/groups.conf
classifies every formula and cask in the curated Brewfile. It deliberately
separates unattended, low-risk command-line updates from packages that can
change a runtime, security posture, graphical application, or developer tool.
foundationruns daily at 05:00 and automatically upgrades a small set of shell-oriented tools.securityruns daily at 09:30 but remains interactive, so security-sensitive packages are reported rather than silently upgraded.runtimes,developer-system, anddesktop-and-creativeare review-only interactive groups. They have no schedule by design.
The Python selector is intentionally dynamic: formula_glob=python@* applies
to installed versioned Python formulae, while brew-watchtower groups prints
the exact packages currently matched. After changing the manifest, inspect it
and apply it to Watchtower's protected runtime:
brew-watchtower groups
brew-watchtower groups sync
brew-watchtower statusgroups_mode=declarative prevents add, remove, and schedule from
silently diverging the protected runtime from this Stow-managed manifest. Edit
the manifest and sync it instead. When a group is retired, preview the exact
root policy files and LaunchAgents that would be removed, then apply explicitly:
brew-watchtower groups prune
brew-watchtower groups prune --applyWatchtower manages Homebrew formulae and casks, not Mac App Store purchases;
the Brewfile's mas entries remain App Store-managed. The mas formula itself
is included as an interactive developer-system package.
Run these before deploying a significant change:
dots plan
~/.dotfiles/common/.bin/bootstrap --dry-run --skip-packages --skip-tmux-plugins
git -C ~/.dotfiles diff --checkFor a full clean-room Stow test without changing $HOME, deploy to a temporary
directory instead:
test_home="$(mktemp -d)"
stow --dir ~/.dotfiles --target "$test_home" common macos
test -f "$test_home/.config/stylua.toml"
test -d "$test_home/.config/tmux/plugins/tpm"Inspect "$test_home" when finished, then remove it explicitly if desired.