Mirrors the azos-core change: azos.claude.enable now sets azos.agent-memory.backends.claude.enable and azos.agent-skills.backends.claude.enable instead of the old azos.claude-memory.enable/azos.claude-skills.enable. The new agent-shell memory backend needs no wiring here -- it's bundled directly in azos-core's editor/emacs module, alongside wherever agent-shell itself gets installed.
11 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
├── suites/ # Suites, each grouping related sub-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 → inside an existing or new suite directory,
azos-core/suites/<suite>/<name>/default.nix(registeringconfig.flake.modules.homeManager.<suite>) — never a bare file directly underazos-core/suites/; every azos-core feature belongs to a suite. - 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,desktop) - 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.desktop— desktop session plumbing (flameshot, dunst, keyboard layout) plus generally-useful apps (browser, libreoffice, zoom, spotify, gimp, whatsapp). Shared, lives in azos-core — wanted on any GUI machine, work included.azos.suites.exwm— EXWM window manager, depends ondesktop
Machine-specific, personal-only apps (steam, discord) are not part of any azos-core
suite — they live as standalone features directly in azos/features/, imported only on
machines that want them (e.g. azos/features/discord, azos/features/steam), since any
such machine already uses the azos project rather than a different downstream repo.
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) or, for shared features,azos-core/suites/<suite>/<name>/default.nixinside an existing or new suite (registeringconfig.flake.modules.homeManager.<suite>, not a standalone module name) - Use the module template above
- Machine-specific: add
suiteModules.homeManager.<name>to the imports list inhome-manager/home.nix. Shared: no change needed if the suite is already imported. - 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 suites/<suite>/<name>/
Agent Memory/Skills Integration
Memory (org-roam) and skills (slash commands/CLAUDE.md) are backend-pluggable, not Claude-specific, even though Claude is currently the only skills backend and one of two memory backends:
| Feature | Option | What it does |
|---|---|---|
azos-core/suites/base/agent-memory |
azos.agent-memory.backends.claude.enable |
Registers org-roam-mcp as a global MCP server in ~/.claude.json; hooks resync org-roam's index on edit and inject project context at session start |
azos-core/suites/base/agent-memory |
azos.agent-memory.backends.agent-shell.enable |
Registers org-roam-mcp via agent-shell-mcp-servers and resyncs on file-write events — applies regardless of which ACP backend (Claude, Gemini CLI, Codex, ...) a given agent-shell session runs. Also marks Claude sessions agent-shell launches (AZOS_AGENT_SHELL_SESSION=1) so the claude backend's own hooks no-op for those sessions instead of double-acting |
azos-core/suites/base/agent-skills |
azos.agent-skills.backends.claude.enable |
Deploys skills to ~/.claude/commands/ and content to ~/.claude/CLAUDE.md |
azos/features/claude |
azos.claude.enable |
Installs claude-code, auto-enables the claude backend for both of the above |
azos-core/suites/editor/emacs |
(bundled, not a separate option) | Installs agent-shell, auto-enables azos.agent-memory.backends.agent-shell.enable |
Both agent-memory and agent-skills register config.flake.modules.homeManager.base
(they live inside the base suite, since that's where agent-shell is introduced) — not
standalone module names, so no separate suiteModules.homeManager.* import is needed for
them in home-manager/home.nix.
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.
'';
globalSkills/globalMdSections stay Claude-specific (~/.claude/commands/,
~/.claude/CLAUDE.md) deliberately — no other backend has a uniform equivalent yet. See
azos-core/suites/base/agent-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/suites/ |
Suites, each grouping related shared sub-features (auto-discovered by import-tree) |
features/ |
Machine-specific features (auto-discovered by import-tree) |
azos-core/suites/editor/emacs/config.org |
Literate Emacs config |
azos-core/suites/base/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)