strat-gameplay-webapp/backend/terminal_client/config.py
Cal Corum 918beadf24 CLAUDE: Add interactive terminal client for game engine testing
Created comprehensive terminal testing tool with two modes:
1. Interactive REPL (recommended) - Persistent in-memory state
2. Standalone CLI commands - Config file persistence

Features:
- Interactive REPL using Python cmd module
- Persistent event loop prevents DB connection issues
- 11 commands for full game control (new_game, defensive, offensive, resolve, etc.)
- Beautiful Rich formatting with colors and panels
- Auto-generated test lineups for rapid testing
- Direct GameEngine access (no WebSocket overhead)
- Config file (~/.terminal_client_config.json) for state persistence

Files added:
- terminal_client/repl.py (525 lines) - Interactive REPL
- terminal_client/main.py (516 lines) - Click standalone commands
- terminal_client/display.py (218 lines) - Rich formatting
- terminal_client/config.py (89 lines) - Persistent config
- terminal_client/__main__.py - Dual mode entry point
- terminal_client/CLAUDE.md (725 lines) - Full documentation

Updated:
- backend/CLAUDE.md - Added terminal client to testing section
- requirements.txt - Added rich==13.9.4

Perfect for rapid iteration on game engine without building frontend!

🚀 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-26 12:51:01 -05:00

96 lines
2.6 KiB
Python

"""
Configuration management for terminal client.
Handles persistent state across command invocations using a config file.
Author: Claude
Date: 2025-10-26
"""
import logging
import json
from pathlib import Path
from uuid import UUID
from typing import Optional
logger = logging.getLogger(f'{__name__}.config')
# Config file location in user's home directory
CONFIG_FILE = Path.home() / '.terminal_client_config.json'
class Config:
"""Persistent configuration manager for terminal client."""
@staticmethod
def _ensure_config_exists() -> None:
"""Create config file if it doesn't exist."""
if not CONFIG_FILE.exists():
CONFIG_FILE.write_text('{}')
logger.debug(f"Created config file: {CONFIG_FILE}")
@staticmethod
def get_current_game() -> Optional[UUID]:
"""
Get the current game ID from config file.
Returns:
UUID of current game, or None if not set
"""
Config._ensure_config_exists()
try:
data = json.loads(CONFIG_FILE.read_text())
game_id_str = data.get('current_game_id')
if game_id_str:
return UUID(game_id_str)
return None
except (json.JSONDecodeError, ValueError) as e:
logger.warning(f"Failed to read config: {e}")
return None
@staticmethod
def set_current_game(game_id: UUID) -> None:
"""
Set the current game ID in config file.
Args:
game_id: UUID of game to set as current
"""
Config._ensure_config_exists()
try:
# Read existing config
data = json.loads(CONFIG_FILE.read_text())
# Update current game
data['current_game_id'] = str(game_id)
# Write back
CONFIG_FILE.write_text(json.dumps(data, indent=2))
logger.debug(f"Set current game to: {game_id}")
except (json.JSONDecodeError, IOError) as e:
logger.error(f"Failed to write config: {e}")
raise
@staticmethod
def clear_current_game() -> None:
"""Clear the current game ID from config file."""
Config._ensure_config_exists()
try:
data = json.loads(CONFIG_FILE.read_text())
data.pop('current_game_id', None)
CONFIG_FILE.write_text(json.dumps(data, indent=2))
logger.debug("Cleared current game")
except (json.JSONDecodeError, IOError) as e:
logger.warning(f"Failed to clear config: {e}")
@staticmethod
def get_config_path() -> Path:
"""Get the path to the config file."""
return CONFIG_FILE