# Agent Development Guide ## Development Environment This project uses: - **mise** (mise.jdx.dev) - tool version manager and task runner - **hk** (hk.jdx.dev) - git hook manager - **uv** - fast Python package installer - **ruff** - linter and formatter - **pytest** - test runner ### Setup ```bash # Install dependencies mise run install # Or equivalently: uv sync ``` ### Available Commands ```bash # Development mise run test # Run tests mise run test-v # Run tests verbose mise run test-cov # Run tests with coverage report mise run lint # Run ruff linter mise run lint-fix # Run ruff with auto-fix mise run format # Run ruff formatter mise run ci # Full CI pipeline # Runtime mise run run # Interactive terminal mode (news) mise run run-poetry # Interactive terminal mode (poetry) mise run run-firehose # Dense headline mode # Daemon mode (recommended for long-running) mise run daemon # Start mainline in background mise run daemon-stop # Stop daemon mise run daemon-restart # Restart daemon # Command & Control mise run cmd # Interactive CLI mise run cmd "/cmd" # Send single command mise run cmd-stats # Watch performance stats mise run topics-init # Initialize ntfy topics # Environment mise run install # Install dependencies mise run sync # Sync dependencies mise run sync-all # Sync with all extras mise run clean # Clean cache files mise run clobber # Aggressive cleanup (git clean -fdx + caches) ``` ## Git Hooks **At the start of every agent session**, verify hooks are installed: ```bash ls -la .git/hooks/pre-commit ``` If hooks are not installed, install them with: ```bash hk init --mise mise run pre-commit ``` The project uses hk configured in `hk.pkl`: - **pre-commit**: runs ruff-format and ruff (with auto-fix) - **pre-push**: runs ruff check ## Workflow Rules ### Before Committing 1. **Always run the test suite** - never commit code that fails tests: ```bash mise run test ``` 2. **Always run the linter**: ```bash mise run lint ``` 3. **Fix any lint errors** before committing (or let the pre-commit hook handle it). 4. **Review your changes** using `git diff` to understand what will be committed. ### On Failing Tests When tests fail, **determine whether it's an out-of-date test or a correctly failing test**: - **Out-of-date test**: The test was written for old behavior that has legitimately changed. Update the test to match the new expected behavior. - **Correctly failing test**: The test correctly identifies a broken contract. Fix the implementation, not the test. **Never** modify a test to make it pass without understanding why it failed. ### Code Review Before committing significant changes: - Run `git diff` to review all changes - Ensure new code follows existing patterns in the codebase - Check that type hints are added for new functions - Verify that tests exist for new functionality ## Testing Tests live in `tests/` and follow the pattern `test_*.py`. Run all tests: ```bash mise run test ``` Run with coverage: ```bash mise run test-cov ``` The project uses pytest with strict marker enforcement. Test configuration is in `pyproject.toml` under `[tool.pytest.ini_options]`. ## Architecture Notes - **ntfy.py** and **mic.py** are standalone modules with zero internal dependencies - **eventbus.py** provides thread-safe event publishing for decoupled communication - **controller.py** coordinates ntfy/mic monitoring - The render pipeline: fetch → render → effects → scroll → terminal output - **display.py** provides swappable display backends (TerminalDisplay, NullDisplay) ## Operating Modes Mainline can run in two modes: ### 1. Standalone Mode (Original) Run directly as a terminal application with interactive pickers: ```bash mise run run # news stream mise run run-poetry # poetry mode mise run run-firehose # dense headline mode ``` This runs the full interactive experience with font picker and effects picker at startup. ### 2. Daemon + Command Mode (Recommended for Long-Running) The recommended approach for persistent displays: ```bash # Start the daemon (headless rendering) mise run daemon # Send commands via ntfy mise run cmd "/effects list" mise run cmd "/effects noise off" mise run cmd "/effects stats" # Watch mode (continuous stats polling) mise run cmd-stats # Stop the daemon mise run daemon-stop ``` #### How It Works - **Daemon**: Runs `mainline.py` in the background, renders to terminal - **C&C Topics**: Uses separate ntfy topics (like UART serial): - `klubhaus_terminal_mainline_cc_cmd` - commands TO mainline - `klubhaus_terminal_mainline_cc_resp` - responses FROM mainline - **Topics are auto-warmed** on first daemon start #### Available Commands ``` /effects list - List all effects and status /effects on - Enable an effect /effects off - Disable an effect /effects intensity 0.5 - Set effect intensity (0.0-1.0) /effects reorder noise,fade,glitch,firehose - Reorder pipeline /effects stats - Show performance statistics ``` ## Effects Plugin System The effects system is implemented as a plugin architecture in `engine/effects/`. ### Core Components | Module | Purpose | |--------|---------| | `effects/types.py` | `EffectConfig`, `EffectContext` dataclasses and `EffectPlugin` protocol | | `effects/registry.py` | Plugin discovery and management (`EffectRegistry`) | | `effects/chain.py` | Ordered pipeline execution (`EffectChain`) | | `effects_plugins/*.py` | Externalized effect plugins | ### Creating a New Effect Create a file in `effects_plugins/` with a class ending in `Effect`: ```python from engine.effects.types import EffectConfig, EffectContext class MyEffect: name = "myeffect" config = EffectConfig(enabled=True, intensity=1.0) def process(self, buf: list[str], ctx: EffectContext) -> list[str]: # Process buffer and return modified buffer return buf def configure(self, config: EffectConfig) -> None: self.config = config ``` ### NTFY Commands Send commands via `cmdline.py` or directly to the C&C topic: ```bash # Using cmdline tool (recommended) mise run cmd "/effects list" mise run cmd "/effects noise on" mise run cmd "/effects noise intensity 0.5" mise run cmd "/effects reorder noise,glitch,fade,firehose" # Or directly via curl curl -d "/effects list" https://ntfy.sh/klubhaus_terminal_mainline_cc_cmd ``` The cmdline tool polls the response topic for the daemon's reply. ## Conventional Commits Commit messages follow the [Conventional Commits](https://www.conventionalcommits.org/) specification: ``` (): [optional body] [optional footer(s)] ``` ### Types - `feat`: A new feature - `fix`: A bug fix - `docs`: Documentation only changes - `style`: Changes that don't affect code meaning (formatting) - `refactor`: Code change that neither fixes a bug nor adds a feature - `test`: Adding or updating tests - `chore`: Changes to build process, dependencies, etc. ### Examples ``` feat(effects): add plugin architecture for visual effects fix(layers): resolve glitch effect not applying on empty buffer docs(AGENTS.md): add effects plugin system documentation test(effects): add tests for EffectChain pipeline ordering ```