16 KiB
Agent Instructions
This project uses bd (beads) for issue tracking. Run bd prime for full workflow context.
Quick Reference
bd ready # Find available work
bd show <id> # View issue details
bd update <id> --claim # Claim work atomically
bd close <id> # Complete work
bd dolt push # Push beads data to remote
Non-Interactive Shell Commands
ALWAYS use non-interactive flags with file operations to avoid hanging on confirmation prompts.
Shell commands like cp, mv, and rm may be aliased to include -i (interactive) mode on some systems, causing the agent to hang indefinitely waiting for y/n input.
Use these forms instead:
# Force overwrite without prompting
cp -f source dest # NOT: cp source dest
mv -f source dest # NOT: mv source dest
rm -f file # NOT: rm file
# For recursive operations
rm -rf directory # NOT: rm -r directory
cp -rf source dest # NOT: cp -r source dest
Other commands that may prompt:
scp- use-o BatchMode=yesfor non-interactivessh- use-o BatchMode=yesto fail instead of promptingapt-get- use-yflagbrew- useHOMEBREW_NO_AUTO_UPDATE=1env var
Beads Issue Tracker
This project uses bd (beads) for persistent task tracking. Run bd prime for full workflow context.
Why Beads?
- Prefer Beads over ad-hoc markdown TODO lists — Beads provides structured, queryable, shareable issue tracking with dependency management
- Never use
bd edit— it opens an interactive editor which blocks agent workflows - Use flags and stdin instead —
bd update <id> --claim,bd create --title "..." --estimate 2
Slash Commands (Agent Workflow)
| Command | Purpose |
|---|---|
/beads:ready |
Find unblocked issues |
/beads:create |
Create a new issue |
/beads:update |
Update an issue (claim, status) |
/beads:close |
Close completed work |
/beads:stats |
Project-level snapshot |
Core Workflow (6 Steps)
1. Find Unblocked Work
bd ready --json
Lists issues with no blocking dependencies that are ready to work on.
2. Claim Work
bd update <id> --claim
Atomically assigns the issue to you (sets status to "in-progress").
3. Inspect Details
bd show <id>
View full issue details including:
- Description and acceptance criteria
- Blocking/blocked-by dependencies
- Time estimates
- Status history
4. Create Newly Discovered Work
# Create a new issue
bd create \
--title "Fix audio on m3-helios" \
--estimate 2 \
--priority high \
--labels nixos,audio
# Link dependencies
bd dep <id> --blocks <blocked-id> # This issue blocks another
bd dep <id> --after <after-id> # This issue after another completes
bd dep <id> --requires <requires-id> # This issue requires another
5. Complete Work
bd close <id> --reason "Added PulseAudio fallback to configuration.nix"
Provide a concise summary of what was done. The --reason is mandatory.
6. Project Snapshot
bd status --json # Current state of all issues
bd stats # Metrics: velocity, cycle time, bottlenecks
Example Complete Workflow
# Start session - find work
bd ready --json
# Claim available issue
bd update 42 --claim
# Do the work...
# Discover something else needed
bd create --title "Document hermes-agent setup" --estimate 1
# Link as related
bd dep 43 --after 42
# Complete original
bd close 42 --reason "Added Hyprland idle timeout config"
# Close related
bd close 43 --reason "Added setup docs to AGENTS.md"
# Push state to remote
bd dolt push
Rules
- Use
bdfor ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists - Run
bd primefor detailed command reference and session close protocol - Use
bd rememberfor persistent knowledge — do NOT use MEMORY.md files
Session Completion
When ending a work session, you MUST complete ALL steps below. Work is NOT complete until git push succeeds.
MANDATORY WORKFLOW:
- File issues for remaining work - Create issues for anything that needs follow-up
- Run quality gates (if code changed) - Tests, linters, builds
- Update issue status - Close finished work, update in-progress items
- PUSH TO REMOTE - This is MANDATORY:
git pull --rebase bd dolt push git push git status # MUST show "up to date with origin" - Clean up - Clear stashes, prune remote branches
- Verify - All changes committed AND pushed
- Hand off - Provide context for next session
CRITICAL RULES:
- Work is NOT complete until
git pushsucceeds - NEVER stop before pushing - that leaves work stranded locally
- NEVER say "ready to push when you are" - YOU must push
- If push fails, resolve and retry until it succeeds
Project Agent
Workspace Path: /home/m3tam3re/p/NIX/nixos-config
(Note to Pi: Your file write/edit tools run in a different directory by default. You MUST use absolute paths starting with the Workspace Path above for ALL file operations!)
Generated: 2026-04-26
Stack
| Component | Version/Source |
|---|---|
| Nixpkgs | nixos-unstable + 25.05 stable |
| Home Manager | github:nix-community/home-manager |
| m3ta-home | code.m3ta.dev/m3tam3re/m3ta-home |
| m3ta-nixpkgs | code.m3ta.dev/m3tam3re/nixpkgs |
| Agenix | github:ryantm/agenix |
| Disko | github:nix-community/disko |
| NUR | github:nix-community/NUR |
| Formatter | alejandra |
| Linters | statix, deadnix |
| IDE | nixd |
| Hermes Agent | NousResearch/hermes-agent |
| LLM Agents | numtide/llm-agents.nix |
Structure
nixos-config/
├── flake.nix # Entry point: hosts, overlays, dev shells, m3ta-home input
├── coding-rules.json # Opencode rules configuration
│
├── hosts/ # Per-host NixOS configurations
│ ├── common/ # Shared across all hosts
│ │ ├── users/
│ │ │ └── m3tam3re.nix # ← Central user + m3ta-home integration
│ │ ├── default.nix # Shared NixOS settings, overlays, home-manager setup
│ │ ├── ports.nix # Network ports config
│ │ └── extraServices/ # Common service toggles
│ ├── m3-ares/ # TUXEDO laptop (desktop)
│ │ └── home.nix # Hyprland: eDP-1 + HDMI, XDG/MIME
│ ├── m3-kratos/ # AMD desktop (desktop)
│ │ └── home.nix # Hyprland: dual DP, XDG/MIME
│ ├── m3-daedalus/ # Portable laptop (desktop, no Hyprland)
│ │ └── home.nix # XDG/MIME only
│ ├── m3-atlas/ # Primary server (server + coding)
│ ├── m3-helios/ # AdGuard DNS server (minimal server)
│ ├── m3-hermes/ # Secondary server (minimal server)
│ └── m3-aether/ # Cloud VM (minimal server)
│
├── modules/ # Reusable NixOS modules
│ └── nixos/ # NixOS-specific modules
│
├── overlays/ # Package overlays (stable/locked/master/pinned)
│ ├── default.nix
│ └── mods/
│
├── pkgs/ # Custom packages
│
├── secrets/ # Encrypted secrets (agenix)
│ └── secrets.nix
│
├── .opencode-rules/ # Opencode AI rules
│ ├── concerns/
│ ├── languages/nix.md
│ └── USAGE.md
│
└── .pi/ # Agent configuration
Home-Manager Integration
Home-Manager configs are managed centrally in the m3ta-home repository:
- Repo:
code.m3ta.dev/m3tam3re/m3ta-home - Docs: See m3ta-home README for full documentation
What lives where:
| Concern | Location | Why |
|---|---|---|
| Shell, CLI tools, editors, apps | m3ta-home/profiles/ |
Portable across all hosts |
| User identity (git, SSH, JJ) | m3ta-home/users/ |
Switchable: private vs work |
| Feature flags (enable/disable) | nixos-config/hosts/common/users/m3tam3re.nix |
Per-host decisions |
| Monitor layouts, window rules | nixos-config/hosts/<name>/home.nix |
Hardware-specific |
| XDG/MIME defaults | nixos-config/hosts/<name>/home.nix |
Host-specific preferences |
| NixOS overlays | nixos-config/overlays/ |
System-level package management |
Host → Profile Mapping
Defined in hosts/common/users/m3tam3re.nix:
hostProfiles = {
# Desktop hosts
m3-ares = { context = "desktop"; sets = ["coding" "gaming" "media"]; };
m3-kratos = { context = "desktop"; sets = ["coding" "gaming" "media"]; };
m3-daedalus = { context = "desktop"; sets = ["coding" "media"]; };
# Server hosts
m3-atlas = { context = "server"; sets = ["coding"]; };
m3-helios = { context = "server"; sets = []; };
m3-hermes = { context = "server"; sets = []; };
m3-aether = { context = "server"; sets = []; };
};
Work Identity Use Case
The same m3ta-home repo supports a work identity for company machines:
# On a work NixOS machine:
(m3ta-lib.mkHome {
user = "m3tam3re";
identity = "work"; # ← switches git to sascha.koenig, SSH to AZ hosts
context = "desktop";
sets = ["coding"];
})
This provides the familiar shell/editor/CLI setup but with work git credentials and SSH configuration.
Commands
| Action | Command | Notes |
|---|---|---|
| Enter dev shell | nix develop |
Includes alejandra, nixd, agenix, statix, deadnix |
| Build host | sudo nixos-rebuild switch --flake .#m3-ares |
Replace hostname as needed |
| Dry run build | sudo nixos-rebuild dry-run --flake .#m3-ares |
Validate without applying |
| List hosts | nix flake show |
Shows all NixOS configurations |
| Update flake | sudo nixos-rebuild switch --flake .#m3-ares --update-input nixpkgs |
Update specific input |
| Format code | alejandra . |
Run before committing |
| Check lint | statix check . |
Run statix for antipatterns |
| Remove dead code | deadnix -w . |
Clean up unused let bindings |
| Build ISO | nix build .#nixosConfigurations.m3-ares.config.system.build.isoImage |
Generate install ISO |
Conventions
Formatting & Style
- Formatter:
alejandra(mandatory, run before commits) - Indentation: 2 spaces (alejandra default)
- Variables: camelCase (e.g.,
maxRetryAttempts) - Types/Modules: PascalCase (e.g.,
MyService) - Constants: UPPER_SNAKE_CASE (e.g.,
MAX_RETRIES) - Files: hyphen-case (e.g.,
my-file.nix)
Nix Module Patterns
{ config, lib, pkgs, ... }:
{
options.myService.enable = lib.mkEnableOption "my service";
config = lib.mkIf config.myService.enable {
services.myService.enable = true;
};
}
Conditionals
config = lib.mkMerge [
(lib.mkIf cfg.enable { ... })
(lib.mkIf cfg.extraConfig { ... })
];
Anti-Patterns (AVOID)
- Never use
with pkgs;— always use explicit package references - Never use
builtins.fetchTarball— use flake inputs instead - Never use
import <nixpkgs>— always use inputs - Never use
builtins.getAttr/hasAttr— uselib.attrByPathorlib.optionalAttrs - Avoid anonymous functions in config — extract to named lets
Imports
- Use flake inputs for dependencies (e.g.,
inputs.home-manager.nixosModules.home-manager) - Import relative paths with
./or../ - Never use absolute paths in imports
Secrets
- Secrets managed via agenix in
secrets/directory - Never commit plaintext secrets
- Use
.nixextension for secret files
Flake Input URLs
All code.m3ta.dev inputs use SSH URLs:
url = "git+ssh://gitea@code.m3ta.dev/m3tam3re/<repo>";
Anonymous HTTPS git on Gitea is unreliable and prompts for auth. SSH works with configured keys.
Key Files
| File | Purpose |
|---|---|
flake.nix |
Central entry point defining all hosts, overlays, packages, dev shells, and nixpkgs config |
hosts/common/default.nix |
Shared Nix settings, nixpkgs overlays, home-manager setup (useGlobalPkgs = true) |
hosts/common/users/m3tam3re.nix |
User definition + m3ta-home mkHome integration + per-host feature flags |
hosts/<name>/home.nix |
Host-specific overrides: monitors, workspaces, window rules, XDG/MIME |
overlays/default.nix |
Package version overrides (stable/locked/master branches) |
.opencode-rules/languages/nix.md |
Nix-specific conventions and patterns |
What to Avoid
- Don't modify
flake.lockdirectly — usenix flake update - Don't use impure operations — this is a pure flake-based config
- Don't commit without formatting — always run
alejandra .first - Don't add packages to hosts directly — prefer adding to overlays or using NUR
- Don't hardcode paths — use
inputsand relative imports - Don't create monolithic modules — keep functions under 20 lines
- Don't skip the dry-run — always test with
--dry-runbefore switching - Don't use lib.mkDefault lightly — understand the precedence implications
Notes
Adding a New Host
- Add entry to
flake.nix→nixosConfigurations - Create directory in
hosts/with:default.nix— imports common + specific configsconfiguration.nix— host-specific system confighardware-configuration.nix— fromnixos-generate-configprograms.nix,services/,secrets.nixas needed
- Add entry to
hostProfilesinhosts/common/users/m3tam3re.nix - Add feature flags in the
hostFlagssection - Create
hosts/<name>/home.nixif the host needs monitor/XDG overrides - Run
sudo nixos-generate-config --dir ./hosts/new-hostfirst time
Adding a New Package
- For simple packages: add to appropriate overlay in
overlays/default.nix - For complex packages: create in
pkgs/directory - For upstream packages: use NUR or add as flake input
Adding a New Home-Manager Feature
- Create the module in
m3ta-homeunder the appropriate profile directory - Add the import to the parent
default.nixin m3ta-home - Enable it per-host via feature flags in
hosts/common/users/m3tam3re.nix
Development Workflow
- Edit config files
- Run
alejandra .to format - Run
statix check .for linting - Run
sudo nixos-rebuild dry-run --flake .#m3-ares - If successful:
sudo nixos-rebuild switch --flake .#m3-ares
Remote Building
# Build on remote machine
nix copy --to ssh://user@host .#nixosConfigurations.m3-ares.config.system.build.toplevel
ssh user@host 'sudo nixos-rebuild switch --flake /nix/store/...-closure'