YouTube Summaries

← All summaries

Reproducible Mac agentic dev environment: Nix, Neovim, WezTerm, Herdr

2026-07-05 Sun ⏱ 44 min kunchenguid

Walkthrough of building a fully reproducible macOS development environment from a freshly installed machine, using Nix (via Determinate Systems' installer), nix-darwin, home-manager, and nix-homebrew to declaratively manage OS settings, packages, and dotfiles. Covers zsh, Starship, WezTerm, a from-scratch Neovim config (Lazy, Snacks, oil.nvim, Neogit/gitsigns, which-key), the Herdr terminal multiplexer for running multiple AI agent sessions, and a shared global AGENTS.md memory file distributed to all agent harnesses via home-manager symlinks.

Reproducibility via Nix

The core goal: apply the same config to any Mac, and recover instantly if an AI agent damages the system. Solution is Nix, a declarative/reproducible config system originally for NixOS (Linux) but made portable to macOS via the Determinate Systems installer (run their one-line install command, then the shell-refresh command it prints). A "dotfiles" repo is created on GitHub and git-initialized locally, with a symlink placed at a fixed, stable path so that all later scripts and configs can reference the dotfiles location consistently regardless of where the actual clone lives.

nix-darwin for macOS system settings

nix-darwin manages macOS-level settings declaratively. Setup:

  • Copy the nix-darwin boilerplate into flake.nix, renaming the default "system" label (from the example username) to something project-specific (here, "Mac").
  • Pin nixpkgs/nix-darwin to a specific stable release rather than tracking unstable, to avoid surprises.
  • Create configuration.nix referenced by the flake, with: =nix.enable = false= (since Determinate already manages the Nix install itself), =nixpkgs.config.allowUnfree = true=, nixpkgs.hostPlatform set per CPU architecture (Apple Silicon vs Intel), system.primaryUser set to the username, and system.stateVersion (set once, e.g. 6, and never touched again).
  • Activate with sudo darwin-rebuild switch --flake .#Mac (first run installs missing deps and is slow). A helper script rebuild.sh wraps this command so future changes are a single command to apply.
  • Practical settings applied through configuration.nix: dark theme, fast key repeat with short delay, autohide menu bar, always show file extensions, autohide the Dock, Finder list view, clean desktop (no icons), and tap-to-click on the trackpad. All are declarative — no manual clicking through System Settings.

Homebrew via nix-homebrew

Installing Homebrew directly via its normal install script isn't reproducible across machines, so it's installed as a Nix package instead, using the nix-homebrew module. Setup:

  • Add nix-homebrew as a flake input and as a module in flake.nix.
  • In configuration.nix, add a homebrew block with casks (e.g. WezTerm) and set =cleanup = "zap"=, which removes any Homebrew package not declared in the Nix config on every rebuild — this forces all installs to go through Nix config instead of ad hoc brew install, keeping the whole system reproducible.
  • Rebuild, verify with brew --version, then switch the default terminal over to WezTerm once it's installed.

home-manager for user-level config

home-manager manages everything under the home directory (as opposed to nix-darwin's OS-level scope). Setup:

  • Add home-manager as a flake input/module, and reference it via a line binding the username to a home.nix file.
  • In configuration.nix, ensure the user's home directory is set correctly (required for home-manager to work).
  • home.nix creates symlinks from real config locations (e.g. ~/.config/wezterm) into paths inside the dotfiles repo. This means the dotfiles repo becomes the source of truth and stays version-controlled even when an app rewrites its own config at runtime, since the config directory itself is a symlink into the repo.
  • home-manager also installs user-level packages, including fonts (e.g. Hack Nerd Font, installed declaratively rather than downloaded manually), and sets environment variables (e.g. default editor = Neovim).

Shell: zsh, Starship

  • home-manager config enables zsh autosuggestions (ghost-text completion from command history) and syntax highlighting.
  • Custom initContent is injected into zsh's rc: a keybind (Ctrl-F) to accept the autosuggestion ghost text, plus a set of shell aliases for frequently typed commands.
  • Starship is installed via home-manager for a customizable, better-looking shell prompt (kept simple here, but highly configurable).

WezTerm configuration

WezTerm is a fast, Rust-based, cross-platform terminal emulator (works consistently even on Windows), configured via Lua and hot-reloading on save. Config file: ~/.config/wezterm/wezterm.lua, symlinked from the dotfiles repo. Settings applied incrementally, each visible immediately on save: colorscheme "Rosé Pine Moon", font set to Hack Nerd Font at size 15, background opacity/blur adjusted, tab bar hidden when only one tab is open, and the window frame/decorations removed entirely for a clean, content-only window.

Neovim configuration from scratch

Config lives at ~/.config/nvim/init.lua (symlinked from dotfiles), loaded via home-manager after rebuild.

  • init.lua just requires other Lua modules (files under a lua/ subdirectory), keeping config modular.
  • lua/config.lua (or similarly named) sets core Vim options: leader key = space; expand tabs to 2-space indents; absolute + relative line numbers (relative numbers let you jump n lines with e.g. 5k instead of counting manually); ignorecase + smartcase search; =clipboard=unnamedplus= to share the system clipboard; =scrolloff=16= to keep context above/below the cursor; persistent undo history across sessions.
  • Plugin manager: Lazy (by folke), the most widely adopted Neovim plugin manager. Boilerplate setup script loads every file under a plugins/ directory automatically.
  • Navigation plugins (plugins/navigation.lua):
  • Snacks.nvim (folke) — picker, notifier, input modules enabled; keybinds: leader-F file picker, leader-S grep/search picker, leader-B buffer picker, gd go-to-definition.
  • oil.nvim — file explorer that represents the filesystem as an editable buffer; leader-E opens it; shows hidden files; editing/saving the buffer performs the corresponding file operations (rename, copy via yank+paste, delete via dd + write).
  • Git plugins (plugins/git.lua): Neogit (diff review/staging UI) and gitsigns (inline blame for current line). Neogit lazy-loaded on the BufWinEnter event for faster startup.
  • which-key (folke) shown under a UI module — displays a popup of available keybinds after pressing the leader key, useful for newly added, not-yet-memorized bindings.
  • Custom keybinds (plugins/keys.lua or similar): Esc also saves the file (since Esc-to-normal-mode is usually followed by a save anyway); Ctrl-A selects all; and a fix for Vim's default paste-register behavior so that repeated pastes don't get overwritten by the deleted/replaced text (keeps the yanked text stable across multiple pastes).
  • LSP and Treesitter setup intentionally left out of scope (noted as well covered elsewhere).

Herdr: agent-aware terminal multiplexer

For running multiple AI agent sessions in parallel (kicking off a new agent while another works, reviewing output, etc.), a plain terminal isn't enough — need session/window/pane management. tmux is the classic option (used for years), and WezTerm has built-in multiplexing too, but the video switches to Herdr (https://terminaltrove.com/herdr/), a newer tmux-like, agent-aware multiplexer:

  • Understands what coding agents are and integrates with most mainstream agent harnesses.
  • Possibly the only terminal multiplexer that also works on Windows.
  • Installed as a Homebrew formula via the nix-homebrew brew list (not casks) in configuration.nix.
  • Config at config.toml, symlinked from dotfiles; used mainly to set tmux-like keybinds to preserve muscle memory (defaults are fine if new to tmux).
  • Provides a side panel listing workspaces/agents plus a main terminal area; supports creating/naming tabs and splitting into panes like tmux.
  • Key feature: real-time status integration — when running Claude Code inside Herdr, the side panel shows what the agent is doing. Combined with Claude Code's /statusline command (asked to show model name and % of context window used), Herdr surfaces that status info directly in its panel, consistently across different agent harnesses (not just Claude Code).

Global AGENTS.md memory file

To make all agent harnesses (Claude, Codex, OpenCode, "pi", Grok, etc.) behave consistently, a single shared memory file is authored once in the dotfiles repo (home/agents.md) and symlinked via home-manager into each harness's expected global-memory location (e.g. Claude's CLAUDE.md). Rules included:

  • Never use em dash; use a plain dash instead.
  • Never auto-add the agent as co-author in commit messages — the human remains accountable for code quality, so crediting the agent as co-author isn't seen as useful.
  • Never manually modify CHANGELOG.md or other autogenerated files.
  • When making technical decisions, don't over-weight development cost; prefer quality, simplicity, robustness, scalability, and long-term maintainability. Rationale: LLMs are trained on human data, and humans estimate dev cost in days/weeks/months even though agents work much faster — this biases agents toward cheap, unscalable solutions unless explicitly corrected.
  • When fixing bugs, always first reproduce the bug end-to-end as close to the real user experience as possible, to avoid the agent "fixing" a problem that doesn't actually exist.
  • When doing end-to-end testing, be picky about UI correctness and pixel-perfection — fix clearly-off visual issues encountered along the way even if unrelated to the current task.
  • Hold engineering quality to a high bar generally: fix lint/test failures and flaky tests encountered along the way, even if not caused by the current work.

Fully reproducible result

After the rebuild script incorporates all of the above, the entire environment — OS settings, Homebrew packages, user packages, shell, WezTerm, Neovim, Herdr, and the shared agent memory file — can be reproduced from scratch on any freshly installed Mac by cloning the dotfiles repo and running the rebuild script. The finished dotfiles repo is published on the author's GitHub (linked from the video description, https://github.com/kunchenguid/dotfiles).