commit ae778ebdab21124e1963a53cc2ef1e361a3103a8 Author: ryan Date: Mon Jan 19 19:52:47 2026 -0800 Initial systant implementation in Bun/TypeScript A lightweight system monitoring agent that: - Collects metrics via configurable shell commands - Publishes to MQTT with Home Assistant auto-discovery - Supports entity types: sensor, binary_sensor, light, switch, button - Responds to commands over MQTT for controllable entities Architecture: - src/config.ts: TOML config loading and validation - src/mqtt.ts: MQTT client with HA discovery - src/entities.ts: Entity state polling and command handling - index.ts: CLI entry point (run, check, once commands) Co-Authored-By: Claude Opus 4.5 diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..d42b3a2 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,40 @@ +{ + "$schema": "https://claude.ai/claude-code/settings.schema.json", + "permissions": { + "allow": [ + "Bash(bun test*)", + "Bash(bun run*)", + "Bash(bun build*)", + "Bash(bun install*)", + "Bash(bunx tsc --noEmit*)", + "Bash(git status*)", + "Bash(git diff*)", + "Bash(git log*)", + "Bash(git add*)", + "Bash(git commit*)", + "Bash(ls*)", + "Bash(cat /proc/*)", + "Bash(cat /sys/*)" + ], + "deny": [ + "Bash(rm -rf /)*", + "Bash(sudo *)", + "Bash(*--force*)" + ] + }, + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write", + "hooks": [ + { + "type": "command", + "command": "bunx tsc --noEmit --pretty 2>&1 | head -20 || true", + "description": "Type check after file changes", + "timeout": 10000 + } + ] + } + ] + } +} diff --git a/.claude/skills/build.md b/.claude/skills/build.md new file mode 100644 index 0000000..ead50d0 --- /dev/null +++ b/.claude/skills/build.md @@ -0,0 +1,16 @@ +# /build + +Build the systant CLI binary. + +## Instructions + +1. Run type checking first: `bunx tsc --noEmit` +2. If types pass, build the binary: `bun build index.ts --compile --outfile dist/systant` +3. Report the binary size and location +4. If there are errors, show them clearly and suggest fixes + +## Success Criteria + +- No TypeScript errors +- Binary created at `dist/systant` +- Binary is executable diff --git a/.claude/skills/plan.md b/.claude/skills/plan.md new file mode 100644 index 0000000..c31117b --- /dev/null +++ b/.claude/skills/plan.md @@ -0,0 +1,26 @@ +# /plan + +Enter planning mode to design an implementation approach. + +## Instructions + +1. Enter plan mode using EnterPlanMode tool +2. Explore the codebase to understand current state +3. Identify affected files and components +4. Design the implementation approach +5. Present the plan for user approval before coding + +## When to Use + +- New features +- Architectural changes +- Complex bug fixes +- Refactoring tasks + +## Output + +A clear plan including: +- Files to create/modify +- Key implementation steps +- Potential risks or considerations +- Testing approach diff --git a/.claude/skills/release.md b/.claude/skills/release.md new file mode 100644 index 0000000..4c01051 --- /dev/null +++ b/.claude/skills/release.md @@ -0,0 +1,28 @@ +# /release + +Prepare a release of systant. + +## Instructions + +1. Ensure working directory is clean (`git status`) +2. Run tests: `bun test` +3. Type check: `bunx tsc --noEmit` +4. Build binary: `bun build index.ts --compile --outfile dist/systant` +5. Ask user for version bump type (patch/minor/major) +6. Update version in package.json +7. Create git commit with message: "release: v{version}" +8. Create git tag: `v{version}` +9. Report next steps (push, publish, etc.) + +## Prerequisites + +- All tests must pass +- No TypeScript errors +- Clean git working directory (or user confirms to proceed) + +## Success Criteria + +- Binary built successfully +- Version bumped in package.json +- Git commit and tag created +- Clear instructions for next steps diff --git a/.claude/skills/test.md b/.claude/skills/test.md new file mode 100644 index 0000000..f5a1880 --- /dev/null +++ b/.claude/skills/test.md @@ -0,0 +1,22 @@ +# /test + +Run the test suite. + +## Instructions + +1. Run `bun test` to execute all tests +2. If tests fail: + - Analyze the failure messages + - Identify the root cause + - Suggest specific fixes +3. If tests pass, report the summary + +## Options + +- `/test ` - Run tests matching a pattern (e.g., `/test metrics`) +- `/test --watch` - Run in watch mode + +## Success Criteria + +- All tests pass +- Clear reporting of any failures with actionable suggestions diff --git a/.claude/skills/typecheck.md b/.claude/skills/typecheck.md new file mode 100644 index 0000000..c402b21 --- /dev/null +++ b/.claude/skills/typecheck.md @@ -0,0 +1,16 @@ +# /typecheck + +Run TypeScript type checking. + +## Instructions + +1. Run `bunx tsc --noEmit --pretty` +2. If errors found: + - List each error with file, line, and message + - Provide suggested fixes for each +3. If no errors, confirm success + +## Success Criteria + +- Report all type errors clearly +- Suggest actionable fixes diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..3550a30 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa61bba --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# Dependencies +node_modules +.pnp +.pnp.js + +# Local env files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +# Testing +coverage + +# Turbo +.turbo + +# Vercel +.vercel + +# Build Outputs +.next/ +out/ +build +dist + + +# Debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Misc +.DS_Store +*.pem +auth_failures.log + +# Nix +.direnv +.direnv/* + +# Local config (use systant.toml.example as template) +systant.toml \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..766db8d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "terminal.integrated.fontFamily": "Fira Code" +} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..1bffd0c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,103 @@ +# Agents + +Specialized sub-agents for systant development tasks. + +## test-runner + +Use this agent after writing or modifying code to run the test suite and verify changes. + +**Responsibilities:** +- Run `bun test` and report results +- Identify failing tests and their root causes +- Suggest fixes for test failures +- Run specific test files when targeted testing is needed + +**Trigger:** After implementing features, fixing bugs, or modifying existing code. + +## code-reviewer + +Use this agent to review code changes before committing. + +**Responsibilities:** +- Check for Bun best practices (no Node.js patterns) +- Verify type safety and explicit return types +- Look for potential bugs or edge cases +- Ensure code follows project conventions +- Flag any security concerns (especially in command execution) + +**Trigger:** Before creating commits or PRs. + +## metrics-specialist + +Use this agent when working on system metric collection. + +**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 + +**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. + +## mqtt-specialist + +Use this agent when working on MQTT publishing or Home Assistant integration. + +**Responsibilities:** +- Understand MQTT topic conventions +- Know Home Assistant discovery protocol +- Ensure proper QoS and retain flag usage +- 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}`. + +## events-specialist + +Use this agent when working on the event/command system. + +**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 + +**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. + +## debug-investigator + +Use this agent when troubleshooting issues or unexpected behavior. + +**Responsibilities:** +- Add strategic logging to trace execution +- Isolate the problem to specific components +- Form and test hypotheses +- Propose minimal fixes + +**Trigger:** When something isn't working as expected. + +## architect + +Use this agent for design decisions and architectural questions. + +**Responsibilities:** +- Evaluate trade-offs between approaches +- Consider future extensibility +- Maintain consistency with existing patterns +- Document decisions in code comments or CLAUDE.md + +**Trigger:** When facing design choices or planning new features. + +## security-auditor + +Use this agent when reviewing security-sensitive code. + +**Responsibilities:** +- Review command execution paths for injection vulnerabilities +- Validate input sanitization +- Check allowlist/denylist implementations +- Ensure proper authentication for MQTT commands +- Review file system access patterns + +**Context:** Systant executes commands based on MQTT messages. This is a critical attack surface that requires careful security review. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8101935 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,120 @@ +# Systant + +A system monitoring agent that collects metrics, monitors services, and reports to MQTT/Home Assistant; and responds to events over MQTT to trigger commands or other behavior. + +## Project Overview + +Systant is a lightweight CLI tool written in Bun/TypeScript that: +- Collects system metrics (CPU, memory, disk, network) +- Monitors service health +- Publishes data to MQTT brokers +- Supports Home Assistant auto-discovery +- **Listens for MQTT commands** to trigger actions (run scripts, restart services, etc.) +- **Responds to events** with configurable handlers +- Runs as a daemon or one-shot command + +### Architecture + +``` +index.ts # CLI entry point (yargs) +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 +``` + +### Event/Command System + +Systant subscribes to MQTT topics and executes configured actions: + +``` +Topic: systant/{hostname}/command/{action} +Payload: { "args": [...], "timeout": 30 } + +Topic: systant/{hostname}/event/{event_name} +Payload: { ... event data ... } +``` + +Actions are sandboxed and configurable via allowlists in the config file. Security is critical - never execute arbitrary commands without validation. + +### Key Design Decisions + +- **Single binary**: Compiles to standalone executable via `bun build --compile` +- **No external services**: Uses Bun built-ins (sqlite, file, etc.) +- **Config-driven**: TOML configuration for flexibility +- **Typed throughout**: Full TypeScript with strict mode + +## 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 + +## Bun Conventions + +Default to using Bun instead of Node.js. + +- Use `bun ` instead of `node ` or `ts-node ` +- Use `bun test` instead of `jest` or `vitest` +- Use `bun build ` instead of `webpack` or `esbuild` +- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install` +- Use `bun run