diff --git a/pyproject.toml b/pyproject.toml index fb3d768..1c0056d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,6 +51,7 @@ requires-python = ">=3.11, <3.15" dependencies = [ "pydantic>=2,<3", + "rich>=14,<15", ] [dependency-groups] diff --git a/src/aignostics_foundry_core/AGENTS.md b/src/aignostics_foundry_core/AGENTS.md index 93f3587..b6ac971 100644 --- a/src/aignostics_foundry_core/AGENTS.md +++ b/src/aignostics_foundry_core/AGENTS.md @@ -8,12 +8,26 @@ This file provides an overview of all modules in `aignostics_foundry_core`, thei | Module | Purpose | Description | |--------|---------|-------------| +| **console** | Themed terminal output | Module-level `console` object (Rich `Console`) with colour theme and `_get_console()` factory | | **health** | Service health checks | `Health` model and `HealthStatus` enum for tree-structured health status | ## Module Descriptions +### console + +**Themed Rich console for structured terminal output** + +- **Purpose**: Provides a module-level `console` object pre-configured with a colour theme for consistent, styled terminal output across all Foundry components +- **Key Features**: + - `console` — module-level `Console` singleton, ready to use + - Colour theme: `success` (green), `info` / `logging.level.info` (purple4), `warning` (yellow1), `error` (red1), `debug` (light_cyan3) + - `AIGNOSTICS_CONSOLE_WIDTH` env var — overrides console width (defaults to Rich's auto-detect, 80 in non-TTY environments) + - `legacy_windows=False` — modern Windows terminal support +- **Location**: `aignostics_foundry_core/console.py` +- **Dependencies**: `rich>=13` + ### health **Tree-structured health status for service health checks** @@ -47,12 +61,22 @@ This file provides an overview of all modules in `aignostics_foundry_core`, thei ┌──────────────┴──────────────┐ │ aignostics_foundry_core │ ├─────────────────────────────┤ +│ console │ │ health │ └─────────────────────────────┘ ``` ## Usage Examples +```python +from aignostics_foundry_core import console + +# Print with theme styles +console.print("[success]Done![/success]") +console.print("[warning]Caution: retrying...[/warning]") +console.print("[error]Failed to connect.[/error]") +``` + ```python from aignostics_foundry_core.health import Health, HealthStatus diff --git a/src/aignostics_foundry_core/__init__.py b/src/aignostics_foundry_core/__init__.py index 8501c33..8142808 100644 --- a/src/aignostics_foundry_core/__init__.py +++ b/src/aignostics_foundry_core/__init__.py @@ -1 +1,4 @@ -"""Foundational infrastructure for Foundry components.""" +"""Foundational infrastructure for Foundry components. + +We do NOT export any of the public APIs of the Foundry components here; see docs/decisions/0002-module-structure.md. +""" diff --git a/src/aignostics_foundry_core/console.py b/src/aignostics_foundry_core/console.py new file mode 100644 index 0000000..aef8f4d --- /dev/null +++ b/src/aignostics_foundry_core/console.py @@ -0,0 +1,31 @@ +"""Themed rich console.""" + +import os + +from rich.console import Console +from rich.theme import Theme + + +def _get_console() -> Console: + """Get a themed rich console. + + The console width can be set via the AIGNOSTICS_CONSOLE_WIDTH environment variable. + + Returns: + Console: The themed rich console. + """ + return Console( + theme=Theme({ + "logging.level.info": "purple4", + "debug": "light_cyan3", + "success": "green", + "info": "purple4", + "warning": "yellow1", + "error": "red1", + }), + width=int(os.environ.get("AIGNOSTICS_CONSOLE_WIDTH", "0")) or None, + legacy_windows=False, # Modern Windows (10+) doesn't need width adjustment + ) + + +console = _get_console() diff --git a/tests/aignostics_foundry_core/console_test.py b/tests/aignostics_foundry_core/console_test.py new file mode 100644 index 0000000..7045d5f --- /dev/null +++ b/tests/aignostics_foundry_core/console_test.py @@ -0,0 +1,43 @@ +"""Tests for console module.""" + +import importlib +import sys + +import pytest +from rich.console import Console + +from aignostics_foundry_core.console import console + +EXPECTED_THEME_KEYS = ["success", "info", "warning", "error", "debug", "logging.level.info"] + + +class TestConsole: + """Tests for the themed rich console module.""" + + @pytest.mark.unit + def test_console_is_console_instance(self) -> None: + """Module-level console is a rich.console.Console instance.""" + assert isinstance(console, Console) + + @pytest.mark.unit + @pytest.mark.sequential + def test_console_default_width(self, monkeypatch: pytest.MonkeyPatch) -> None: + """Module-level console has Rich's default width (80) when env var is not set.""" + monkeypatch.delenv("AIGNOSTICS_CONSOLE_WIDTH", raising=False) + reloaded = importlib.reload(sys.modules["aignostics_foundry_core.console"]) + assert reloaded.console.width == 80 + + @pytest.mark.unit + @pytest.mark.sequential + def test_console_custom_width(self, monkeypatch: pytest.MonkeyPatch) -> None: + """Module-level console uses width from AIGNOSTICS_CONSOLE_WIDTH env var.""" + monkeypatch.setenv("AIGNOSTICS_CONSOLE_WIDTH", "100") + reloaded = importlib.reload(sys.modules["aignostics_foundry_core.console"]) + assert reloaded.console.width == 100 + + @pytest.mark.unit + def test_console_theme_contains_expected_keys(self) -> None: + """Console can render text with all required theme style names without error.""" + for key in EXPECTED_THEME_KEYS: + style = console.get_style(key) + assert style is not None, f"Theme style '{key}' should be defined on the console." diff --git a/uv.lock b/uv.lock index b32734a..f15646f 100644 --- a/uv.lock +++ b/uv.lock @@ -11,6 +11,7 @@ version = "0.0.0" source = { editable = "." } dependencies = [ { name = "pydantic" }, + { name = "rich" }, ] [package.dev-dependencies] @@ -43,7 +44,10 @@ dev = [ ] [package.metadata] -requires-dist = [{ name = "pydantic", specifier = ">=2,<3" }] +requires-dist = [ + { name = "pydantic", specifier = ">=2,<3" }, + { name = "rich", specifier = ">=14,<15" }, +] [package.metadata.requires-dev] dev = [