- Refactor major-domo skill: api_client.py, cli.py, and CLI modules (admin, common, injuries, results, schedule, transactions) with significant simplification (-275 lines net) - Update CLI_REFERENCE.md and SKILL.md docs for major-domo - Update create-scheduled-task SKILL.md - Update plugins blocklist.json and known_marketplaces.json - Add patterns/ directory to repo - Update CLAUDE.md Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
87 lines
2.4 KiB
Python
87 lines
2.4 KiB
Python
"""
|
|
Shared utilities for Major Domo CLI modules.
|
|
|
|
All CLI sub-modules import from here for consistent output and state management.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
from typing import Any, List, Optional
|
|
|
|
from rich.console import Console
|
|
from rich.table import Table
|
|
|
|
# Import the API client from same directory
|
|
sys.path.insert(0, os.path.dirname(__file__))
|
|
from api_client import MajorDomoAPI
|
|
|
|
console = Console()
|
|
|
|
|
|
class State:
|
|
"""Global state for API client and settings"""
|
|
|
|
api: Optional[MajorDomoAPI] = None
|
|
json_output: bool = False
|
|
current_season: Optional[int] = None
|
|
|
|
|
|
state = State()
|
|
|
|
|
|
def output_json(data):
|
|
"""Output data as formatted JSON"""
|
|
console.print_json(json.dumps(data, indent=2, default=str))
|
|
|
|
|
|
def output_table(
|
|
title: str, columns: List[str], rows: List[List], show_lines: bool = False
|
|
):
|
|
"""Output data as a rich table"""
|
|
table = Table(
|
|
title=title, show_header=True, header_style="bold cyan", show_lines=show_lines
|
|
)
|
|
for col in columns:
|
|
table.add_column(col)
|
|
for row in rows:
|
|
table.add_row(*[str(cell) if cell is not None else "" for cell in row])
|
|
console.print(table)
|
|
|
|
|
|
def handle_error(e: Exception, context: str = ""):
|
|
"""Graceful error handling with helpful messages"""
|
|
import typer
|
|
|
|
error_str = str(e)
|
|
if "401" in error_str:
|
|
console.print("[red]Error:[/red] Unauthorized. Check your API_TOKEN.")
|
|
elif "404" in error_str:
|
|
console.print(f"[red]Error:[/red] Not found. {context}")
|
|
elif "Connection" in error_str or "ConnectionError" in error_str:
|
|
console.print(
|
|
"[red]Error:[/red] Cannot connect to API. Check network and --env setting."
|
|
)
|
|
else:
|
|
console.print(f"[red]Error:[/red] {e}")
|
|
raise typer.Exit(1)
|
|
|
|
|
|
def safe_nested(obj: Any, *keys: str, default: str = "N/A") -> str:
|
|
"""Safely extract a value from nested dicts. Returns default if any key is missing or obj isn't a dict."""
|
|
for key in keys:
|
|
if not isinstance(obj, dict):
|
|
return default
|
|
obj = obj.get(key)
|
|
return obj if obj is not None else default
|
|
|
|
|
|
def get_season(season: Optional[int] = None) -> int:
|
|
"""Get season, defaulting to current if not specified. Lazy-fetches current season on first call."""
|
|
if season is not None:
|
|
return season
|
|
if state.current_season is None:
|
|
current = state.api.get_current()
|
|
state.current_season = current["season"]
|
|
return state.current_season
|