8.5 KiB
AGENTS.md - Guidelines for Agentic Coding in Azos
This is a NixOS/Nix flake-based configuration repository using home-manager for dotfiles and system configuration.
Build Commands
Evaluating
nix eval .#nixosConfigurations.lauretta.config.system.build.toplevel
nix flake show
Building
# VM (with submodules for azos-core)
nix build '.?submodules=1#nixosConfigurations.vm.config.system.build.vm'
# Formatter
nix build .#formatter.x86_64-linux
Deploying
# Rebuild the azos environment for lauretta (this machine) — use this when asked to "rebuild"
azos-lauretta-update
nix flake update --flake '.?submodules=1' # Update all inputs
nix flake lock --flake '.?submodules=1' --update-input X # Update specific input
Note
: When the user asks to "rebuild", they mean rebuilding the lauretta environment with the command above — NOT building the VM.
Dev Shell & REPL
nix develop .#shells.x86_64-linux.default
nix repl
:lf .
Ad-hoc Commands
If an executable doesn't exist on the system, run it ad-hoc with:
nix shell nixpkgs#<package> --command <cmd> <args>
Linting/Formatting
Uses alejandra:
nix fmt # Format all Nix files
alejandra .
Testing
Manual VM testing:
nix build '.?submodules=1#nixosConfigurations.vm.config.system.build.vm'
./result/bin/run-nixos-vm
Emacs Debugging
When adding or debugging Emacs functionality, you can interact with the running Emacs session via emacsclient (with user permission):
# Create and switch to a buffer
emacsclient -c -e '(switch-to-buffer (get-buffer-create "test"))'
# Evaluate arbitrary Elisp
emacsclient -e '(message "hello")'
Repository Structure
azos/
├── flake.nix # Main flake
├── home-manager/home.nix # Home-manager entry point; manually imports all modules
├── _machines/ # Per-machine NixOS configs (pass suiteModules as specialArgs)
├── nixos/ # NixOS system configs (configuration.nix, configuration-vm.nix, etc.)
├── features/ # Machine-specific home-manager features (auto-discovered)
├── overlays/ # Package overlays
├── shells/ # Dev shells
└── azos-core/ # Shared feature library (git submodule)
├── flake.nix
├── features/ # Shared/reusable features (auto-discovered)
├── overlays/ # Core overlays
└── _lib/ # Module schema helpers
Features are auto-discovered by import-tree — no central imports file. Each feature's
default.nix registers itself via config.flake.modules.homeManager.<name>. Modules are
then explicitly imported in home-manager/home.nix via suiteModules.homeManager.<name>.
Where to put a new feature:
- Shared / reusable across machines →
azos-core/features/<name>/default.nix - Machine-specific →
azos/features/<name>/default.nix
Code Style Guidelines
Nix Module Template
All features follow this flake-parts registration pattern:
{...}: {
config.flake.modules.homeManager.<name> = {
lib,
config,
pkgs,
...
}: {
options.azos.<name>.enable = lib.mkOption {
default = false;
example = true;
type = lib.types.bool;
};
config = lib.mkIf config.azos.<name>.enable {
home.packages = with pkgs; [pkg1 pkg2];
};
};
}
Naming Conventions
- Option paths:
azos.<module-name>.<option> - Module names: kebab-case (
git-config,claude-memory) - Feature directories: kebab-case, one
default.nixper feature - Variables: kebab-case
Formatting
- Indentation: 2 spaces
- Packages:
with pkgs; [pkg1 pkg2](no trailing semicolon inside list)
Imports
- Always include
{...}for the outer module args - Inner module args:
lib, config, pkgs, ...
Option Patterns
- Boolean enable:
lib.mkOption { default = false; example = true; type = lib.types.bool; } - Conditional config:
lib.mkIf config.azos.<name>.enable { ... } - Auto-enable a dependency:
azos.<dep>.enable = lib.mkDefault true; - Force value:
lib.mkForce(sparingly)
Extensible Options
For options that multiple modules should be able to contribute to, use attrset options:
options.azos.<name>.things = lib.mkOption {
default = {};
type = lib.types.attrsOf lib.types.path; # or lines, str, etc.
};
Any module can then add entries without modifying the owning feature. Examples in use:
azos.claude.globalSkills— attrset of skill name → markdown file pathazos.claude.globalMdSections— attrset of key → markdown string (merged into~/.claude/CLAUDE.md)
Home-Manager vs NixOS
- Home-manager:
home.packages,home.file,programs.<program> - NixOS:
config.services,environment.systemPackages
Deploying Files
- Static file:
home.file."dest".source = ./file; - Generated text:
home.file."dest".text = "..."; - Runtime script (e.g. merging JSON at activation):
home.activation.<name> = lib.hm.dag.entryAfter ["writeBoundary"] ''...'';
Suites System
Top-level suites group related features (defined in azos-core/):
azos.suites.base— base packages and configazos.suites.editor— editor toolsazos.suites.dev— development toolsazos.suites.station— desktop applicationsazos.suites.exwm— EXWM window manager
The machine suite (azos.suites.lauretta) enables the relevant suites for this machine.
Specializations
NixOS supports specializations for alternative configurations:
specialisation = {
hyprland = {
configuration = {
home-manager.users.aner = {pkgs, ...}: {
azos.suites.exwm.enable = lib.mkForce false;
azos.suites.hyprland.enable = true;
};
};
};
};
Adding New Modules
- Create
features/<name>/default.nix(machine-specific) orazos-core/features/<name>/default.nix(shared) - Use the module template above
- Add
suiteModules.homeManager.<name>to the imports list inhome-manager/home.nix - Run
nix fmt
Submodule gotcha: New files in
azos-core/must begit add-ed inside the submodule beforenix buildwill see them — Nix flakes only evaluate git-tracked files.cd azos-core && git add features/<name>/
Claude Integration
Claude Code is integrated with this environment via several azos-core features:
| Feature | Option | What it does |
|---|---|---|
azos-core/features/claude-memory |
azos.claude-memory.enable |
Registers org-roam-mcp as a global MCP server in ~/.claude.json |
azos-core/features/claude-skills |
azos.claude-skills.enable |
Deploys skills to ~/.claude/commands/ and content to ~/.claude/CLAUDE.md |
azos/features/claude |
azos.claude.enable |
Installs claude-code, auto-enables the above two |
Adding Claude skills or standing instructions
From any module — no need to touch azos-core:
azos.claude.globalSkills.my-skill = ./skills/my-skill.md;
azos.claude.globalMdSections.my-rule = ''
# My Rule
Always do X when Y.
'';
See azos-core/features/claude-skills/README.md for full documentation.
Key Files
| File | Purpose |
|---|---|
flake.nix |
Main flake, defines systems and overlays |
home-manager/home.nix |
Home-manager entry point, imports all modules |
_machines/lauretta.nix |
Lauretta machine config, passes suiteModules as specialArgs |
nixos/configuration.nix |
Lauretta NixOS system config |
nixos/configuration-vm.nix |
Test VM config |
overlays/ |
Package overlays |
azos-core/ |
Shared feature library (git submodule) |
azos-core/features/ |
Shared features (auto-discovered by import-tree) |
features/ |
Machine-specific features (auto-discovered by import-tree) |
azos-core/features/editor/emacs/config.org |
Literate Emacs config |
azos-core/features/claude-skills/README.md |
Claude skills extensibility docs |
Common Tasks
Add system package: Edit a NixOS feature, add to environment.systemPackages = with pkgs; [ pkg ]
Add home-manager package: Edit a feature's config block, add to home.packages = with pkgs; [ pkg ]
Add system service: Edit a NixOS feature, add under services.<service>
Add custom package: Add to azos-core/overlays/ via config.flake.overlayPkgs.<name>, then use as pkgs.<name>
Access unstable packages: Use pkgs.unstable.<package> (via unstable-packages overlay)