Personal dotfiles for macOS, managed with GNU Stow.
| Component | Tool |
|---|---|
| Shell | Fish |
| Package manager | Homebrew |
| Dotfile manager | GNU Stow |
| Runtime manager | Mise |
| Terminal | Ghostty |
| Prompt | Starship |
| Editor | Zed |
| Window management | AeroSpace |
| Keyboard | Karabiner-Elements |
For a fresh Mac, follow MIGRATION.md instead of the snippet below. The build scripts require an authenticated 1Password CLI session, DEVONthink installed, and a few other prerequisites that setup.sh does not bootstrap on its own.
git clone https://github.com/alechemy/dotfiles-fish.git ~/.dotfiles
cd ~/.dotfiles
./scripts/setup.shThis installs Homebrew + all dependencies from Brewfile, builds generated configs (injecting secrets via 1Password CLI), symlinks the stow packages to $HOME (DEVONthink and streamrip are opt-in prompts), and sets Fish as the default shell.
.dotfiles/
βββ Brewfile # Homebrew dependencies
βββ homebrew/ # Local-only tap (casks with no upstream cask)
βββ devonthink/ # DEVONthink pipeline docs
βββ drafts/ # Drafts action scripts (repo is canonical)
βββ keyboard-maestro/ # Scripts referenced by KM macros
βββ hrm/ # Glove80 keyboard layout + README
βββ firmware/ # Keyboard/device firmware + layouts
β βββ ploopy-knob/ # Ploopy Knob QMK keymap + build recipe
β βββ tailorkey/ # Glove80/Go60 TailorKey layouts
βββ scripts/
β βββ setup.sh # Main bootstrap script
β βββ restow-changed.sh # Auto-restow worker (git post-merge/-rewrite/-commit hooks)
β βββ git-hooks/ # Tracked hooks wired via core.hooksPath
β βββ build-zed-config.sh # Inject 1Password secrets into Zed config
β βββ build-streamrip-config.sh # Inject 1Password secrets into streamrip config
β βββ build-context7-config.sh # op read β fish conf.d Context7 key export
β βββ build-things-config.sh # op read β ~/.zshenv Things auth token
β βββ build-vscode-config.sh # Expand ${HOME} in VSCodium settings.json
β βββ build-launchd-plists.sh # Expand __HOME__ in launch-agent plist templates
β βββ lint-launchd-plists.sh # Enforce TCC-stable interpreters in launch agents
β βββ seed-devonthink-config.sh # Copy-if-absent DEVONthink seed plists
β βββ setup-vscode.sh # VSCodium extension install
β βββ aerospace-*.sh|py # AeroSpace gap/window helpers
β βββ macos.sh # macOS system defaults
βββ stow/ # Stow packages (auto-linked by setup.sh; devonthink + streamrip opt-in)
β βββ aerospace/ # Tiling window manager
β βββ bin/ # ~/.local/bin scripts
β βββ borders/ # JankyBorders window borders
β βββ chromium-bookmarks/ # Chromium β Safari bookmark bridge for Alfred
β βββ claude/ # Claude Code global config, skills, hooks
β βββ copilot/ # Copilot CLI config
β βββ devonthink/ # DEVONthink automation (opt-in)
β βββ dropzone/ # Dropzone action bundles
β βββ editorconfig/ # ~/.editorconfig
β βββ espanso/ # Text expansion
β βββ fish/ # Fish shell config, functions, plugins
β βββ ghostty/ # Terminal emulator
β βββ git/ # Git config + global excludes
β βββ karabiner/ # Keyboard remapping (goku EDN source)
β βββ linearmouse/ # Ploopy Knob scroll config (seeded, not stowed)
β βββ mise/ # Runtime version manager
β βββ nas-mount/ # Auto-mount NAS SMB shares (launch agent)
β βββ navidrome/ # Navidrome client env
β βββ sketchybar/ # Menu bar
β βββ ssh/ # SSH client config
β βββ starship/ # Shell prompt theme
β βββ streamrip/ # Music downloader config (opt-in, op inject)
β βββ tmux/ # Phone-friendly tmux config
β βββ vscode/ # VSCodium settings (${HOME} expansion)
β βββ zed/ # Zed editor (1Password op inject)
βββ stow-work/ # Work config (gitignored; auto-stowed when populated by file-copy)
βββ stow-local/ # Machine-local config (gitignored)
Each directory under stow/ mirrors the path relative to $HOME. Stow creates symlinks from $HOME back into this repo. Editing stowed files requires no action since symlinks already point here -- only restow when adding or removing files.
Language runtimes and globally-installed CLIs are declared in stow/mise/.config/mise/config.toml. Running mise install on a fresh machine reproduces everything listed there.
mise use -g node@lts # add/pin a runtime globally
mise use -g npm:defuddle # add a global npm CLI (survives node upgrades)
mise up # update all tools to latest matching versions
mise up npm:defuddle # update a single tool
mise ls # list everything mise manages
mise uninstall <tool> # remove from disk
mise unuse -g <tool> # remove the declaration from config.tomlmise use/mise unuse edit config.toml in place, so the dotfiles repo stays in sync. Prefer the npm:<pkg> backend over raw npm install -g so globals get reinstalled against whichever node version is active.
Some configs are generated from a tracked template at install time. Three flavors:
- 1Password secret injection (
stow/zed/,stow/streamrip/): the template hasop://Vault/Item/Fieldreferences whichop injectresolves into the real config (gitignored). Both also do${HOME}expansion. - 1Password single-value fetch (
build-context7-config.shβ fish conf.d Context7 key;build-things-config.shβ~/.zshenvThings token):op readfetches one secret and the script writes the output directly β no template file, used where a template sibling would be harmful (fish auto-sources everything in conf.d) or the output lives outside the stow tree. - Path expansion only (
stow/vscode/): the template has${HOME}placeholders that get expanded into absolute paths. Used where the target tool requires absolute paths and doesn't honor its own variable substitution (e.g.,be5invis.vscode-custom-cssreadsfile://URIs literally).
Each follows the same pattern: tracked *.template.{json,toml}, generated output gitignored, build script run before stow, and a .stow-local-ignore entry that excludes the template from being symlinked. See scripts/build-zed-config.sh as the canonical example.
stow-work/ holds work-specific config (fish functions, sketchybar overlays, Copilot/Atlassian MCP setup, docs). It is gitignored apart from .gitkeep, so a fresh clone leaves it empty and nothing links. On a work machine, populate it by file-copy from another machine β setup.sh auto-stows it once it has content. To link without a full setup re-run:
cd ~/.dotfiles/stow-work
stow --restow --no-folding --ignore='.DS_Store' --ignore='__pycache__' --target="$HOME" work