● Terminal Client Improvement Plan - Part 4: Detailed Help System

  Overview

  Enhance the REPL help system with detailed documentation, examples, and better formatting. This makes the terminal
  client self-documenting and easier to use for new developers.

  Files to Create

  1. Create backend/terminal_client/help_text.py

  """
  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("")

          # Options
          if 'options' in help_data and help_data['options']:
              help_text.append("**OPTIONS:**")

              # 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']:
              help_text.append("**EXAMPLES:**")
              for example in help_data['examples']:
                  help_text.append(f"  {example}")
              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)

              # Print rest outside panel for better formatting
              if len(help_text) > 3:
                  console.print()
                  for line in help_text[3:]:
                      if line.startswith('**'):
                          console.print(line.replace('**', ''), style="bold cyan")
                      else:
                          console.print(line)

      @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("  quick_play    Auto-play multiple plays")
          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 <command>' 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.'
      },

      '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 <GAME_ID>',
          '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 <TAB>    # 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'
          ]
      }
  }


  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(f"[dim]Type 'help' to see all available commands.[/dim]")
      else:
          HelpFormatter.show_command_list()

  Files to Update

  2. Update backend/terminal_client/repl.py

  # Add import at top
  from terminal_client.help_text import show_help, get_help_text, HelpFormatter

  # Update the intro to be more helpful
  class GameREPL(GameREPLCompletions, cmd.Cmd):
      """Interactive REPL for game engine testing."""

      intro = """
  ╔══════════════════════════════════════════════════════════════════════════════╗
  ║                  Paper Dynasty Game Engine - Terminal Client                 ║
  ║                              Interactive Mode                                ║
  ╚══════════════════════════════════════════════════════════════════════════════╝

  [cyan]Type 'help' to see all available commands.[/cyan]
  [cyan]Type 'help <command>' for detailed information about a specific command.[/cyan]
  [cyan]Use TAB for auto-completion of commands and options.[/cyan]

  [yellow]Quick start:[/yellow]
    new_game          Create and start a new game
    status            Show current game state
    defensive         Submit defensive decision
    offensive         Submit offensive decision
    resolve           Resolve the play
    quick_play 10     Auto-play 10 plays

  Press Ctrl+D or type 'quit' to exit.

  """
      prompt = '⚾ > '

      # ... rest of __init__ stays the same ...

      # ==================== Enhanced Help System ====================

      def do_help(self, arg):
          """
          Show help for commands.
          
          Usage:
            help              List all commands
            help <command>    Show detailed help for a command
          """
          if arg:
              # Show detailed help for specific command
              show_help(arg)
          else:
              # Show command list
              HelpFormatter.show_command_list()

      def help_new_game(self):
          """Show detailed help for new_game command."""
          show_help('new_game')

      def help_defensive(self):
          """Show detailed help for defensive command."""
          show_help('defensive')

      def help_offensive(self):
          """Show detailed help for offensive command."""
          show_help('offensive')

      def help_resolve(self):
          """Show detailed help for resolve command."""
          show_help('resolve')

      def help_quick_play(self):
          """Show detailed help for quick_play command."""
          show_help('quick_play')

      def help_status(self):
          """Show detailed help for status command."""
          show_help('status')

      def help_box_score(self):
          """Show detailed help for box_score command."""
          show_help('box_score')

      def help_list_games(self):
          """Show detailed help for list_games command."""
          show_help('list_games')

      def help_use_game(self):
          """Show detailed help for use_game command."""
          show_help('use_game')

      def help_config(self):
          """Show detailed help for config command."""
          show_help('config')

      def help_clear(self):
          """Show detailed help for clear command."""
          show_help('clear')

      # ==================== Keep existing command methods ====================
      # All do_* methods remain unchanged

  3. Create backend/terminal_client/__main__.py Update

  # Add help command for standalone mode
  # Add this at the bottom of main.py

  @cli.command('help')
  @click.argument('command', required=False)
  def show_cli_help(command):
      """
      Show help for terminal client commands.
      
      Usage:
        python -m terminal_client help              # Show all commands
        python -m terminal_client help new-game     # Show help for specific command
      """
      from terminal_client.help_text import show_help

      # Convert hyphenated command to underscore for lookup
      if command:
          command = command.replace('-', '_')

      show_help(command)

  Testing Plan

  4. Create backend/tests/unit/terminal_client/test_help_text.py

  """
  Unit tests for help text system.
  """
  import pytest
  from io import StringIO
  from unittest.mock import patch

  from terminal_client.help_text import (
      HelpFormatter,
      get_help_text,
      show_help,
      HELP_DATA
  )


  class TestHelpData:
      """Tests for help data structure."""

      def test_all_commands_have_help(self):
          """Test that all major commands have help data."""
          required_commands = [
              'new_game', 'defensive', 'offensive', 'resolve',
              'quick_play', 'status', 'use_game', 'list_games'
          ]

          for cmd in required_commands:
              assert cmd in HELP_DATA, f"Missing help data for {cmd}"

      def test_help_data_structure(self):
          """Test that help data has required fields."""
          for cmd, data in HELP_DATA.items():
              assert 'summary' in data, f"{cmd} missing summary"
              assert 'usage' in data, f"{cmd} missing usage"
              assert 'options' in data, f"{cmd} missing options"
              assert 'examples' in data, f"{cmd} missing examples"

              # Validate options structure
              for opt in data['options']:
                  assert 'name' in opt, f"{cmd} option missing name"
                  assert 'desc' in opt, f"{cmd} option missing desc"

      def test_get_help_text_valid(self):
          """Test getting help text for valid command."""
          result = get_help_text('new_game')
          assert result is not None
          assert 'summary' in result

      def test_get_help_text_invalid(self):
          """Test getting help text for invalid command."""
          result = get_help_text('nonexistent_command')
          assert result == {}


  class TestHelpFormatter:
      """Tests for HelpFormatter."""

      @patch('terminal_client.help_text.console')
      def test_show_command_help(self, mock_console):
          """Test showing help for a command."""
          help_data = {
              'summary': 'Test command',
              'usage': 'test [OPTIONS]',
              'options': [
                  {'name': '--option', 'type': 'STRING', 'desc': 'Test option'}
              ],
              'examples': ['test --option value']
          }

          HelpFormatter.show_command_help('test', help_data)

          # Verify console.print was called
          assert mock_console.print.called

      @patch('terminal_client.help_text.console')
      def test_show_command_list(self, mock_console):
          """Test showing command list."""
          HelpFormatter.show_command_list()

          # Verify console.print was called multiple times
          assert mock_console.print.call_count > 5


  class TestShowHelp:
      """Tests for show_help function."""

      @patch('terminal_client.help_text.HelpFormatter.show_command_help')
      def test_show_help_specific_command(self, mock_show):
          """Test showing help for specific command."""
          show_help('new_game')

          mock_show.assert_called_once()
          args = mock_show.call_args[0]
          assert args[0] == 'new_game'
          assert 'summary' in args[1]

      @patch('terminal_client.help_text.HelpFormatter.show_command_list')
      def test_show_help_no_command(self, mock_list):
          """Test showing command list when no command specified."""
          show_help()

          mock_list.assert_called_once()

      @patch('terminal_client.help_text.console')
      def test_show_help_invalid_command(self, mock_console):
          """Test showing help for invalid command."""
          show_help('invalid_command')

          # Should print warning message
          assert mock_console.print.called
          call_args = str(mock_console.print.call_args)
          assert 'No help available' in call_args

  Example Output

  Command List (help):
  ⚾ > help

  Available Commands:

  Game Management:
    new_game      Create a new game with test lineups and start it
    list_games    List all games in state manager
    use_game      Switch to a different game
    status        Display current game state
    box_score     Display box score

  Gameplay:
    defensive     Submit defensive decision
    offensive     Submit offensive decision
    resolve       Resolve the current play
    quick_play    Auto-play multiple plays

  Utilities:
    config        Show configuration
    clear         Clear the screen
    help          Show help for commands
    quit/exit     Exit the REPL

  Type 'help <command>' for detailed information.
  Use TAB for auto-completion of commands and options.

  Detailed Help (help defensive):
  ⚾ > help defensive

  ╭─────────────── Help: defensive ───────────────╮
  │ defensive - Submit defensive decision for     │
  │ the current play                              │
  │                                               │
  │ USAGE:                                        │
  │   defensive [--alignment TYPE] [--infield    │
  │   DEPTH] [--outfield DEPTH] [--hold BASES]   │
  ╰───────────────────────────────────────────────╯

  OPTIONS:
    --alignment    STRING    Defensive alignment: normal, shifted_left,
                            shifted_right, extreme_shift (default: normal)
    --infield      STRING    Infield depth: in, normal, back, double_play
                            (default: normal)
    --outfield     STRING    Outfield depth: in, normal, back (default: normal)
    --hold         LIST      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

  Benefits

  1. Self-documenting: No need to check external docs for basic usage
  2. Rich formatting: Beautiful output with colors, tables, and panels
  3. Comprehensive: Every option explained with examples
  4. Discoverable: Easy to explore what's available
  5. Consistent: Same help format across all commands
  6. Learning tool: Examples teach proper usage patterns
