This skill should be used when the user asks to "write nix", "nix expression", "flake.nix", "home-manager config", "programs.*", "services.*", or works with Nix language, flakes, or Home Manager. Provides comprehensive Nix ecosystem patterns and best practices.

52 stars
0 forks
6 views

SKILL.md


name: Nix Ecosystem description: This skill should be used when the user asks to "write nix", "nix expression", "flake.nix", "home-manager config", "programs.", "services.", or works with Nix language, flakes, or Home Manager. Provides comprehensive Nix ecosystem patterns and best practices.

Provide comprehensive patterns for Nix language, flakes, and Home Manager configuration.

<nix_language> Nix is lazily evaluated. Expressions are only computed when needed. Only evaluates needed attributes let expensive = builtins.trace "Computing expensive" (1 + 1); in { a = 1; b = expensive; }.a # Does not compute expensive

<concept name="pure_functions">
  <description>All Nix functions are pure. Same inputs always produce same outputs.</description>
  <example>
    <note>Pure function - always returns same result for same input</note>
    double = x: x * 2;

    <note>Avoid side effects; use derivations for build actions</note>
    buildResult = pkgs.stdenv.mkDerivation { ... };
  </example>
</concept>

<concept name="attribute_sets">
  <description>Primary data structure in Nix</description>
  <example>
    <note>Basic attribute set</note>
    { attr1 = value1; attr2 = value2; }

    <note>Access patterns</note>
    set.attr
    set."attr-with-dashes"

    <note>Recursive attribute set</note>
    rec { a = 1; b = a + 1; }
  </example>
</concept>
Local bindings for complex expressions let helper = x: x + 1; value = helper 5; in value * 2
<pattern name="with">
  <description>Bring attribute set into scope</description>
  <example>
    with pkgs; [ git vim tmux ]
  </example>
  <warning>Avoid nested with; prefer explicit references for clarity</warning>
</pattern>

<pattern name="inherit">
  <description>Copy attributes from another set</description>
  <example>
    { inherit (pkgs) git vim; inherit name version; }
  </example>
</pattern>

<pattern name="overlay">
  <description>Modify or extend nixpkgs</description>
  <example>
    final: prev: {
    myPackage = prev.myPackage.override { ... };
    }
  </example>
  <decision_tree name="when_to_use">
    <question>Do you need to modify existing packages or add new ones globally?</question>
    <if_yes>Use overlays to extend nixpkgs</if_yes>
    <if_no>Use local package definitions with callPackage</if_no>
  </decision_tree>
</pattern>

<pattern name="callPackage">
  <description>Dependency injection pattern</description>
  <example>
    myPackage = pkgs.callPackage ./package.nix { };
  </example>
</pattern>

<pattern name="mkDerivation">
  <description>Standard package builder</description>
  <example>
    pkgs.stdenv.mkDerivation {
    pname = "mypackage";
    version = "1.0.0";
    src = fetchFromGitHub { ... };

    nativeBuildInputs = [ pkgs.cmake ];
    buildInputs = [ pkgs.openssl ];

    installPhase = ''
    mkdir -p $out/bin
    cp mypackage $out/bin/
    '';
    }
  </example>
  <note>Required attributes: pname, version, src</note>
  <note>Standard phases: unpackPhase, patchPhase, configurePhase, buildPhase, installPhase</note>
</pattern>

<pattern name="build_inputs">
  <description>Dependency specification in derivations</description>
  <example>
    {
    # Tools run at build time (compilers, build tools)
    nativeBuildInputs = [ cmake pkg-config ];

    # Libraries linked at runtime
    buildInputs = [ openssl zlib ];
    }
  </example>
</pattern>

<pattern name="options_config">
  <description>NixOS/Home Manager module structure</description>
  <example>
    { config, lib, pkgs, ... }:
    {
    options.myModule = {
    enable = lib.mkEnableOption "my module";
    setting = lib.mkOption {
    type = lib.types.str;
    default = "value";
    description = "A setting";
    };
    };

    config = lib.mkIf config.myModule.enable { # configuration when enabled
    };
    }
  </example>
</pattern>

<pattern name="mkOption">
  <description>Define module options with types and defaults</description>
  <example>
    options.myOption = lib.mkOption {
    type = lib.types.bool;
    default = false;
    description = "Enable my feature";
    example = true;
    };
  </example>
  <note>Common types: lib.types.bool, lib.types.str, lib.types.listOf, lib.types.attrsOf</note>
</pattern>

<pattern name="mkEnableOption">
  <description>Shorthand for boolean enable option</description>
  <example>
    enable = lib.mkEnableOption "my service";
  </example>
</pattern>

<anti_patterns> Directly referencing absolute paths breaks reproducibility Use fetchurl, fetchFromGitHub, or relative paths within the repository

<avoid name="nested_with">
  <description>Multiple nested with statements reduce code clarity</description>
  <instead>Prefer explicit attribute access (pkgs.git) for better readability</instead>
</avoid>

<avoid name="rec_overuse">
  <description>Recursive attribute sets can be hard to understand and maintain</description>
  <instead>Use let-in for complex recursive definitions</instead>
</avoid>

<avoid name="string_interpolation_abuse">
  <description>Using string interpolation for path operations is error-prone</description>
  <instead>Use lib functions for path manipulation (lib.concatStringsSep, builtins.path)</instead>
</avoid>

</anti_patterns> </nix_language>

Basic structure of a flake.nix file { description = "Project description";
  inputs = {
  nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  };

  outputs = { self, nixpkgs, ... }@inputs: { # output attributes
  };
  }
</example>
Derivations for nix build packages.x86_64-linux.default = pkgs.hello;
<concept name="devShells">
  <description>Development environments for nix develop</description>
  <example>
    devShells.x86_64-linux.default = pkgs.mkShell {
    packages = [ pkgs.nodejs ];
    };
  </example>
</concept>

<concept name="apps">
  <description>Runnable applications for nix run</description>
  <example>
    apps.x86_64-linux.default = {
    type = "app";
    program = "${pkgs.hello}/bin/hello";
    };
  </example>
</concept>

<concept name="overlays">
  <description>Nixpkgs overlays</description>
  <example>
    overlays.default = final: prev: {
    myPackage = prev.callPackage ./myPackage.nix { };
    };
  </example>
</concept>

<concept name="nixosModules">
  <description>NixOS modules</description>
  <example>
    nixosModules.default = { config, lib, pkgs, ... }: {
    options.services.myService = { ... };
    config = { ... };
    };
  </example>
</concept>

<concept name="homeManagerModules">
  <description>Home Manager modules</description>
  <example>
    homeManagerModules.default = { config, lib, pkgs, ... }: {
    options.programs.myProgram = { ... };
    config = { ... };
    };
  </example>
</concept>

<concept name="nixosConfigurations">
  <description>Full NixOS system configurations</description>
  <example>
    nixosConfigurations.hostname = nixpkgs.lib.nixosSystem {
    system = "x86_64-linux";
    modules = [ ./configuration.nix ];
    };
  </example>
</concept>

<concept name="homeConfigurations">
  <description>Home Manager configurations</description>
  <example>
    homeConfigurations."user@host" = home-manager.lib.homeManagerConfiguration {
    pkgs = nixpkgs.legacyPackages.x86_64-linux;
    modules = [ ./home.nix ];
    };
  </example>
</concept>
Reference GitHub repositories as flake inputs inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; # Specific branch stable.url = "github:NixOS/nixpkgs/nixos-24.11"; # Specific revision pinned.url = "github:owner/repo/abc123def"; };
<pattern name="follows">
  <description>Share input between flakes to avoid duplication</description>
  <example>
    home-manager = {
    url = "github:nix-community/home-manager";
    inputs.nixpkgs.follows = "nixpkgs";
    };
  </example>
</pattern>

<pattern name="flake_false">
  <description>Non-flake inputs for legacy repositories</description>
  <example>
    my-source = {
    url = "github:owner/repo";
    flake = false;
    };
  </example>
</pattern>

<pattern name="per_system">
  <description>Generate outputs for multiple systems</description>
  <example>
    outputs = { self, nixpkgs, ... }:
    let
    systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ];
    forAllSystems = nixpkgs.lib.genAttrs systems;
    in {
    packages = forAllSystems (system:
    let pkgs = nixpkgs.legacyPackages.${system};
    in { default = pkgs.hello; }
    );
    };
  </example>
  <decision_tree name="when_to_use">
    <question>Does your flake need to support multiple platforms?</question>
    <if_yes>Use per-system pattern with genAttrs or flake-utils</if_yes>
    <if_no>Define outputs for single system directly</if_no>
  </decision_tree>
</pattern>

<pattern name="devShell">
  <description>Development environment with packages and hooks</description>
  <example>
    devShells.default = pkgs.mkShell {
    packages = with pkgs; [ nodejs yarn ];
    shellHook = ''
    echo "Development environment ready"
    '';
    };
  </example>
</pattern>
Update all inputs to their latest versions Regular dependency updates
<tool name="nix flake update input-name">
  <description>Update a specific input</description>
  <use_case>Selective updates to test compatibility</use_case>
</tool>

<tool name="nix flake show">
  <description>Display all flake outputs</description>
  <use_case>Exploring available packages and configurations</use_case>
</tool>

<tool name="nix flake check">
  <description>Validate flake and run checks</description>
  <use_case>CI/CD validation, pre-commit checks</use_case>
</tool>

<home_manager> Standard Home Manager module structure { config, pkgs, lib, ... }: { options.custom.feature = { enable = lib.mkEnableOption "feature description"; };

  config = lib.mkIf config.custom.feature.enable { # configuration when enabled
  };
  }
</example>
Organize modules by program name home-manager/programs/git.nix home-manager/programs/neovim.nix home-manager/programs/tmux.nix
<pattern name="by_category">
  <description>Organize modules by category</description>
  <example>
    home-manager/development/default.nix
    home-manager/shell/default.nix
    home-manager/editors/default.nix
  </example>
</pattern>

<pattern name="imports">
  <description>Import multiple modules</description>
  <example>
    imports = [
    ./programs/git.nix
    ./programs/neovim.nix
    ./shell/fish.nix
    ];
  </example>
</pattern>

<pattern name="basic_enable">
  <description>Enable a program with defaults</description>
  <example>
    programs.git.enable = true;
  </example>
</pattern>

<pattern name="with_options">
  <description>Enable and configure a program</description>
  <example>
    programs.git = {
    enable = true;
    userName = "name";
    userEmail = "email";
    extraConfig = {
    core.editor = "nvim";
    init.defaultBranch = "main";
    };
    };
  </example>
  <decision_tree name="when_to_use">
    <question>Does Home Manager provide built-in module for this program?</question>
    <if_yes>Use programs.* with configuration options</if_yes>
    <if_no>Use home.file or xdg.configFile for manual configuration</if_no>
  </decision_tree>
</pattern>

<pattern name="package_override">
  <description>Use alternative package version</description>
  <example>
    programs.git = {
    enable = true;
    package = pkgs.gitFull;
    };
  </example>
</pattern>

<pattern name="home.file">
  <description>Manage dotfiles directly</description>
  <example>
    home.file.".config/app/config" = {
    source = ./config;
    # or
    text = ''
    key = value
    '';
    };
  </example>
</pattern>

<pattern name="xdg.configFile">
  <description>XDG config directory files</description>
  <example>
    xdg.configFile."app/config".source = ./config;
  </example>
</pattern>

<pattern name="home.sessionVariables">
  <description>Environment variables for login shells</description>
  <example>
    home.sessionVariables = {
    EDITOR = "nvim";
    PAGER = "less";
    };
  </example>
</pattern>

<pattern name="home.sessionPath">
  <description>Add directories to PATH</description>
  <example>
    home.sessionPath = [ "$HOME/.local/bin" ];
  </example>
</pattern>

<common_modules> Git version control configuration programs.git = { enable = true; userName = "Your Name"; userEmail = "[email protected]"; signing = { key = "KEY_ID"; signByDefault = true; }; aliases = { co = "checkout"; st = "status"; }; extraConfig = { core.editor = "nvim"; }; };

<concept name="programs.neovim">
  <description>Neovim editor configuration</description>
  <example>
    programs.neovim = {
    enable = true;
    viAlias = true;
    vimAlias = true;
    plugins = with pkgs.vimPlugins; [
    vim-commentary
    vim-surround
    ];
    extraConfig = ''
    set number
    set relativenumber
    '';
    extraLuaConfig = ''
    vim.opt.expandtab = true
    '';
    };
  </example>
</concept>

<concept name="programs.fish">
  <description>Fish shell configuration</description>
  <example>
    programs.fish = {
    enable = true;
    shellInit = ''
    set -g fish_greeting
    '';
    shellAliases = {
    ll = "ls -lah";
    };
    functions = {
    gitignore = "curl -sL https://www.gitignore.io/api/$argv";
    };
    plugins = [
    { name = "z"; src = pkgs.fishPlugins.z.src; }
    ];
    };
  </example>
</concept>

<concept name="programs.tmux">
  <description>Terminal multiplexer configuration</description>
  <example>
    programs.tmux = {
    enable = true;
    terminal = "screen-256color";
    keyMode = "vi";
    plugins = with pkgs.tmuxPlugins; [
    sensible
    yank
    ];
    extraConfig = ''
    set -g mouse on
    '';
    };
  </example>
</concept>

<concept name="programs.direnv">
  <description>Directory-specific environment loader</description>
  <example>
    programs.direnv = {
    enable = true;
    nix-direnv.enable = true;
    enableBashIntegration = true;
    enableZshIntegration = true;
    };
  </example>
</concept>

</common_modules>

<best_practices> Use programs.* when available instead of manual configuration

<practice priority="critical">
  Set home.stateVersion to your initial HM version and do not change after initial setup unless migrating
</practice>

<practice priority="high">
  Group related configurations in separate modules for maintainability
</practice>

<practice priority="high">
  Use lib.mkIf for conditional configuration
</practice>

<practice priority="medium">
  Prefer xdg.configFile over home.file for XDG-compliant apps
</practice>

<practice priority="medium">
  Use home.packages for additional packages not configured via programs.*
</practice>

</best_practices>

Track Home Manager state version for compatibility home.stateVersion = "24.11"; # Current stable. 25.05 (upcoming). Do not change after initial setup unless migrating HM 24.11+ (25.05 upcoming) supports minimal mode for faster evaluation imports = [ "${modulesPath}/programs/fzf.nix" ]; Advanced users optimizing evaluation time Understand Nix expression requirements 1. Identify target: flake, module, derivation, or expression 2. Check existing patterns in project 3. Consult Serena memories for conventions Write idiomatic Nix code 1. Follow patterns from decision trees 2. Use appropriate lib functions 3. Apply best practices for target type Verify Nix expression correctness 1. Check syntax with nix flake check 2. Verify evaluation with nix eval 3. Test build with nix build

<error_escalation> Style inconsistency in Nix expression Note issue, suggest formatting Evaluation error or type mismatch Debug with --show-trace, fix expression Build failure in derivation Analyze build log, present options to user Impure expression breaking reproducibility Block operation, require pure alternatives </error_escalation>

Use lib functions for complex operations Follow project's existing Nix patterns Maintain reproducibility in all expressions Impure paths and absolute references Nested with statements Overusing rec for attribute sets

<related_agents> Architecture and module dependency analysis for Nix configurations Implementation of flake outputs, Home Manager modules, and NixOS configurations Nix expression validation, formatting, and best practices enforcement </related_agents>

<related_skills> Symbol operations for navigating Nix expressions and module definitions Fetch latest nixpkgs and Home Manager documentation Debug evaluation errors and understand derivation failures </related_skills>

Basic NixOS configuration with Home Manager nixosConfigurations.hostname = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ ./configuration.nix home-manager.nixosModules.home-manager ]; specialArgs = { inherit inputs; }; };
<pattern name="standalone_home_manager">
  <description>Standalone Home Manager without NixOS</description>
  <example>
    homeConfigurations."user@host" = home-manager.lib.homeManagerConfiguration {
    pkgs = nixpkgs.legacyPackages.x86_64-linux;
    modules = [ ./home.nix ];
    extraSpecialArgs = { inherit inputs; };
    };
  </example>
</pattern>

<pattern name="as_nixos_module">
  <description>Home Manager as a NixOS module</description>
  <example>
    {
    imports = [ home-manager.nixosModules.home-manager ];

    home-manager.useGlobalPkgs = true;
    home-manager.useUserPackages = true;
    home-manager.users.username = import ./home.nix;
    }
  </example>
</pattern>