Nix-based dotfiles for macOS (nix-darwin + home-manager). Primary configs: Neovim (Lua), Tmux, Zsh, CLI tools.
Platform: aarch64-darwin (Apple Silicon only). Flake-based with custom modular structure using lib.autoImport.
Run td usage --new-session at conversation start (or after /clear). This tells you what to work on next.
Sessions are automatic (based on terminal/agent context). Optional:
- td session "name" to label the current session
- td session --new to force a new session in the same context
Use td usage -q after first read.
Primary Commands:
darwin-rebuild switch --flake .- Apply system configurationnix develop- Enter dev shell (deadnix, statix, nixfmt-tree, just)
Testing & Validation:
nix flake check- Validate flake structurenix build .#darwinConfigurations.k.system- Test build without applyingnix eval .#darwinConfigurations.k.config.<path>- Evaluate specific config valuesnix flake show- Show all flake outputsnix build .#packages.aarch64-darwin.<package>- Build specific package
Code Quality:
nix fmt- Format all Nix files (nixfmt-tree)statix check .- Lint Nix codedeadnix .- Find unused Nix code
- Module pattern:
optionswithkriswill.<feature>.enable+configwithlib.mkIf. Seemodules/darwin/core/programs/nh/default.nixfor reference. - Imports: Use
inheritfor cleaner destructuring - Package lists: Use
builtins.attrValues { inherit (pkgs) ...; }pattern - Options:
lib.mkEnableOption,lib.mkProgramOption(custom),lib.mkDefault - Symlinks: Use
config.lib.file.mkOutOfStoreSymlink(see neovim module) - Unfree packages: Add to
allowUnfreePredicateinflake.nix
Formatter: stylua (configured in config/nvim/.stylua.toml)
indent_width = 2collapse_simple_statement = "FunctionOnly"sort_requires.enabled = true
Structure:
- Entry:
init.lua→require("config") - Core config:
lua/config/(options, keymaps, autocmds, LSP) - Plugins:
lua/plugins/(lazy.nvim plugin specs)
- Always start with:
set -euo pipefail - Use
trap 'cleanup_command' EXITfor temp resources - Colors: Define at top (
RED,GREEN,YELLOW,BLUE,NC) - Variables:
UPPER_CASEfor constants,lower_casefor locals - Create backups before modifying files
- Module options:
kriswill.<feature>.enable(e.g.,kriswill.neovim.enable) - Packages: kebab-case (e.g.,
kitten,iv,tofu-ls) - Nix functions: camelCase (e.g.,
mkDarwin,autoImport,mkProgramOption) - Files: kebab-case for multi-word (e.g.,
alias-en0.nix,update-opencode.sh) - Hosts: Descriptive names (e.g.,
k,SOC-Kris-Williams)
├── modules/
│ ├── darwin/ # nix-darwin system modules (uses lib.autoImport)
│ │ ├── core/ # Essential system configuration
│ │ └── mixins/ # Optional features (homebrew, aliases)
│ └── home-manager/ # User-level tool configs (neovim, tmux, zsh, git, etc.)
├── config/ # Actual config files (symlinked to XDG locations)
│ ├── nvim/ # Neovim Lua configuration
│ └── tmux/ # Tmux configuration
├── pkgs/ # Custom package definitions (*.nix files or subdirectories)
├── overlays/ # Nixpkgs overlays (makes custom packages available)
├── hosts/ # Host-specific configurations
├── lib/ # Custom library functions (autoImport, mkDarwin, etc.)
└── scripts/ # Helper scripts for package updates
Located in lib/default.nix: autoImport (directory-based module loading), mkDarwin, mkHomeManager, mkProgramOption. See file for API details.
Adding a New Module:
- Create file in
modules/darwin/ormodules/home-manager/ - Follow module pattern (see Code Style - Nix above)
- File is auto-discovered via
lib.autoImportin parentdefault.nix - Must
git addnew files beforenix build— flakes only see git-tracked files
Adding a Custom Package:
- Create
pkgs/<name>/package.nix - Add to
packages.${system}inflake.nix - Create overlay in
overlays/<name>.nix - If unfree: add to
allowUnfreePredicateinflake.nix
Symlinked Configs:
- Live config files in
config/directory - Symlink via
config.lib.file.mkOutOfStoreSymlinkin home-manager module - Allows editing without system rebuild