This is Dave's headless macOS dev environment (cm01, MacBook Air M1), managed declaratively via nix-darwin + Home Manager (flake-based).
Everything is declarative. No brew install, no claude plugin marketplace add, no pip install, no manual edits to ~/.claude/* files, no editing /etc/sudoers directly. If a change can't be expressed in configuration.nix, home.nix, or files under this repo, stop and flag it — don't reach for the imperative shortcut.
There is one known mutable-state gap: marketplace registration for Claude Code plugins (see below). Don't introduce new gaps without explicit signoff.
drs — alias for sudo darwin-rebuild switch --flake ~/.config/nix-darwin. Passwordless sudo is wired for this command only.
configuration.nix— system-level: packages, casks (Homebrew via nix-darwin), sudo rules, system defaults.home.nix— user-level: shell, git, tmux, Claude Code config, MCPs, skills, hooks.scripts/— wrapper scripts referenced from Nix (e.g.scripts/bambuddy-mcp-wrapper). Added to PATH.claude/skills/cw-*— local skill dirs symlinked viahome.file.secrets/*.yaml— sops-encrypted (age key).
- Local skill (lives in this repo): drop the dir under
claude/skills/<name>/and add ahome.file.".claude/skills/<name>" = { source = ./claude/skills/<name>; recursive = true; };entry. - Upstream skill (from a GitHub repo): add a
pkgs.fetchFromGitHubto theletblock at the top ofhome.nix(pinnedrev+sha256), thenhome.file.".claude/skills/<name>".source = "${src}/path/to/skill";. See themattpocock-skillsblock for the pattern. Get sha256 with:nix-prefetch-url --unpack https://github.com/<owner>/<repo>/archive/<rev>.tar.gz.
Go in home.file.".claude/mcp.json", NOT programs.claude-code.settings.mcpServers. Claude Code reads from mcp.json, not from settings.json. We've made this mistake before.
Add to programs.claude-code.settings.enabledPlugins in home.nix as "<plugin>@<marketplace>" = true;. The marketplace itself must already be registered via claude plugin marketplace add <source> — that step is the one mutable-state gap; the registration sits in ~/.claude/plugins/known_marketplaces.json. Don't try to write that file via home.file — Claude Code rewrites it.
Hook command strings must be prefixed with bash (e.g. "bash ~/.claude/hooks/foo.sh"). Files in the nix store are r--r--r-- (no execute bit), so direct invocation fails with a PreToolUse:Bash hook error.
security.sudo.extraRulesdoesn't exist in nix-darwin. Usesecurity.sudo.extraConfigwith raw sudoers syntax. (NixOS hasextraRules; nix-darwin doesn't.)- Home Manager 25.05 git signing:
programs.git.signing.formatmust be set at the module level, NOT insideprograms.git.settings. Setting it insidesettingsfails type-check. - macOS
grep -Pdoesn't work — usesedfor regex extraction in any script that needs to run here. - Plugin install via
claude plugin installwrites tosettings.json, which is nix-managed (read-only). UseenabledPluginsinhome.nixinstead. uvanduvxare installed system-wide viaconfiguration.nix. Useuv run --project <path>to invoke project-local CLIs.
No Co-Authored-By lines. Short, conventional-ish messages (fix:, feat:, docs:).