nix-agent-init · git:20260812.58be358 · 2026-08-12 · sha256 4ffa3d861b4bf758

nix-agent-init git:20260812.58be358A

Immutable. This exact content is served forever at /api/v1/blob/4ffa3d861b4bf758.

---
name: nix-agent-init
description: Onboard an unfamiliar NixOS or Home Manager flake by running nix-agent inspect-flake and generating AGENT_MAP.md, CLAUDE.md, and .mcp.json from observed facts. Use on first contact with a config repo, when the user asks how the flake is laid out, or when making the repo agent-ready. Requires the nix-agent MCP server.
---

# Nix Agent Init

## Overview

Make a NixOS/Home Manager config repo agent-ready in one pass. Run
`nix-agent inspect-flake` once, then generate three artifacts, each derived
from the returned facts and the files you actually read:

- `AGENT_MAP.md`: task-routing table plus search shortcuts.
- `CLAUDE.md` (+ `AGENTS.md`): a one-screen repo brief.
- `.mcp.json`: wires `nix-agent` and `mcp-nixos`.

Everything you write must trace back to an observed fact. A sparse repo
gets a short map; a rich repo gets a longer one. Never pad with generic
NixOS advice, aspirational structure, or rules the facts do not support.
If a fact is null or "unknown", say so or omit the row. Do not invent.

You do not run `nix flake show` yourself: `nix-agent inspect-flake` does.
You do read the repo's own files (module dirs, flake.nix) with your native
tools to name real files in the tables.

## Step 1: Inspect

Run `nix-agent inspect-flake` (via Bash); it prints the fact bundle as JSON.
With no argument it auto-resolves the target; if the user named a repo, pass
the path: `nix-agent inspect-flake /path/to/repo`.

The returned facts drive every decision below:

- `flake_dir`: absolute repo path. Pin `.mcp.json` to this.
- `hosts`: nixosConfiguration names. `null` means flake show failed.
- `home_configurations`: standalone HM outputs.
- `hm_integration`: `"integrated"`, `"standalone"`, `"none"`, or
  `"unknown"`.
- `module_dirs`: dirs where module files actually live.
- `auto_import`: `"import-tree"`, `"none"`, or `"unknown"`.
- `formatter`: `"treefmt"`, `"nixfmt"`, a passthrough name, or `"none"`.
- `lint_tools`: subset of `["deadnix", "statix"]` on PATH.
- `has_justfile`, `has_ci`: booleans.
- `mcp_json`: `"present"` or `"absent"`.

If `hosts` is `null` (flake show failed), note in `CLAUDE.md` which facts
are unavailable and read `note` / `first_error`, then continue with the
file-level facts only (`module_dirs`, `auto_import`, `has_justfile`,
`mcp_json`). Do not stall.

Read the `module_dirs` with your native tools so the routing table names
real files (`hosts/laptop.nix`, `modules/nixos/desktop.nix`), not
placeholders. If `module_dirs` is empty, the table stays generic and
short; that is correct, not a gap to fill.

## Step 2: AGENT_MAP.md

A task-routing table plus search shortcuts, both keyed to the layout you
observed. Columns: Task, Inspect first, Usually edit, Validate.

Derive the **Validate** column from `has_justfile`:

- `has_justfile == true`: read the justfile and map validation to its real
  recipes (`just check`, `just switch`). Cite recipes that exist, not ones
  you assume.
- otherwise: the nix-agent ladder, `check("lint")` then
  `check("dry-build")` then `diff()` then `switch()`. Drop `check("lint")`
  from the recommendation if `lint_tools` is empty.

Derive **Usually edit** targets from `module_dirs` and the files you read.
One row per real task the layout supports (add a package, change a host
option, edit an HM program). Do not emit rows for structure the repo does
not have.

Add a **Search shortcuts** section keyed to the repo's actual directory
names, so an agent greps the right place. Add an **Edit rules** section
only for rules the facts support, for example:

- `auto_import == "import-tree"`: "Drop a new `.nix` file into
  `<module_dir>`; it is imported automatically, no manual import line."
- `auto_import == "none"`: note that new modules need an explicit import.

Skeleton (this is the *shape*, not the content; every cell must come from
this repo's facts):

```markdown
<!-- generated by nix-agent /init -->
# Agent Map

<!-- nix-agent:begin routing -->
| Task | Inspect first | Usually edit | Validate |
| --- | --- | --- | --- |
| Add a system package | `eval_config("environment.systemPackages")` | `modules/nixos/packages.nix` | `check("dry-build")` then `diff()` then `switch()` |
<!-- nix-agent:end routing -->

<!-- nix-agent:begin search -->
## Search shortcuts
- Host definitions: `hosts/`
- NixOS modules: `modules/nixos/`
<!-- nix-agent:end search -->
```

## Step 3: CLAUDE.md and AGENTS.md

A one-screen repo brief. Include only what the facts establish: what this
repo is, its `hosts`, its HM mode, the validation commands (justfile
recipes or the nix-agent ladder), and gotchas the facts support.

When `hm_integration == "integrated"`, state the mode trap plainly: Home
Manager is wired in as a NixOS module, there is no separate
`home-manager switch`, and nix-agent calls that touch HM options must use
`mode="nixos"` (the default) with the attr spelled
`home-manager.users.<user>.<attr>`.

When `hm_integration == "standalone"`, note that HM is its own flake and
its operations use `mode="home-manager"`.

When `hm_integration == "unknown"`, the facts were inconclusive
(lock-file presence alone does not prove integration). Say that. Do not
write a hard mode rule.

Keep it to one screen. Do not restage generic NixOS tutorials.

Then create `AGENTS.md` as a symlink to `CLAUDE.md`:

```bash
ln -s CLAUDE.md AGENTS.md
```

If the platform cannot symlink, write `AGENTS.md` as a copy carrying the
same markers.

## Step 4: .mcp.json

Wire both servers. Pin `NIX_AGENT_FLAKE` to `flake_dir`. Append `#<host>`
only when exactly one host exists; with several hosts, pin the bare dir
and tell the user which host you would otherwise have defaulted to so they
can set it. Never put a secret, token, or password in this file.

```json
{
  "mcpServers": {
    "nix-agent": {
      "command": "nix-agent",
      "env": { "NIX_AGENT_FLAKE": "<flake_dir>[#<host>]" }
    },
    "mcp-nixos": {
      "command": "mcp-nixos"
    }
  }
}
```

Match the host's actual MCP config schema if it differs from this shape;
the constants are the two server names and the `NIX_AGENT_FLAKE` pin.

## Marker and no-clobber contract

This is exact and testable. Follow it verbatim.

- Every file you generate starts with the header line, on its own line:

  ```
  <!-- generated by nix-agent /init -->
  ```

- Each section you own is bounded by a matched pair:

  ```
  <!-- nix-agent:begin <section> -->
  ...generated content...
  <!-- nix-agent:end <section> -->
  ```

- On a **re-run**, regenerate ONLY the content between marker pairs. Text
  the user added outside any marked section is left byte-for-byte intact.

- If a target file already exists and does NOT contain the header marker,
  it is hand-written. Do NOT write it. Instead show the proposed content
  as a diff and let the user decide. This holds for `CLAUDE.md`,
  `AGENT_MAP.md`, and `.mcp.json` alike. `.mcp.json` is JSON and cannot
  carry HTML comments, so treat ANY existing `.mcp.json` as hand-written:
  propose the merged content as a diff, never silently replace it.

## What not to do

- No validation infrastructure. Do not create justfiles, CI workflows, or
  git hooks. Report the ladder; do not build one.
- No editing hand-written docs. Existing unmarked `CLAUDE.md` /
  `AGENT_MAP.md` are proposed-as-diff only, never overwritten.
- No secrets in `.mcp.json`. Reference them via sops-nix/agenix elsewhere;
  this file holds only the flake pin and server names.
- No boilerplate. Every line traces to a fact from `nix-agent inspect-flake`
  or a file you read. When a fact is missing, omit the line.