""" Help text and documentation for terminal client commands. Provides detailed, formatted help text for all REPL commands with usage examples and option descriptions. Author: Claude Date: 2025-10-27 """ from typing import Dict from rich.console import Console from rich.table import Table from rich.panel import Panel from rich.markdown import Markdown from rich import box console = Console() class HelpFormatter: """Format and display help text for commands.""" @staticmethod def show_command_help(command_name: str, help_data: Dict) -> None: """ Display detailed help for a specific command. Args: command_name: Name of the command help_data: Dictionary with help information { 'summary': 'Brief description', 'usage': 'command [OPTIONS]', 'options': [ {'name': '--option', 'type': 'TYPE', 'desc': 'Description'} ], 'examples': ['example 1', 'example 2'] } """ # Build help text help_text = [] # Summary help_text.append(f"**{command_name}** - {help_data.get('summary', 'No description')}") help_text.append("") # Usage if 'usage' in help_data: help_text.append("**USAGE:**") help_text.append(f" {help_data['usage']}") help_text.append("") # Display in panel if help_text: md = Markdown("\n".join(help_text[:3])) # Just summary and usage panel = Panel( md, title=f"[bold cyan]Help: {command_name}[/bold cyan]", border_style="cyan", box=box.ROUNDED ) console.print(panel) console.print() # Options if 'options' in help_data and help_data['options']: console.print("[bold cyan]OPTIONS:[/bold cyan]") # Create options table table = Table(box=box.SIMPLE, show_header=False, padding=(0, 2)) table.add_column("Option", style="cyan", no_wrap=True) table.add_column("Type", style="yellow") table.add_column("Description", style="white") for opt in help_data['options']: table.add_row( opt['name'], opt.get('type', ''), opt.get('desc', '') ) console.print(table) console.print() # Examples if 'examples' in help_data and help_data['examples']: console.print("[bold cyan]EXAMPLES:[/bold cyan]") for example in help_data['examples']: console.print(f" {example}") console.print() # Notes if 'notes' in help_data: console.print(f"[dim]{help_data['notes']}[/dim]") console.print() @staticmethod def show_command_list() -> None: """Display list of all available commands.""" console.print("\n[bold cyan]Available Commands:[/bold cyan]\n") # Game Management console.print("[bold yellow]Game Management:[/bold yellow]") console.print(" new_game Create a new game with test lineups and start it") console.print(" list_games List all games in state manager") console.print(" use_game Switch to a different game") console.print(" status Display current game state") console.print(" box_score Display box score") console.print() # Gameplay console.print("[bold yellow]Gameplay:[/bold yellow]") console.print(" defensive Submit defensive decision") console.print(" offensive Submit offensive decision") console.print(" resolve Resolve the current play") console.print(" resolve_with Resolve with a specific outcome (testing)") console.print(" quick_play Auto-play multiple plays") console.print() # Testing console.print("[bold yellow]Testing & Development:[/bold yellow]") console.print(" list_outcomes Show all available PlayOutcome values") console.print(" test_location Test hit location distribution") console.print(" rollback Roll back the last N plays") console.print() # X-Check Testing (Dice Systems) console.print("[bold yellow]X-Check Testing:[/bold yellow]") console.print(" roll_jump Roll jump dice for stolen base testing") console.print(" test_jump Test jump roll distribution") console.print(" roll_fielding Roll fielding check dice") console.print(" test_fielding Test fielding roll distribution") console.print() # Interrupt Plays console.print("[bold yellow]Interrupt Plays:[/bold yellow]") console.print(" force_wild_pitch Force a wild pitch interrupt play") console.print(" force_passed_ball Force a passed ball interrupt play") console.print() # Utilities console.print("[bold yellow]Utilities:[/bold yellow]") console.print(" config Show configuration") console.print(" clear Clear the screen") console.print(" help Show help for commands") console.print(" quit/exit Exit the REPL") console.print() console.print("[dim]Type 'help ' for detailed information.[/dim]") console.print("[dim]Use TAB for auto-completion of commands and options.[/dim]\n") # Detailed help data for each command HELP_DATA = { 'new_game': { 'summary': 'Create a new game with test lineups and start it immediately', 'usage': 'new_game [--league LEAGUE] [--home-team ID] [--away-team ID]', 'options': [ { 'name': '--league', 'type': 'sba|pd', 'desc': 'League type (default: sba)' }, { 'name': '--home-team', 'type': 'INT', 'desc': 'Home team ID (default: 1)' }, { 'name': '--away-team', 'type': 'INT', 'desc': 'Away team ID (default: 2)' } ], 'examples': [ 'new_game', 'new_game --league pd', 'new_game --league sba --home-team 5 --away-team 3' ] }, 'defensive': { 'summary': 'Submit defensive decision for the current play', 'usage': 'defensive [--alignment TYPE] [--infield DEPTH] [--outfield DEPTH] [--hold BASES]', 'options': [ { 'name': '--alignment', 'type': 'STRING', 'desc': 'Defensive alignment: normal, shifted_left, shifted_right, extreme_shift (default: normal)' }, { 'name': '--infield', 'type': 'STRING', 'desc': 'Infield depth: in, normal, back, double_play (default: normal)' }, { 'name': '--outfield', 'type': 'STRING', 'desc': 'Outfield depth: in, normal, back (default: normal)' }, { 'name': '--hold', 'type': 'LIST', 'desc': 'Comma-separated bases to hold runners: 1,2,3 (default: none)' } ], 'examples': [ 'defensive', 'defensive --alignment shifted_left', 'defensive --infield double_play --hold 1,3', 'defensive --alignment extreme_shift --infield back --outfield back' ] }, 'offensive': { 'summary': 'Submit offensive decision for the current play', 'usage': 'offensive [--approach TYPE] [--steal BASES] [--hit-run] [--bunt]', 'options': [ { 'name': '--approach', 'type': 'STRING', 'desc': 'Batting approach: normal, contact, power, patient (default: normal)' }, { 'name': '--steal', 'type': 'LIST', 'desc': 'Comma-separated bases to steal: 2,3 (default: none)' }, { 'name': '--hit-run', 'type': 'FLAG', 'desc': 'Execute hit-and-run play (default: false)' }, { 'name': '--bunt', 'type': 'FLAG', 'desc': 'Attempt bunt (default: false)' } ], 'examples': [ 'offensive', 'offensive --approach power', 'offensive --steal 2', 'offensive --steal 2,3 --hit-run', 'offensive --approach contact --bunt' ] }, 'resolve': { 'summary': 'Resolve the current play using submitted decisions', 'usage': 'resolve', 'options': [], 'examples': [ 'resolve' ], 'notes': 'Both defensive and offensive decisions must be submitted before resolving.' }, 'list_outcomes': { 'summary': 'Display all available PlayOutcome values for manual outcome testing', 'usage': 'list_outcomes', 'options': [], 'examples': [ 'list_outcomes' ], 'notes': 'Shows a categorized table of all play outcomes. Use these values with resolve_with command.' }, 'resolve_with': { 'summary': 'Resolve current play with a specific outcome (bypassing dice rolls)', 'usage': 'resolve_with \n resolve_with x-check [[+]]', 'options': [ { 'name': 'OUTCOME', 'type': 'STRING', 'desc': 'PlayOutcome enum value. Use list_outcomes to see all available values.' }, { 'name': 'POSITION', 'type': 'P|C|1B|2B|3B|SS|LF|CF|RF', 'desc': 'For x-check: defensive position to test (required)' }, { 'name': 'RESULT', 'type': 'G1|G2|G3|F1|F2|F3|SI1|SI2|DO2|DO3|TR3|FO|PO', 'desc': 'For x-check: force specific converted result (optional)' }, { 'name': 'ERROR', 'type': 'NO|E1|E2|E3|RP', 'desc': 'For x-check: force specific error result (optional, default: NO)' } ], 'examples': [ 'resolve_with single_1', 'resolve_with homerun', 'resolve_with groundball_a', 'resolve_with double_uncapped', 'resolve_with strikeout', 'resolve_with x-check SS # Random X-Check to shortstop', 'resolve_with x-check LF DO2 # Force double to LF, no error', 'resolve_with x-check 2B G2+E1 # Force groundout to 2B with E1', 'resolve_with x-check SS SI2+E2 # Force single to SS with E2', 'resolve_with x-check LF FO+RP # Force flyout to LF with rare play' ], 'notes': 'Experimental feature for testing specific scenarios without random dice rolls. X-Check mode uses full defense tables and error charts with actual player ratings. When RESULT+ERROR are specified, table lookups are bypassed for precise testing of specific outcomes.' }, 'quick_play': { 'summary': 'Auto-play multiple plays with default decisions', 'usage': 'quick_play [COUNT]', 'options': [ { 'name': 'COUNT', 'type': 'INT', 'desc': 'Number of plays to execute (default: 1). Positional argument.' } ], 'examples': [ 'quick_play', 'quick_play 10', 'quick_play 27 # Play roughly 3 innings', 'quick_play 100 # Play full game quickly' ] }, 'status': { 'summary': 'Display current game state', 'usage': 'status', 'options': [], 'examples': [ 'status' ] }, 'box_score': { 'summary': 'Display box score for the current game', 'usage': 'box_score', 'options': [], 'examples': [ 'box_score' ] }, 'list_games': { 'summary': 'List all games currently loaded in state manager', 'usage': 'list_games', 'options': [], 'examples': [ 'list_games' ] }, 'use_game': { 'summary': 'Switch to a different game', 'usage': 'use_game ', 'options': [ { 'name': 'GAME_ID', 'type': 'UUID', 'desc': 'UUID of the game to switch to. Positional argument.' } ], 'examples': [ 'use_game a1b2c3d4-e5f6-7890-abcd-ef1234567890', 'use_game # Use tab completion to see available games' ] }, 'config': { 'summary': 'Show terminal client configuration', 'usage': 'config', 'options': [], 'examples': [ 'config' ] }, 'clear': { 'summary': 'Clear the screen', 'usage': 'clear', 'options': [], 'examples': [ 'clear' ] }, # X-Check Testing Commands 'roll_jump': { 'summary': 'Roll jump dice for stolen base testing', 'usage': 'roll_jump [LEAGUE]', 'options': [ { 'name': 'LEAGUE', 'type': 'sba|pd', 'desc': 'League type (default: sba)' } ], 'examples': [ 'roll_jump # Roll for SBA league', 'roll_jump pd # Roll for PD league' ], 'notes': 'Components: 1d20 check (1=pickoff, 2=balk, 3+=normal), 2d6 jump total' }, 'test_jump': { 'summary': 'Test jump roll distribution statistics', 'usage': 'test_jump [COUNT] [LEAGUE]', 'options': [ { 'name': 'COUNT', 'type': 'integer', 'desc': 'Number of rolls to test (default: 10)' }, { 'name': 'LEAGUE', 'type': 'sba|pd', 'desc': 'League type (default: sba)' } ], 'examples': [ 'test_jump # 10 rolls for SBA', 'test_jump 100 # 100 rolls for SBA', 'test_jump 50 pd # 50 rolls for PD' ], 'notes': 'Shows pickoff/balk/normal frequency and 2d6 distribution' }, 'roll_fielding': { 'summary': 'Roll fielding check dice for X-Check defensive plays', 'usage': 'roll_fielding [LEAGUE]', 'options': [ { 'name': 'POSITION', 'type': 'P|C|1B|2B|3B|SS|LF|CF|RF', 'desc': 'Fielding position (required)' }, { 'name': 'LEAGUE', 'type': 'sba|pd', 'desc': 'League type (default: sba)' } ], 'examples': [ 'roll_fielding SS # Roll for shortstop (SBA)', 'roll_fielding P pd # Roll for pitcher (PD)', 'roll_fielding CF # Roll for center field' ], 'notes': 'Components: 1d20 range, 3d6 error total, d100/special rare play check' }, 'test_fielding': { 'summary': 'Test fielding roll distribution for a position', 'usage': 'test_fielding [COUNT] [LEAGUE]', 'options': [ { 'name': 'POSITION', 'type': 'P|C|1B|2B|3B|SS|LF|CF|RF', 'desc': 'Fielding position (required)' }, { 'name': 'COUNT', 'type': 'integer', 'desc': 'Number of rolls (default: 10)' }, { 'name': 'LEAGUE', 'type': 'sba|pd', 'desc': 'League type (default: sba)' } ], 'examples': [ 'test_fielding SS # 10 rolls for shortstop', 'test_fielding 2B 100 # 100 rolls for second base', 'test_fielding CF 50 pd # 50 rolls for center field (PD)' ], 'notes': 'Shows rare play frequency, average range/error, and distribution stats' }, 'test_location': { 'summary': 'Test hit location distribution for an outcome', 'usage': 'test_location [HANDEDNESS] [COUNT]', 'options': [ { 'name': 'OUTCOME', 'type': 'PlayOutcome', 'desc': 'PlayOutcome enum value (e.g., groundball_c)' }, { 'name': 'HANDEDNESS', 'type': 'L|R', 'desc': 'Batter handedness (default: R)' }, { 'name': 'COUNT', 'type': 'integer', 'desc': 'Number of samples (default: 100)' } ], 'examples': [ 'test_location groundball_c', 'test_location groundball_c L', 'test_location flyout_b R 200' ], 'notes': 'Shows distribution of hit locations based on pull rates' }, 'rollback': { 'summary': 'Roll back the last N plays', 'usage': 'rollback ', 'options': [ { 'name': 'NUM_PLAYS', 'type': 'integer', 'desc': 'Number of plays to roll back (must be > 0)' } ], 'examples': [ 'rollback 1 # Undo the last play', 'rollback 3 # Undo the last 3 plays', 'rollback 5 # Undo the last 5 plays' ], 'notes': 'Deletes plays from database and reconstructs state by replaying' }, 'force_wild_pitch': { 'summary': 'Force a wild pitch interrupt play', 'usage': 'force_wild_pitch', 'options': [], 'examples': [ 'force_wild_pitch # Runner on 2nd advances to 3rd' ], 'notes': 'Advances all runners one base. This is an interrupt play (pa=0)' }, 'force_passed_ball': { 'summary': 'Force a passed ball interrupt play', 'usage': 'force_passed_ball', 'options': [], 'examples': [ 'force_passed_ball # Runner on 1st advances to 2nd' ], 'notes': 'Advances all runners one base. This is an interrupt play (pa=0)' } } def get_help_text(command: str) -> Dict: """ Get help data for a command. Args: command: Command name Returns: Help data dictionary or empty dict if not found """ return HELP_DATA.get(command, {}) def show_help(command: str = None) -> None: """ Show help for a command or list all commands. Args: command: Command name or None for command list """ if command: help_data = get_help_text(command) if help_data: HelpFormatter.show_command_help(command, help_data) else: console.print(f"[yellow]No help available for '{command}'[/yellow]") console.print("[dim]Type 'help' to see all available commands.[/dim]") else: HelpFormatter.show_command_list()