7.2 KiB
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
# Install dependencies
mise run install
# Or equivalently:
uv sync
Available Commands
# 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:
ls -la .git/hooks/pre-commit
If hooks are not installed, install them with:
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
-
Always run the test suite - never commit code that fails tests:
mise run test -
Always run the linter:
mise run lint -
Fix any lint errors before committing (or let the pre-commit hook handle it).
-
Review your changes using
git diffto 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 diffto 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:
mise run test
Run with coverage:
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:
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:
# 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.pyin the background, renders to terminal - C&C Topics: Uses separate ntfy topics (like UART serial):
klubhaus_terminal_mainline_cc_cmd- commands TO mainlineklubhaus_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 <name> on - Enable an effect
/effects <name> off - Disable an effect
/effects <name> 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:
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:
# 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 specification:
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
Types
feat: A new featurefix: A bug fixdocs: Documentation only changesstyle: Changes that don't affect code meaning (formatting)refactor: Code change that neither fixes a bug nor adds a featuretest: Adding or updating testschore: 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