Files
Mainline/AGENTS.md

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

  1. Always run the test suite - never commit code that fails tests:

    mise run test
    
  2. Always run the linter:

    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:

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.

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.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 <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 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