nix-agent-init ยท diff
git:20260707.97a986b to git:20260713.b7455b3
11 added, 10 removed. Audit A to A.
---
name: nix-agent-init
description: Use when the user wants to make a NixOS/Home Manager config repo agent-ready (AGENT_MAP.md, CLAUDE.md, .mcp.json) and the host exposes the nix-agent MCP server.
---
# Nix Agent Init
## Overview
- Make a NixOS/Home Manager config repo agent-ready in one pass. Call
- `inspect_flake()` once, then generate three artifacts, each derived from
- the returned facts and the files you actually read:
+ 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: `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.
+ 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
- Call `inspect_flake()`. With no argument it auto-resolves the target; if
- the user named a repo, pass `inspect_flake(flake_uri="/path/to/repo")`.
+ 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"`.
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 `inspect_flake()` or a
- file you read. When a fact is missing, omit the line.
+ - 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.