Watch the walkthrough: https://youtu.be/5N-okeDdIuI
My personal Mac setup, managed with nix-darwin and home-manager. One repo, one command, and a fresh Mac ends up configured the same way every time.
These are my personal dotfiles, shared publicly so people can read them, learn from them, and fork them freely. Feature requests and pull requests are not accepted here, and PRs are auto-closed. If you find a bug, please open a GitHub Issue using the bug report template.
Running the switch builds:
- System settings (dark mode, key repeat, dock, Finder, trackpad)
- Homebrew apps (casks and CLI tools)
- Nix user packages (ripgrep, fd, fzf, jq, lazygit, Neovim, Hack Nerd Font)
- Shell (zsh, aliases, starship prompt)
- Editor (Neovim config with the rose-pine moon theme)
- Terminal (Ghostty config with the Cursor Dark theme)
- Agent configs (Claude, Codex, opencode all share one AGENTS.md)
- Pi coding agent, theme and local extensions, generic UI settings and model overrides, plus two deliberately pinned third-party Pi packages
- Apple Silicon Mac, by default.
- Intel Mac: change one line.
In
configuration.nix, setnixpkgs.hostPlatform = "x86_64-darwin";(the comment right there tells you the same thing).
On a brand new Mac, from a bare clone of this repo:
git clone https://github.com/kunchenguid/dotfiles.git
cd dotfilesBefore you run it: review "Make it yours" below.
Change the host label or CPU architecture if needed, and read the Homebrew cleanup warning.
bootstrap.sh applies the config to your machine, so do this first.
./bootstrap.shbootstrap.sh does four things, in order:
- Installs Determinate Nix, if it isn't already installed.
- Checks that this repo lives at
~/dotfiles.home.nixpoints at config files through that path, so the build links to nothing if the repo lives anywhere else. - Checks the
userconfigured inflake.nixagainst your actual macOS username, and offers to fix it for you if they differ. - Runs the first
darwin-rebuild switch. It fetches thedarwin-rebuildtool from the nix-darwin 26.05 release branch, then applies this repo's locked flake config.
After that, darwin-rebuild exists and you're on the normal workflow below.
Once Nix is installed (bootstrap.sh step 1 handles that), you can check that the config builds without touching your system - handy when you have edited something:
nix flake check --no-build
nix build .#darwinConfigurations.mac.system --dry-runIf you renamed the host label in "Make it yours", substitute your label for mac in these commands.
Edit the config files in place, then apply:
./rebuild.shThat's it. No separate build-and-copy step.
This repo is mine.
If you clone it, review these before you run bootstrap.sh:
- Username: run
./bootstrap.sh(it detects your macOS username and offers to set it) OR change the singleuser = "kunchen"line inflake.nix. Everything else (configuration.nix,home.nix, home directory paths) is threaded from that one variable. - Host label
"mac", in three places:flake.nix(thedarwinConfigurations."mac"name),rebuild.sh:5(the#macat the end of the flake reference), andbootstrap.sh's first-switch command (also#mac). All three have to match. - CPU architecture,
hostPlatforminconfiguration.nix(see Prerequisites above).
Git identity: this config deliberately does not set your git name or email.
Git will stop your first commit and tell you to set them (git config --global user.name "Your Name" and git config --global user.email you@example.com).
If you'd rather manage that declaratively, add this back to home.nix with your own identity:
programs.git = {
enable = true;
settings.user = {
name = "Your Name";
email = "you@example.com";
};
};Homebrew cleanup warning: configuration.nix sets homebrew.onActivation.cleanup = "zap".
That means every time you switch, Homebrew removes any package or cask on your machine that isn't listed in the brews and casks arrays in configuration.nix.
If you already have Homebrew stuff installed that isn't in that list, the first switch will uninstall it.
Read through brews and casks before you run bootstrap.sh or rebuild.sh for the first time, and add anything you want to keep.
About herdr: it's in the brews list.
It's a real public Homebrew formula (brew info herdr finds it in homebrew-core, no tap needed), so it will install fine.
If you don't use it, just remove it from brews in your copy.
Heads-up:
home/AGENTS.mdis my personal agent policy, andhome.nixinstalls it for Claude, Codex, and opencode. If you clone this repo, you'd silently inherit my agent instructions - edit or deletehome/AGENTS.mdif you don't want that.- The
ccalias inhome/.zshrcrunsclaude --dangerously-skip-permissions;corunscodex. Codex's symlinkedhome/.codex/config.tomlsets globalapproval_policy = "never"andsandbox_mode = "danger-full-access". New Codex sessions default to no approval prompts and no sandbox across projects, unless overridden.
flake.nix- the entry point. Wires up nixpkgs, nix-darwin, home-manager, and nix-homebrew, and declares themacmachine.configuration.nix- system-level config: macOS defaults, Homebrew.home.nix- user-level packages and the symlinks described below.rebuild.sh- re-applies the config after the first switch. Run this after changing Nix declarations or adding managed links.home/- the actual config files that get symlinked into place; the sections below explain the shared symlink model and Pi's narrower selective setup.
The files under home/ are the real files - editing them here is editing your live config, no rebuild needed to see the change in your editor.
home.nix uses mkOutOfStoreSymlink to point paths like ~/.config/nvim straight at home/.config/nvim in this repo, so the two never drift out of sync.
You only run ./rebuild.sh when you change something that isn't just a symlinked file, like a package list or a system default.
Zsh settings, aliases, and fzf integration live in home/.zshrc; Starship settings live in home/.config/starship.toml. Nix installs the tools and exposes stable plugin paths under ~/.local/share/zsh-packages. Start a new shell after editing .zshrc, or use reload to rebuild and restart it. Codex links only config.toml, leaving credentials and sessions in its local directory.
Codex and Claude Code both declare Semble, shadcn, Atlassian, Grafana, and Excalidraw:
- Codex reads
home/.codex/config.tomlthrough its existing global config symlink. - Claude Code auto-loads the
dotfiles-mcp@skills-dirplugin fromhome/.claude/skills/dotfiles-mcp/. Home Manager links that directory into~/.claude/skills/, making the servers available across projects without CLI flags. This requires a Claude Code version with skills-directory plugin support (verified with 2.1.267); the Homebrew declaration installsclaude-code@latest.
Run ./rebuild.sh to install the Claude plugin link, then start new agent sessions.
Check discovery with codex mcp list and
claude plugin details dotfiles-mcp@skills-dir; use /mcp inside Claude to check
connections and authenticate. In Codex, use codex mcp login atlassian and, if
required by the server, codex mcp login grafana.
Each app keeps its own authentication state outside the repo.
Grafana points at a work-specific endpoint and requires the appropriate network access.
Excalidraw uses its recommended
hosted server at https://mcp.excalidraw.com, so no local build is needed.
Its interactive diagram interface requires a client with MCP Apps support.
When adding or changing a server, update both Codex's mcp_servers tables and
the Claude plugin's .mcp.json. Keep tokens out of these tracked files; use
OAuth or the clients' environment-variable credential settings instead.
node and uv are already declared in configuration.nix; npx and uvx resolve
the shadcn and Semble packages on launch, so those package versions are not
pinned by the Nix lockfile.
The four tools in the official AXI catalog are installed using
their upstream skill-based setup: gh-axi, chrome-devtools-axi, lavish-axi
(skill name lavish), and quota-axi. No community catalog tools are included.
Their official skills live in home/.agents/skills/ and home.nix links each one
into ~/.agents/skills/, ~/.claude/skills/, and ~/.codex/skills/. Run
./rebuild.sh after adding or changing those links. The skills invoke the CLIs
with npx -y; node, gh, and google-chrome are already declared in
configuration.nix. No global npm install or extra Homebrew tap is needed.
The skill files are checked in; CLI versions are resolved by npx rather than
pinned by the Nix lockfile. Update the skills from their respective
kunchenguid/<tool> repositories, keeping upstream content intact.
The Pi coding agent is declared as pi-coding-agent in configuration.nix's Homebrew package list. ./bootstrap.sh or ./rebuild.sh installs it with the other managed CLI tools. Launch it in a project:
piPi Launcher is also optional and installed from its owner, not declared by this config:
brew install --cask kunchenguid/tap/pi-launcherHome Manager owns exactly two repository-authored Pi directories: ~/.pi/agent/themes and ~/.pi/agent/extensions. It also links models.json and settings.json as individual files. The local extension directory is for public, repository-authored extensions only - third-party package code never belongs there. Run /reload after editing a local extension or other Pi resources. The terminal-title extension shows a spinner while Pi is working, then a completion mark with the session name or current directory. The rose-pine-moon theme was authored clean-room from the public Rosé Pine Moon palette and Pi's public theme schema, not from a private or live theme file.
home/.pi/agent/extensions/calm is a standalone local Pi extension. Home Manager's existing global extensions-directory link makes Pi auto-load it without another declaration. /calm toggles a conversation-only presentation mode and is off by default. Its choice is stored locally in ~/.pi/agent/calm (or the directory selected by PI_CODING_AGENT_DIR), not in this repository or Home Manager. Adapted from Firstmate under the bundled MIT license, Calm imports no Firstmate modules and has no Firstmate runtime dependency.
When enabled, Calm hides collapsed thinking and the call/result shells for Pi's seven built-in tools (read, bash, edit, write, grep, find, and ls) without leaving blank transcript rows. During an active run it replaces Pi's working row with a two-line animated blue-water, yellow-boat widget. /calm restores Pi's stock rendering and preserves the existing Ctrl+O tool-expansion choice.
Calm never changes prompts, tool execution, model context, session data, or ordering. /share and /export use the complete stock transcript. Generic custom tools, images, and unsupported Pi transcript classes deliberately remain visible because Pi has no safe general-purpose transcript filter. If a future Pi release no longer exports the exact collapsed-thinking rendering seam, Calm logs one diagnostic and leaves only that adapter disabled; all other behavior remains available.
Pi's package system declares two third-party sources in the linked global settings.json:
npm:@ryan_nookpi/pi-extension-codex-fast-mode@0.2.6- the exact public npm release fromryan_nookpi.git:github.com/algal/pi-openai-server-compaction@c6d593087709e9481223dc6c6c2269b371b5e055- the exact publicalgalcommit for experimental OpenAI server-side compaction.
The version and commit are immutable pins, so Pi does not move them during package updates. Deliberate updates require a new source and security audit, followed by an explicit pin change in home/.pi/agent/settings.json. On Pi 0.82.0, global settings declarations install missing pinned packages automatically at startup. No one-time install command is required. Pi keeps the downloaded npm and git package trees in its own unmanaged ~/.pi/agent/npm and ~/.pi/agent/git runtime directories, outside Home Manager and Git tracking.
Both packages execute with your full user permissions and must be trusted like any other executable code. The compaction package is experimental, sends the relevant OpenAI compaction and continuity data to OpenAI, and upstream declares the stale peer range >=0.80.9 <0.81.0; this exact immutable ref was locally proven to load and perform remote compaction on Pi 0.82.0. Do not treat that proof as a guarantee for a different Pi version or a different package ref.
Home Manager deliberately does not manage ~/.pi/agent itself, or Pi authentication, sessions, trust decisions, caches, npm/git package trees, or any other runtime state. The model overrides contain no credentials or endpoint settings, do not choose a default model, and only take effect after you authenticate Pi yourself. Use /login inside Pi to connect a supported subscription, then /model to choose a model. Homebrew manages the CLI version separately from the pinned extension packages; the Pi 0.82.0 compatibility proof above does not cover later CLI releases. A launcher and third-party package source code are not installed into this repository.
The first time you launch nvim, it bootstraps lazy.nvim by cloning plugins from GitHub.
That needs network access once; after that it's offline.
Neovim uses the rose-pine moon theme; Ghostty uses Cursor Dark.
Neovim keeps italics off and uses a transparent background on macOS, Windows, and WSL so it matches the terminal setup.
This repo is licensed under MIT No Attribution.
See LICENSE.