diff --git a/AGENTS.md b/AGENTS.md index 1bffd0c..9237b6a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,17 +27,17 @@ Use this agent to review code changes before committing. **Trigger:** Before creating commits or PRs. -## metrics-specialist +## entity-specialist -Use this agent when working on system metric collection. +Use this agent when working on entity configuration or the entity system. **Responsibilities:** -- Understand Linux /proc and /sys interfaces -- Know cross-platform metric collection strategies -- Ensure metrics are properly typed and documented -- Validate metric units and normalization +- Understand entity types (sensor, binary_sensor, switch, light, button) +- Design shell commands for state polling and actions +- Ensure proper Home Assistant discovery payloads +- Validate entity configuration options -**Context:** Systant collects CPU, memory, disk, and network metrics. Metrics should be normalized (percentages 0-100, bytes for sizes) and include metadata for Home Assistant discovery. +**Context:** Systant uses a unified "entity" system where all metrics and controls are defined as entities in TOML config. Each entity has a `state_command` and optionally `on_command`/`off_command`/`press_command` depending on type. ## mqtt-specialist @@ -50,20 +50,19 @@ Use this agent when working on MQTT publishing or Home Assistant integration. - Handle connection lifecycle (connect, reconnect, disconnect) - Design topic hierarchies for commands and events -**Context:** Systant publishes to MQTT with Home Assistant auto-discovery. Topics follow the pattern `systant/{hostname}/{metric_type}`. Command topics use `systant/{hostname}/command/{action}`. +**Context:** Systant publishes to MQTT with Home Assistant auto-discovery. Topics follow the pattern `systant/{hostname}/{entity_id}/state` for state updates, `systant/{hostname}/{entity_id}/set` for switch/light commands, and `homeassistant/{type}/{hostname}_{entity_id}/config` for discovery. -## events-specialist +## nix-specialist -Use this agent when working on the event/command system. +Use this agent when working on Nix packaging or the home-manager module. **Responsibilities:** -- Design secure command execution with allowlists -- Implement event handlers and action dispatching -- Ensure proper input validation and sanitization -- Handle timeouts and error reporting -- Consider security implications of remote command execution +- Maintain the Nix flake and package definition +- Update the home-manager module options +- Handle fixed-output derivations for npm dependencies +- Ensure cross-system compatibility -**Context:** Systant listens for MQTT commands and executes configured actions. Security is paramount - all commands must be validated against an allowlist, inputs sanitized, and execution sandboxed where possible. +**Context:** Systant is packaged as a Nix flake with a home-manager module. The package uses a two-phase build: FOD for `bun install`, then `bun build --compile` for the binary. The home-manager module creates a systemd user service. ## debug-investigator diff --git a/CLAUDE.md b/CLAUDE.md index 8101935..da01bf0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,30 +16,49 @@ Systant is a lightweight CLI tool written in Bun/TypeScript that: ### Architecture ``` -index.ts # CLI entry point (yargs) +index.ts # CLI entry point src/ - commands/ # CLI command handlers - metrics/ # System metric collectors - mqtt/ # MQTT client and publishing - events/ # MQTT event listeners and handlers - actions/ # Executable actions (shell, service, notify) - ha/ # Home Assistant discovery - config/ # Configuration loading + config.ts # TOML configuration loading + mqtt.ts # MQTT client, publishing, and HA discovery + entities.ts # Entity management (state polling, command handling) ``` -### Event/Command System +### Entity System -Systant subscribes to MQTT topics and executes configured actions: +Systant uses a unified "entity" concept that combines state monitoring and command handling. Entity types: -``` -Topic: systant/{hostname}/command/{action} -Payload: { "args": [...], "timeout": 30 } +- **sensor**: Read-only numeric/string values (CPU usage, temperature, etc.) +- **binary_sensor**: Read-only on/off states (service running, etc.) +- **switch**: Controllable on/off with `on_command` and `off_command` +- **light**: Same as switch, for display/monitor control +- **button**: Press-only actions with `press_command` -Topic: systant/{hostname}/event/{event_name} -Payload: { ... event data ... } +Each entity is defined in TOML with shell commands: + +```toml +[entities.cpu_usage] +type = "sensor" +state_command = "awk '/^cpu / {print int(($2+$4)*100/($2+$4+$5))}' /proc/stat" +unit = "%" +icon = "mdi:chip" +name = "CPU Usage" + +[entities.headphones] +type = "switch" +state_command = "pactl get-default-sink | grep -q usb && echo ON || echo OFF" +on_command = "pactl set-default-sink alsa_output.usb-..." +off_command = "pactl set-default-sink alsa_output.pci-..." ``` -Actions are sandboxed and configurable via allowlists in the config file. Security is critical - never execute arbitrary commands without validation. +### MQTT Topics + +``` +systant/{hostname}/{entity_id}/state # State updates +systant/{hostname}/{entity_id}/set # Commands (for switch/light) +systant/{hostname}/{entity_id}/press # Button presses +systant/{hostname}/availability # Online/offline status +homeassistant/{type}/{hostname}_{id}/config # HA auto-discovery +``` ### Key Design Decisions @@ -51,10 +70,29 @@ Actions are sandboxed and configurable via allowlists in the config file. Securi ## Tech Stack - **Runtime**: Bun (not Node.js) -- **CLI**: yargs -- **Config**: TOML -- **MQTT**: mqtt.js or Bun-native when available -- **Package**: Nix flake for reproducible builds +- **Config**: TOML (smol-toml) +- **MQTT**: mqtt.js +- **Packaging**: Nix flake with home-manager module + +### NixOS/Home Manager Integration + +```nix +# flake.nix inputs +inputs.systant.url = "git+ssh://..."; + +# Add overlay for pkgs.systant +nixpkgs.overlays = [ inputs.systant.overlays.default ]; + +# Import home-manager module +home-manager.sharedModules = [ inputs.systant.homeManagerModules.default ]; + +# In user config +services.systant = { + enable = true; + settings = { /* TOML as Nix attrset */ }; + # or: configFile = ./systant.toml; +}; +``` ## Bun Conventions @@ -105,10 +143,17 @@ Watch mode: `bun test --watch` ## Commands ```bash -bun run start # Run in development -bun run dist # Build standalone binary -bun test # Run tests -bun test --watch # Watch mode +# Development +bun run index.ts run --config systant.toml + +# Build standalone binary +bun build index.ts --compile --outfile systant + +# Nix build +nix build .#systant + +# Run tests +bun test ``` ## Planning Protocol