Nix DevShell Init
Scaffold a reproducible flake.nix and .envrc for a project based on
detected languages and tooling needs.
What this skill does
- Detects project language(s) from sentinel files in the working directory.
- Selects the appropriate devShell packages for each language.
- Generates a
flake.nixwith adevShells.defaultoutput. - Creates an
.envrcwithuse flakefor direnv integration. - Adds
.direnv/to.gitignore(direnv cache directory). - Verifies the generated flake evaluates cleanly.
Language detection
Scan the project root for these sentinel files. Multiple matches produce a combined multi-language devShell.
Sentinel file(s) → Language → Primary packages
─────────────────────────────────────────────────────────
go.mod → Go → go, gopls, gotools
package.json → Node.js → nodejs, npm
pyproject.toml → Python → python3, uv
requirements.txt → Python → python3, uv
Cargo.toml → Rust → rustc, cargo, rust-analyzer
Makefile (alone) → Generic → gnumake
If no sentinel files are found, ask the user which language(s) to support and proceed with that selection.
Version inference
When possible, infer the language version from project metadata:
- Go: parse
godirective fromgo.mod(e.g.,go 1.22→go_1_22if available in nixpkgs) - Node: parse
engines.nodefrompackage.json(e.g.,>=18→nodejs_18) - Python: parse
requires-pythonfrompyproject.toml(e.g.,>=3.11→python311) - Rust: if
rust-toolchain.tomlexists, note the channel but use the nixpkgs Rust (pinning Rust nightly requires an overlay — suggestoxalica/rust-overlayin a comment)
When the exact version is unavailable in nixpkgs, default to the latest stable and add a comment noting the requested version.
Flake generation
Generate flake.nix at the project root. The structure follows:
{
description = "<project-name> development environment";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = { nixpkgs, ... }:
let
# Support both x86_64 and aarch64 for Linux and Darwin
forAllSystems = nixpkgs.lib.genAttrs [
"x86_64-linux" "aarch64-linux"
"x86_64-darwin" "aarch64-darwin"
];
in
{
devShells = forAllSystems (system:
let pkgs = nixpkgs.legacyPackages.${system}; in
{
default = pkgs.mkShell {
packages = with pkgs; [
# <detected packages here>
];
shellHook = ''
# <language-specific shell hooks>
'';
};
}
);
};
}
For per-language package lists and shellHook contents, see
references/language-templates.md.
Multi-platform support
Always use forAllSystems (or flake-utils.lib.eachDefaultSystem)
rather than hardcoding a single system string. This ensures the flake
works on both Linux and macOS, x86_64 and aarch64. If the project is
exclusively Linux, add a comment noting the restriction rather than
removing other systems.
.envrc generation
Create .envrc at the project root:
use flake
If .envrc already exists, append use flake only if it is not
already present.
.gitignore updates
Add these entries to .gitignore if not already present:
# Nix / direnv
.direnv/
result
Do not add flake.lock to .gitignore — the lock file pins
package versions and must be committed for reproducibility.
Verification
After generating the flake, verify it evaluates:
nix flake check --no-build
If nix flake check fails, read the error and fix the flake.nix.
Common issues:
- Missing attribute — a package name was wrong for nixpkgs.
Search with
nix search nixpkgs <name>. - Infinite recursion — usually a
letbinding that references itself. Check for circular definitions. - IFD (import-from-derivation) — avoid in devShells; use
pkgs.mkShellwith static package lists only.
If direnv is installed, run:
direnv allow
Existing flake handling
If flake.nix already exists in the project:
- Read it and report what it currently provides.
- Ask the user whether to replace, extend, or leave it unchanged.
- Never overwrite an existing flake.nix without confirmation.
If flake.lock exists without flake.nix, the lock file is orphaned.
Note this to the user and proceed with generation.
Error handling
- Nix not installed — stop and suggest the
nix-installskill. - Flakes not enabled — stop and suggest enabling flakes (see
nix-installreferences for the config command). - No sentinel files and no user input — create a minimal generic
shell with
gnumake,curl,jqand inform the user. - flake check fails — do not leave a broken flake. Fix or remove it before finishing. Report what failed and why.
References
references/language-templates.md— per-language package lists, shellHook contents, and common extras (LSPs, formatters, linters).references/flake-anatomy.md— annotated flake.nix structure explaining each section for users new to Nix.