# Changing your Mac with an agent (/docs/haus/agent-rebuilds)



Point an AI agent at a normal Mac and it `brew install`s things, writes dotfiles
and clicks through System Settings, and the next rebuild silently undoes most of
it. Here every change is text in one file, `haus rebuild` **builds before it
switches**, and `haus rollback` puts the last one back in seconds. The safety was
always there. The missing half was knowledge, so haus ships it.

## The skills haus writes [#the-skills-haus-writes]

Every rebuild writes a skill called `haus` into the skills directory of each
client in `haus.ai.clients`: `~/.claude/skills/haus`, `~/.codex/skills/haus`,
`~/.config/opencode/skills/haus`, `~/.pi/agent/skills/haus`. Name no clients at
all, or turn the room off, and the copies are written anyway, so the skill is
never the missing piece on a machine running a client haus didn't install.

| File                                       | What it is                                                                                                                                          |
| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `SKILL.md`                                 | The loop, the boundaries, the traps. Capped at 150 lines: it routes.                                                                                |
| `references/rooms.md`                      | Which room a request belongs to, and whether it has a runtime verb. Read before `options.md`.                                                       |
| `references/options.md`                    | Every `haus.*` option.                                                                                                                              |
| `references/this-machine.md`               | Your hostname and host file, the rooms that are on, your theme, keys and app roster with its taken leader keys, and this machine's rebuild hazards. |
| `references/recipes.md`                    | Worked examples for the common asks.                                                                                                                |
| `references/vm.md`                         | Seeing a change without taking your screen: a headless macOS VM driven over `ssh`.                                                                  |
| `consumer-AGENTS.md`, `consumer-CLAUDE.md` | A starter pair for your own config repo.                                                                                                            |

`rooms.md` and `options.md` are **generated from the exact revision you're pinned
to**, so they cannot name an option you don't have, and `haus update` regenerates
them.

`haus.ai.skill` puts every hausfold tool's own skill in the same directory, and
a skill with a binary behind it follows the room that installs that binary,
because instructions for a command your Mac hasn't got are worse than none. Only
`nebelung` (this machine's palette) has no room to wait for. `scruff`
(agent worktrees, and setting work aside without `git stash`), `handoff` (turning
a piece of work into a brief a cold session can act on) and `factory`
([merging pull requests](/docs/haus/night-shift) while nobody watches) come with
[the AI room](/docs/haus/rooms/ai), which is what puts `scruff` and `factory` on
your PATH. `trill` waits for
[`haus.notifications.compositor`](/docs/haus/rooms/notifications), `pounce` for
[`haus.launcher.enable`](/docs/haus/rooms/launcher), `perch` for
[`haus.shelf.enable`](/docs/haus/rooms/shelf). Those three switches are about the
room rather than the app, so `haus-notify` finds a Trill.app you installed
yourself either way.

A second skill of haus's own, `hausfold`, carries a rough edge upstream. Nothing we make sends
telemetry, so a GitHub issue is the only way one reaches the people who made it,
and you usually notice one while talking to an agent. It knows which repo owns
which symptom and whichever `report` verb or `doctor` fills that repo's bug form
in, and files nothing without showing you the whole draft first, diagnostics block
included.

<Callout type="tip" title="Check it's there">
  `haus doctor` has an **Agents** section: whether the `haus` skill is installed
  (it names the copy it found in each client's directory), 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.
</Callout>

## The loop it follows [#the-loop-it-follows]

### Read the machine [#read-the-machine]

`references/this-machine.md`, your host file, and `haus status`.

### Find the option [#find-the-option]

Grep the generated reference. &#x2A;*If it isn't listed it doesn't exist on your
revision, and the agent says so rather than guessing.**

### Edit one file [#edit-one-file]

`~/.config/nix/hosts/<hostname>/default.nix`, the one file you own.

### Apply [#apply]

`haus rebuild`.

### Verify, or roll back [#verify-or-roll-back]

Check the change is really there, in the app it belongs to. If it isn't,
`haus rollback`.

## What to ask for [#what-to-ask-for]

Ordinary requests, in your own words.

| You say                                         | It reaches for                                                                                                     |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| *"Install Slack and give it its own workspace"* | `haus.roster`, for the leader key and the Homebrew cask, plus `haus.workspaces` for the workspace and its bar pill |
| *"Everything's too small"*                      | `haus.ui.scale`                                                                                                    |
| *"Switch to light mode"*                        | `haus.theme.flavor` with `haus.theme.systemAppearance`, so macOS's own Light/Dark moves too                        |
| *"Hide the weather"*                            | a `haus.bar.items.*` toggle                                                                                        |
| *"Bind a key to my notes"*                      | `haus.keys.leaderExtras`                                                                                           |

The skill prefers a `haus.*` option over a raw nix-darwin setting, and says so
when nothing covers your ask, because that's a gap in haus.

## What it won't do [#what-it-wont-do]

Touch identity or secrets without asking: git identity, signing keys,
`haus.secrets.*`. Edit haus itself, `flake.lock` or anything in `/nix/store`,
since a newer revision is `haus update`. Uninstall apps behind your back:
dropping one from your config stops haus *managing* it, and the app stays on disk
until you `brew uninstall --zap` it.

<Callout type="warn" title="The one rebuild it will refuse">
  A host file that sets `system.defaults.universalaccess.*` directly makes `haus
  rebuild` refuse whenever the app it runs under lacks **Full Disk Access**. It is
  **not an agent rule**: the grant follows the app, so an agent pane in a granted
  terminal rebuilds perfectly well, and you at that same keyboard in an ungranted
  one are refused identically. Point the host file at `haus.accessibility.*`
  instead, or rebuild from an app that holds the grant. [The one rebuild it
  refuses](/docs/haus/reference/haus#the-one-rebuild-it-refuses) has the whole
  rule.
</Callout>

## What a rollback doesn't undo [#what-a-rollback-doesnt-undo]

`haus rollback` rewinds everything Nix manages, instantly. It does **not** rewind
the macOS settings a rebuild wrote (Dock, keyboard, Finder), and it does not
rewind Homebrew casks. A good agent says which kind of change it's about to make.

A rebuild *you* ran that fails offers the same help the other way round, in [When
a rebuild fails](/docs/haus/rooms/ai#when-a-rebuild-fails): `haus fix`, one agent
turn that never activates and whose whole change is one commit, so `git -C ~/.config/nix revert HEAD` puts it back.

## An AGENTS.md for your own config [#an-agentsmd-for-your-own-config]

`~/.config/nix` is your own git repo, your machine in text, and an `AGENTS.md` in
it orients any agent you open there, skill or no skill: what the repo is, which
file is yours to edit, and that a change lands with `haus rebuild`. A starter pair
ships inside the skill, and an agent that has the skill offers to copy it in when
that repo has neither file. Nothing writes it for you, because it's your repo. By
hand it's two lines:

```sh
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.md
```

Every client gets its own identical copy of the skill, so without Claude Code
point `skill` at `~/.codex/skills/haus`, `~/.config/opencode/skills/haus` or
`~/.pi/agent/skills/haus` instead.

`install -m 644`, not `cp`: the pair are symlinks into the read-only Nix store and
`cp` preserves that mode, landing you on an `AGENTS.md&#x60; your editor refuses to
save. Two files, because the tools disagree about the filename. **`AGENTS.md`
carries the rules**, read by Codex, OpenCode, Cursor, Zed, Copilot and everything
else that speaks [agents.md](https://agents.md); Claude Code reads only `CLAUDE.md`, so that one is
a one-line `@AGENTS.md` import.

## Your own instructions, in every client [#your-own-instructions-in-every-client]

`haus.ai.instructions` is 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`, `~/.pi/agent/AGENTS.md`),
under a short generated preamble.

```nix
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 ⌘⏎ lane
spawned. Empty, the default, writes nothing, so a hand-managed instructions file
is never touched; *setting* it on a machine that already has one moves yours aside
to `<file>.backup`. `haus.ai.skill = false;` stops every skill above being
written; `haus.ai.skillExclude = [ "factory" "pounce" ];` leaves out just the
ones you name, and a name haus ships no skill by fails the rebuild rather than
changing nothing.

## Reading the docs, without the skill [#reading-the-docs-without-the-skill]

On a machine that has haus, prefer the skill: it is generated from the revision
you actually run. Everywhere else these docs publish themselves machine-readably.
[`/llms.txt`](https://hausfold.co/llms.txt) is a table of contents for something
that fetches selectively, [`/llms-full.txt`](https://hausfold.co/llms-full.txt)
is every page inlined, and any page is its own Markdown from the button above its
title.

<Callout type="warn" title="It's still your machine">
  An agent that can rebuild your Mac can change how your Mac behaves. The safety
  here is mostly *recovery* rather than prevention: mistakes are cheap, not
  impossible. Read the diff before you say yes. Activating (`haus rebuild`,
  `darwin-rebuild switch`) is the one slice that does get prevention, because it
  redraws the whole desktop:
  [`agent-desktop-guard`](/docs/haus/rooms/ai#the-screen-belongs-to-you) puts `haus
  rebuild` back in front of you, even in a pane running on auto.
</Callout>
