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: wiresnix-agentandmcp-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.jsonto this.hosts: nixosConfiguration names.nullmeans 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")thencheck("dry-build")thendiff()thenswitch(). Dropcheck("lint")from the recommendation iflint_toolsis 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.nixfile 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):
<!-- 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. When the
brief includes the nix-agent ladder, state that the default is apply:
diff() then switch() without waiting, unless the user asked only to
preview or check.
Then create AGENTS.md as a symlink to CLAUDE.md:
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
(hostname resolution and unique-host fallback pick the attr). List the
hosts in CLAUDE.md. Do not wait for the user to pick one. Never put a
secret, token, or password in this file.
{
"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.jsonalike..mcp.jsonis JSON and cannot carry HTML comments, so treat ANY existing.mcp.jsonas 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.mdare 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-flakeor a file you read. When a fact is missing, omit the line.