babarot's dotfiles declare a whole macOS setup in one Nix flake
A repository that gathered files starting with dot
At a glance
- What is it?
- This repository is a personal macOS environment for two Macs declared in a single nix-darwin and home-manager flake, with a shell that stays plain when an AI agent is driving it, a one file one lifecycle rule for every tool, and a working example of wrapping a binary so it survives another package being deleted.
- Who is it for?
- People who already run nix-darwin and home-manager and want to see how one person keeps a personal macOS environment coherent should read this repository, because the one file one lifecycle rule and the agent-aware shell are patterns that transfer to any home-manager setup.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Nix, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One flake for two Macs, and a deliberately plain shell
The repository is a personal macOS environment for two Macs, declared in one Nix flake that combines nix-darwin and home-manager.
The stack it declares is small and specific. The terminal is Ghostty with Herdr, with one workspace per git worktree for Claude Code and Codex. The editor is Neovim. The package manager is Nix itself.
The most interesting part of the README is the section on the shell being for humans and AI agents. The premise is stated without ceremony: AI agents such as Claude Code and Codex now run more commands in this shell than the author does, so the shell is plain by default and human UX is opt-in.
The mechanism is a single shell variable..zshenv defines is_human as true only when standard input and standard output are a TTY and no agent marker such as CLAUDECODE or AI_AGENT is set. When an agent is present, the shell gets plain zsh: none of the human aliases such as cp -i, no ls rewritten to eza, no fzf-driven cd, and EDITOR set to true with PAGER set to cat, so nothing waits for input. The.zshrc then returns early unless is_human, and below that line come the aliases, plugins, prompt, keybinds and setopts.
Export AI_AGENT=1 to force the agent side. That is a whole design philosophy in three lines of shell: a human gets the decorated experience, an agent gets a predictable, non-interactive one, and the switch is explicit.
The repository is not archived and the last push was on 2026-10-21. It publishes no GitHub releases, and its licence is recorded as one that GitHub cannot classify.
One file, one lifecycle
The organising rule of the repository is the one file, one lifecycle section, and it is the idea most worth taking from this setup.
What is added and removed together is installed and configured in one file under nix/home-manager/tools/, or under nix/hosts/<host>/ for a single Mac, and it is imported automatically. Usually it is one tool, sometimes a tool with its companions, or a set. Adding a tool is adding a file, and deleting the file is all it takes to remove it.
Three properties make that safe, and the README names them. Cohesion means deleting a tool's file removes everything that was for it, the package, the aliases, the variables and the plugins, so nothing outlives the tool. Loose coupling means deleting a tool's file breaks nothing else, because a tool used inside another tool's settings is referenced by its store path rather than through PATH. And reproducibility means a new Mac gets the same thing, because everything is declared in the flake and pinned by flake.lock, with the exceptions declared explicitly, such as self-updating apps installed through Homebrew casks and Claude Code.
The settings of a tool sit next to its package, split by who they are for. my.human is for humans only, my.ai is for agents only, and my.env is for both. The rules are written down in AGENTS.md, under a section on principles, and the mechanism that makes the properties hold is explained in docs/concepts/dependencies.md.
The practical value of this is that the answer to what happens when I remove a tool is a file deletion rather than an audit. For a personal environment that is the difference between a setup you can trust and one you slowly stop trusting.
The Kubernetes set, and how a wrapper outlives the package it uses
The README's worked example is the Kubernetes tool set, and it exists to demonstrate the loose coupling property rather than to show off a Kubernetes setup.
# nix/hosts/PC-M-2025-026/kubernetes.set.nix (abridged)
# A set: the Kubernetes tools, added and removed together with this file
{ lib, pkgs, ... }:
let
# kubectx runs fzf for its picker, looking it up on PATH by name. This wraps
# kubectx so it finds fzf by its path in the Nix store instead: deleting
# fzf.nix takes fzf off PATH, but not away from kubectx.
kubectx = pkgs.symlinkJoin {
name = "kubectx-with-fzf";
paths = [ pkgs.kubectx ];
nativeBuildInputs = [ pkgs.makeWrapper ];
postBuild = ''
wrapProgram $out/bin/kubectx --suffix PATH : ${lib.makeBinPath [ pkgs.fzf ]}
'';
};
in
{
# Installed by this file, and uninstalled when it is deleted
home.packages = with pkgs; [
helmfile
kubectl
kubectx # the wrapped one above
kustomize
];
# For humans only: agents never get this abbreviation
my.human.plugins.zsh-abbr.init = ''
abbr --session --quiet k=kubectl
'';
}The problem being solved is narrow and real. kubectx runs fzf for its picker and looks it up on PATH by name, so if fzf is declared in its own file and that file is deleted, kubectx breaks. The fix is a wrapper: symlinkJoin with makeWrapper and wrapProgram adds the fzf store path to the PATH of the wrapped binary only, so deleting fzf.nix removes fzf from PATH while kubectx keeps working, because it no longer depends on PATH.
The comment in the file says the same thing in one line, and the README makes the general point: in the example, kubectx's picker keeps working after fzf.nix is deleted, and fzf on PATH is deleted along with it.
The last part of the example shows the audience split again. The zsh-abbr plugin that turns k into kubectl is under my.human.plugins, and the comment says agents never get this abbreviation.
The layout separates system state from hand-written dotfiles
The repository listing in the README is short and explains how the pieces fit.
flake.nix # one configuration per Mac
nix/
darwin.nix, macos.nix # system settings and macOS System Settings for every Mac
homebrew.nix # vendor apps installed by Homebrew casks
hosts/ # per-Mac packages, casks and App Store apps
home-manager/ # dotfile links, shell env, tool files in tools/
...
home/ # hand-written dotfiles, linked into ~ under the same names
.zshenv, .zshrc, .zsh/ # zsh
.config/ # linked to ~/.config as a whole
.claude/ # Claude Code user settings
bin/ # scripts still being shaped
skills/ # my own Agent Skills on trial
...
docs/ # guides, concepts and reference (see docs/README.md)The division is between nix/ and home/. Everything under nix/ is the declared environment: system settings and macOS System Settings for every Mac, vendor apps installed by Homebrew casks, per-Mac packages, casks and App Store apps under hosts/, and the home-manager configuration with the dotfile links, the shell environment and the tool files.
Everything under home/ is hand-written and linked into the home directory under the same names. The .config directory is linked to ~/.config as a whole rather than file by file, .claude holds Claude Code user settings, bin holds scripts still being shaped, and skills holds the author's own Agent Skills on trial.
The last two entries are worth noticing. A bin directory of scripts still being shaped and a skills directory of agent skills on trial describe an environment that is expected to keep moving, and both are described in the repository's own words as in progress rather than as finished products.
The docs directory is the fourth piece, with guides, concepts and reference indexed in docs/README.md, and the top level of the repository adds a .githooks directory, a .gitleaksignore file, an AGENTS.md and a flake.nix with a flake.lock.
Applying a change is one rebuild command
Changes are applied with a single command, and the documentation for the rest of the lifecycle lives in the docs directory.
sudo darwin-rebuild switch --flake ~/src/github.com/babarot/dotfilesThat is the whole apply step. The flake is pointed at by path, and darwin-rebuild performs the switch, which is the nix-darwin mechanism for moving the machine from one configuration to another.
The README then points at the maintenance guide for the other half of the lifecycle, updating packages, with a link to the update-flake-inputs section of docs/guides/maintenance.md.
The documentation set is organised as a set of guides rather than one long file. There is a setup guide for setting up a Mac, and the README says everything else, including how things work, how to maintain them and why they are this way, is indexed in docs/. The supporting documents named along the way are AGENTS.md for the principles behind the tool file rules, docs/concepts/dependencies.md for how Nix makes those properties hold, docs/guides/setup-mac.md and docs/guides/maintenance.md, and the skills directory for agent skills on trial.
The combination is what makes the setup maintainable. A configuration that has to be applied and then maintained has two halves, and this one documents both, with the reasoning kept next to the rules.
Where this dotfiles repository is the wrong tool
The limits are the ones that come with a personal configuration, and they are honest ones.
First, it is not a template. It encodes one person's environment: Ghostty with Herdr, Neovim, a specific Kubernetes tool set, a specific shell aesthetic and a specific opinion about which aliases a human should have and which an agent should not. Cloning it gives you that environment, not a starting point, and the difference between the two is the work of replacing everything you would not have chosen.
Second, it assumes Nix, and specifically nix-darwin and home-manager on macOS. The system settings, the macOS System Settings integration, the Homebrew cask handling and the App Store app handling are all Darwin specific. There is no Linux path described here, and nothing in the structure suggests one.
Third, the agent-aware shell is a personal bet. The premise that agents run more commands in your shell than you do is not true for most people, and the is_human check with its list of agent markers is a mechanism to copy rather than a decision most users will make. It is also the part most likely to surprise: a shell that behaves differently depending on environment variables is a shell where behaviour is harder to reason about.
Fourth, the licence is not classified. For a personal dotfiles repository that hardly matters. For a company that wants to reuse the tool file structure, the wrapping trick for kubectx, or the agent-aware shell pattern, the terms need checking before the code is copied, and a repository that states its own principles in AGENTS.md without a stated licence is a gap worth raising with the author.
What is reusable regardless is the shape: one file per tool, settings split by audience, and dependencies referenced by store path rather than by PATH.
Editorial conclusion
People who already run nix-darwin and home-manager and want to see how one person keeps a personal macOS environment coherent should read this repository, because the one file one lifecycle rule and the agent-aware shell are patterns that transfer to any home-manager setup. It is the wrong starting point for anyone who does not already use Nix, since every piece of the environment is expressed as Nix, and it is the wrong thing to clone wholesale, because the configuration encodes one person's tools, one person's aliases and one person's judgement about what an AI agent should see. Before adopting anything, read AGENTS.md and docs/concepts/dependencies.md, and remember that the repository is licensed as unclassified, so the terms of use need checking.
Frequently asked questions
How does the dotfiles shell decide whether a human or an AI agent is using it?
The .zshenv file defines is_human as true only when standard input and standard output are a TTY and no agent marker such as CLAUDECODE or AI_AGENT is set. The .zshrc returns early unless is_human, so agents get plain zsh with EDITOR=true and PAGER=cat, and you can force the agent side with AI_AGENT=1.
What is the one file, one lifecycle rule in babarot/dotfiles?
Each tool is installed and configured in one file under nix/home-manager/tools/, or under nix/hosts/<host>/ for one Mac, and it is imported automatically. Deleting that file removes the tool and everything configured for it, and the README explains how cohesion, loose coupling and reproducibility are maintained.
How do I apply changes to this macOS configuration?
Run sudo darwin-rebuild switch --flake with the path to the flake, for example the path shown in the README. Package updates are handled separately, with the README pointing at the update-flake-inputs section of docs/guides/maintenance.md.
What does the Kubernetes example in the README demonstrate?
It shows how a wrapped binary can survive another package being deleted. kubectx finds fzf on PATH by name, so it is wrapped with makeWrapper and wrapProgram to add the fzf store path instead, which means deleting fzf.nix removes fzf from PATH without breaking the kubectx picker.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/babarot-dotfiles)Was this useful?