# AGENTS.md ## Build Commands ### Local Development ```bash nix run .#build # Build current host config nix run .#build -- # Build specific host (chidi, jason, michael, tahani) nix run .#apply # Build and apply locally (darwin-rebuild/nixos-rebuild switch) nix flake check # Validate flake ``` ### Remote Deployment (NixOS only) ```bash nix run .#deploy # Deploy to all NixOS hosts nix run .#deploy -- .#michael # Deploy to specific NixOS host nix run .#deploy -- .#tahani # Deploy to specific NixOS host ``` When you're on tahani and asked to apply, that means running `nix run .#deploy`. ### Formatting ```bash alejandra . # Format all Nix files ``` ## Code Style ### Formatter - **Tool**: Alejandra - **Config**: `alejandra.toml` specifies tabs for indentation - **Command**: Run `alejandra .` before committing ### File Structure - **Modules**: `modules/` - All configuration (flake-parts modules, auto-imported by import-tree) - `_lib/` - Utility functions (underscore = ignored by import-tree) - `_darwin/` - Darwin-specific sub-modules - `_neovim/` - Neovim plugin configs - `_opencode/` - OpenCode agent/command/skill configs - `_hosts/` - Host-specific sub-files (disk-config, hardware, etc.) - **Apps**: `apps/` - Per-system app scripts (Nushell) - **Secrets**: `secrets/` - SOPS-encrypted secrets (`.sops.yaml` for config) ### Architecture **Framework**: den (vic/den) — every .nix file in `modules/` is a flake-parts module **Pattern**: Feature/aspect-centric, not host-centric **Aspects**: `den.aspects..` where class is: - `nixos` - NixOS-only configuration - `darwin` - macOS-only configuration - `homeManager` - Home Manager configuration - `os` - Applies to both NixOS and darwin **Hosts**: `den.hosts..` defined in `modules/hosts.nix` **Defaults**: `den.default.*` defined in `modules/defaults.nix` **Imports**: Auto-imported by import-tree; underscore-prefixed dirs (`_lib/`, `_darwin/`, etc.) are excluded from auto-import **Deployment**: deploy-rs for NixOS hosts (michael, tahani); darwin hosts (chidi, jason) are local-only ### Nix Language Conventions **Function Arguments**: ```nix {inputs, pkgs, lib, ...}: ``` Destructure arguments on separate lines. Use `...` to capture remaining args. **Attribute Sets**: ```nix den.aspects.myfeature.os = { enable = true; config = "value"; }; ``` One attribute per line with trailing semicolons. **Lists with Packages**: ```nix with pkgs; [ age alejandra ast-grep ] ``` Use `with pkgs;` for package lists, one item per line. **Aspect Definition**: ```nix { config, lib, pkgs, ... }: with lib; let cfg = config.den.aspects.myfeature; in { options.den.aspects.myfeature = { enable = mkEnableOption "Feature description"; }; config = mkIf cfg.enable { # configuration }; } ``` - Destructure args on separate lines - Use `with lib;` for brevity with NixOS lib functions - Define `cfg` for config options - Use `mkIf`, `mkForce`, `mkDefault` appropriately **Conditional Platform-Specific Code**: ```nix ++ lib.optionals stdenv.isDarwin [ _1password-gui dockutil ] ++ lib.optionals stdenv.isLinux [ lm_sensors ] ``` ### Naming Conventions - **Aspect names**: `den.aspects..` for feature configuration - **Hostnames**: Lowercase, descriptive (e.g., `michael`, `tahani`, `chidi`, `jason`) - **Module files**: Descriptive, lowercase with hyphens (e.g., `neovim-config.nix`) ### Secrets Management - Use SOPS for secrets (see `.sops.yaml`) - Never commit unencrypted secrets - Secret definitions live in per-host modules (`modules/michael.nix`, `modules/tahani.nix`, etc.) - Shared SOPS defaults (module imports, key paths) in `modules/secrets.nix` ### Aspect Composition Use `den.aspects..includes` to compose aspects: ```nix den.aspects.myfeature.includes = [ "other-aspect" "another-aspect" ]; ``` ### Key Conventions - No `specialArgs` — den batteries handle input passing - No hostname string comparisons in shared aspects - Host-specific config goes in `den.aspects..*` - Shared config uses `os` class (applies to both NixOS and darwin) - Non-module files go in `_`-prefixed subdirs