Changing your Mac with an agent
Your machine is text and every rebuild is reversible, so an AI agent can reconfigure it safely: the skill haus ships, the loop it follows, and the one rebuild it refuses.
Point an AI agent at a normal Mac and it brew installs things, writes
dotfiles, and clicks through System Settings on your behalf. On a haus machine
most of that would be silently undone by the next rebuild, with no record of
what changed.
Declarative config flips that. Every change is text in one file, haus rebuild
builds before it switches so a broken edit never reaches the running system,
and haus rollback puts the last one back in seconds. That makes yours the rare
machine an agent can reconfigure without it being reckless.
The safety was always there; the missing half was knowledge. So haus ships it.
The skill haus writes
Every rebuild writes a skill called haus into the skills directory of each
client in haus.ai.clients: ~/.claude/skills/haus for Claude Code,
~/.codex/skills/haus for Codex, ~/.config/opencode/skills/haus for OpenCode.
Name no clients at all (or turn the room off, which resolves to the same empty
list) and all three get a copy, so the skill is never the missing piece. One body, one copy per client:
| File | What it is |
|---|---|
SKILL.md | The loop, the boundaries, the traps. |
references/options.md | Every haus.* option, generated from the exact revision you're pinned to. |
references/this-machine.md | Your host: hostname, host-file path, which rooms are on, your theme and keys, your app roster and its taken leader keys, plus the rebuild hazards specific to this machine. |
references/recipes.md | Worked examples for the common asks. |
consumer-AGENTS.md | A starter AGENTS.md for your own config repo. |
consumer-CLAUDE.md | Its one-line @AGENTS.md pointer, for the client that reads only CLAUDE.md. |
The option reference being generated is the point: it can only describe
options that exist on your revision (a confidently-wrong option name costs you
a failed build), and haus update regenerates it with the rest.
Check it's there
haus doctor has an Agents section: whether the skill is installed (it
checks every client's directory and names the copy it found), whether your
config repo has an AGENTS.md and the CLAUDE.md pointer beside it, and
whether the app you're running from can write macOS's protected settings.
The loop it follows
Orient: read references/this-machine.md and your host file, and check
haus status.
Find the option: grep the generated reference. If it isn't listed, it doesn't exist on your revision, and the agent should say so rather than guess.
Edit ~/.config/nix/hosts/<hostname>/default.nix, the one file you own.
Apply with haus rebuild: the build gates the switch.
Verify, or haus rollback.
What to ask for
Ordinary requests, in your own words:
- "Install Slack and give it its own workspace" → an entry in
haus.rosterfor the leader key and the Homebrew cask, paired with one inhaus.workspacesfor the workspace and its bar pill. - "Everything's too small" →
haus.ui.scale. - "Switch to light mode" →
haus.theme.flavorplushaus.theme.systemAppearance, so macOS's own Light/Dark moves with the rest. - "Hide the weather in the bar" → the matching
haus.sill.items.*toggle. - "Bind a key to open my notes" →
haus.keys.leaderExtras.
The skill tells the agent to prefer a haus.* option over a raw nix-darwin
setting, and to say so when nothing covers what you asked, because that's a gap
in haus, not something to bolt onto your machine.
What it won't do
- Touch identity or secrets without asking: git identity, signing keys,
haus.secrets.*. - Edit haus itself,
flake.lock, or anything in/nix/store. Pulling a newer revision ishaus update, not a hand edit. - Uninstall apps behind your back. Removing an app from your config stops
haus managing it; the app stays on disk until you
brew uninstall --zapit.
The one rebuild it will refuse
If your host file sets system.defaults.universalaccess.* directly, haus rebuild refuses to run whenever the app it runs under lacks Full Disk
Access, and tells you how to get past it.
That domain is TCC-protected, and the grant belongs to the app the rebuild runs under, not to you and not to root. So this is not an agent rule: an agent pane inside a terminal that holds the grant rebuilds perfectly well, and you at that same keyboard, in a terminal nobody has granted, are refused identically. Without the grant the write fails partway through activation and takes everything after it down too: your bar, tiling and palette come back dead, with the symptom nowhere near the cause. Refusing is the kinder failure: nothing is changed, and your edit is still on disk.
The way past it is always to stop writing the domain by hand.
haus.accessibility.* reaches every key in it that macOS actually honours
(seven of them, which since August 2026 is every key nix-darwin types there,
so the raw form no longer reaches anything the safe one doesn't) with a
guarded write, so without the grant you lose that setting and nothing else,
and the rebuild runs from anywhere. Otherwise:
run it from an app that holds the grant, or set HAUS_FDA_ANYWAY=1 when you know
better (HAUS_AGENT_REBUILD=1, the name this had while the check was
agent-scoped, still works).
haus plan tells you which grant a rebuild wants before it runs, and haus doctor's Permissions section says whether this app has it.
haus.theme.contrast is a different thing again: haus's own palette axis,
painted into the apps haus themes rather than into macOS, so it always applies.
What a rollback doesn't undo
haus rollback rewinds everything Nix manages, instantly. It does not rewind
macOS system settings a rebuild wrote (Dock, keyboard, Finder), and it does not
rewind Homebrew casks; those live outside Nix generations entirely. A good
agent says which kind of change it's about to make.
An AGENTS.md for your own config
~/.config/nix is your own git repo, your machine in text. Dropping an
AGENTS.md in it orients any agent you open there, whether or not it found the
skill. A starter pair ships inside the skill; nothing writes it for you, because
it's your repo:
# whichever of these exists on your machine; the copies are identical:
# ~/.claude/skills/haus · ~/.codex/skills/haus · ~/.config/opencode/skills/haus
skill=~/.claude/skills/haus
install -m 644 "$skill"/consumer-AGENTS.md ~/.config/nix/AGENTS.md
install -m 644 "$skill"/consumer-CLAUDE.md ~/.config/nix/CLAUDE.mdinstall -m 644, not cp: the starter pair are symlinks into the Nix store,
whose files are read-only. A plain cp preserves that mode, so you land on an
AGENTS.md your editor refuses to save, on the one file the whole point of
copying is to then edit.
Two files, because the tools disagree about the filename. AGENTS.md carries
the rules: Codex, OpenCode, Cursor, Zed, Copilot and everything else that
speaks agents.md reads it directly. Claude Code reads only
CLAUDE.md, so the second file is a one-line @AGENTS.md import and nothing
else. Copy one and you orient some of your tools and not others.
Your own instructions, in every client
haus.ai.instructions is the other half of what haus writes into an agent's
home: your always-on operating context, rendered once per installed client at
the path that client reads (~/.claude/CLAUDE.md, ~/.codex/AGENTS.md,
~/.config/opencode/AGENTS.md), under a short generated preamble.
haus.ai.instructions = ''
# Global instructions
Ship small, verified changes. Ask before anything hard to reverse…
'';Write it client-neutrally: the same text reaches whichever agent your ⌘A pane
spawned, so a line about one client's flags is noise in the other two. Empty (the
default) writes nothing at all, so a hand-managed instructions file is never
touched. Setting it on a machine that already has one, though, moves yours
aside to <file>.backup. And haus.ai.skill = false; stops the skill being written
at all, for when you'd rather own each client's skills directory yourself.
(Both were haus.claude.globalMd and haus.claude.skill until August 2026; the
old names still evaluate, with a warning.)
It's still your machine
An agent that can rebuild your Mac can change how your Mac behaves. The safety here is real, but it is recovery, not prevention: the build gates the switch and the rollback is atomic, so mistakes are cheap, not impossible. Read the diff before you say yes, exactly as you would for any other change to your config.
AI
Coding agents as a room: the clients this machine installs, holt giving each agent its own checkout, and the lifecycle around them. Any git repo, any client.
Text expansion
Type a short trigger, get the long thing, in any app, terminals and Electron included, from a table you keep in your host file.