feat(system): CLI stderr standardization + drone error propagation (FPLAN-0032)

CLI display.py now routes error() and warning() to stderr via err_console.
Added fatal() for unrecoverable errors. Drone imports err_console from CLI
instead of creating its own. RegistryMismatchError added to drone exception
hierarchy for credential verification failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
AIOSAI
2026-03-13 23:27:51 -07:00
co-authored by Claude Opus 4.6
parent c6e504f861
commit 154e92d13e
7 changed files with 62 additions and 27 deletions
+4 -1
View File
@@ -21,7 +21,7 @@ PATTERN (from Prax):
"""
# Rich console (primary service - like Prax logger)
from aipass.cli.apps.modules.display import console
from aipass.cli.apps.modules.display import console, err_console
# Display functions
from aipass.cli.apps.modules.display import (
@@ -29,6 +29,7 @@ from aipass.cli.apps.modules.display import (
success,
error,
warning,
fatal,
section
)
@@ -41,12 +42,14 @@ from aipass.cli.apps.modules.templates import (
__all__ = [
# Rich console (primary service)
'console',
'err_console',
# Display
'header',
'success',
'error',
'warning',
'fatal',
'section',
# Templates
+26 -4
View File
@@ -35,6 +35,7 @@ from rich.columns import Columns
# Initialize Rich console (lowercase follows service instance pattern)
CONSOLE = Console() # Internal constant
console = CONSOLE # Primary export (lowercase service instance pattern)
err_console = Console(stderr=True) # Stderr console for error/warning output
# Trigger loaded lazily to avoid circular import
_trigger = None
@@ -325,9 +326,9 @@ def error(message: str, suggestion: str | None = None) -> None:
Example:
error('Branch not found', suggestion='Check branch name spelling')
"""
CONSOLE.print(f"❌ [red bold]{message}[/red bold]")
err_console.print(f"❌ [red bold]{message}[/red bold]")
if suggestion:
CONSOLE.print(f" [yellow]→ Try: {suggestion}[/yellow]")
err_console.print(f" [yellow]→ Try: {suggestion}[/yellow]")
def warning(message: str, details: str | None = None) -> None:
@@ -341,9 +342,28 @@ def warning(message: str, details: str | None = None) -> None:
Example:
warning('Branch already exists, skipping')
"""
CONSOLE.print(f"⚠️ [yellow]{message}[/yellow]")
err_console.print(f"⚠️ [yellow]{message}[/yellow]")
if details:
CONSOLE.print(f" [dim]{details}[/dim]")
err_console.print(f" [dim]{details}[/dim]")
def fatal(message: str, suggestion: str | None = None) -> None:
"""
Display error message with Rich styling and exit with code 1
Like error() but terminates the process. Use for unrecoverable failures.
Args:
message: Error message
suggestion: Optional suggestion for fixing
Example:
fatal('Config file missing', suggestion='Run aipass init first')
"""
err_console.print(f"❌ [red bold]{message}[/red bold]")
if suggestion:
err_console.print(f" [yellow]→ Try: {suggestion}[/yellow]")
sys.exit(1)
def section(title: str) -> None:
@@ -369,10 +389,12 @@ def section(title: str) -> None:
__all__ = [
'console', # Primary export (service instance pattern)
'CONSOLE', # Internal constant (kept for backward compatibility)
'err_console', # Stderr console for error/warning output
'header',
'success',
'error',
'warning',
'fatal',
'section',
]