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
+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',
]